1249259Sdim/*-
2249259Sdim * Copyright (c) 2016 Andriy Gapon <avg@FreeBSD.org>
3249259Sdim * All rights reserved.
4249259Sdim *
5249259Sdim * Redistribution and use in source and binary forms, with or without
6249259Sdim * modification, are permitted provided that the following conditions
7249259Sdim * are met:
8249259Sdim * 1. Redistributions of source code must retain the above copyright
9249259Sdim *    notice, this list of conditions and the following disclaimer.
10249259Sdim * 2. Redistributions in binary form must reproduce the above copyright
11249259Sdim *    notice, this list of conditions and the following disclaimer in the
12249259Sdim *    documentation and/or other materials provided with the distribution.
13249259Sdim *
14249259Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15249259Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16249259Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17249259Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18249259Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19249259Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20249259Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21249259Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22249259Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23249259Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24249259Sdim */
25249259Sdim
26249259Sdim#include <sys/types.h>
27249259Sdim#include <errno.h>
28249259Sdim#include <limits.h>
29249259Sdim#include <inttypes.h>
30249259Sdim#include <stdio.h>
31249259Sdim#include <stdlib.h>
32249259Sdim#include <stdbool.h>
33249259Sdim#include <string.h>
34249259Sdim#include <kenv.h>
35249259Sdim#include <unistd.h>
36249259Sdim
37249259Sdim#include <libzfsbootenv.h>
38249259Sdim
39249259Sdim#ifndef ZFS_MAXNAMELEN
40249259Sdim#define	ZFS_MAXNAMELEN	256
41249259Sdim#endif
42249259Sdim
43249259Sdimstatic int
44249259Sdimadd_pair(const char *name, const char *nvlist, const char *key,
45249259Sdim    const char *type, const char *value)
46249259Sdim{
47249259Sdim	void *data, *nv;
48252723Sdim	size_t size;
49252723Sdim	int rv;
50249259Sdim	char *end;
51249259Sdim
52249259Sdim	rv = lzbe_nvlist_get(name, nvlist, &nv);
53252723Sdim	if (rv != 0)
54249259Sdim		return (rv);
55249259Sdim
56249259Sdim	data = NULL;
57249259Sdim	rv = EINVAL;
58249259Sdim	if (strcmp(type, "DATA_TYPE_STRING") == 0) {
59249259Sdim		data = __DECONST(void *, value);
60249259Sdim		size = strlen(data) + 1;
61249259Sdim		rv = lzbe_add_pair(nv, key, type, data, size);
62249259Sdim	} else if (strcmp(type, "DATA_TYPE_UINT64") == 0) {
63249259Sdim		uint64_t v;
64249259Sdim
65249259Sdim		v = strtoull(value, &end, 0);
66252723Sdim		if (errno != 0 || *end != '\0')
67252723Sdim			goto done;
68252723Sdim		size = sizeof (v);
69252723Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
70252723Sdim	} else if (strcmp(type, "DATA_TYPE_INT64") == 0) {
71252723Sdim		int64_t v;
72249259Sdim
73249259Sdim		v = strtoll(value, &end, 0);
74249259Sdim		if (errno != 0 || *end != '\0')
75249259Sdim			goto done;
76249259Sdim		size = sizeof (v);
77249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
78249259Sdim	} else if (strcmp(type, "DATA_TYPE_UINT32") == 0) {
79249259Sdim		uint32_t v;
80263509Sdim
81249259Sdim		v = strtoul(value, &end, 0);
82249259Sdim		if (errno != 0 || *end != '\0')
83249259Sdim			goto done;
84249259Sdim		size = sizeof (v);
85249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
86249259Sdim	} else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
87249259Sdim		int32_t v;
88249259Sdim
89249259Sdim		v = strtol(value, &end, 0);
90249259Sdim		if (errno != 0 || *end != '\0')
91249259Sdim			goto done;
92249259Sdim		size = sizeof (v);
93249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
94249259Sdim	} else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
95249259Sdim		uint16_t v;
96249259Sdim
97249259Sdim		v = strtoul(value, &end, 0);
98249259Sdim		if (errno != 0 || *end != '\0')
99249259Sdim			goto done;
100249259Sdim		size = sizeof (v);
101249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
102249259Sdim	} else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
103249259Sdim		int16_t v;
104249259Sdim
105249259Sdim		v = strtol(value, &end, 0);
106249259Sdim		if (errno != 0 || *end != '\0')
107249259Sdim			goto done;
108249259Sdim		size = sizeof (v);
109249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
110249259Sdim	} else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
111249259Sdim		uint8_t v;
112249259Sdim
113249259Sdim		v = strtoul(value, &end, 0);
114249259Sdim		if (errno != 0 || *end != '\0')
115249259Sdim			goto done;
116249259Sdim		size = sizeof (v);
117249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
118249259Sdim	} else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
119249259Sdim		int8_t v;
120249259Sdim
121249259Sdim		v = strtol(value, &end, 0);
122249259Sdim		if (errno != 0 || *end != '\0')
123249259Sdim			goto done;
124249259Sdim		size = sizeof (v);
125249259Sdim		rv = lzbe_add_pair(nv, key, type, &v, size);
126249259Sdim	} else if (strcmp(type, "DATA_TYPE_BYTE") == 0) {
127		uint8_t v;
128
129		v = strtoul(value, &end, 0);
130		if (errno != 0 || *end != '\0')
131			goto done;
132		size = sizeof (v);
133		rv = lzbe_add_pair(nv, key, type, &v, size);
134	} else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) {
135		int32_t v;
136
137		v = strtol(value, &end, 0);
138		if (errno != 0 || *end != '\0') {
139			if (strcasecmp(value, "YES") == 0)
140				v = 1;
141			else if (strcasecmp(value, "NO") == 0)
142				v = 0;
143			if (strcasecmp(value, "true") == 0)
144				v = 1;
145			else if (strcasecmp(value, "false") == 0)
146				v = 0;
147			else goto done;
148		}
149		size = sizeof (v);
150		rv = lzbe_add_pair(nv, key, type, &v, size);
151	}
152
153	if (rv == 0)
154		rv = lzbe_nvlist_set(name, nvlist, nv);
155
156done:
157	lzbe_nvlist_free(nv);
158	return (rv);
159}
160
161static int
162delete_pair(const char *name, const char *nvlist, const char *key)
163{
164	void *nv;
165	int rv;
166
167	rv = lzbe_nvlist_get(name, nvlist, &nv);
168	if (rv == 0) {
169		rv = lzbe_remove_pair(nv, key);
170	}
171	if (rv == 0)
172		rv = lzbe_nvlist_set(name, nvlist, nv);
173
174	lzbe_nvlist_free(nv);
175	return (rv);
176}
177
178/*
179 * Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p]
180 *	zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]
181 *
182 * if nvlist is set, we will update nvlist in bootenv.
183 * if nvlist is not set, we update pairs in bootenv.
184 */
185int
186main(int argc, char * const *argv)
187{
188	char buf[ZFS_MAXNAMELEN], *name;
189	const char *key, *value, *type, *nvlist;
190	int rv;
191	bool print, delete;
192
193	nvlist = NULL;
194	name = NULL;
195	key = NULL;
196	type = NULL;
197	value = NULL;
198	print = delete = false;
199	while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) {
200		switch (rv) {
201		case 'd':
202			delete = true;
203			key = optarg;
204			break;
205		case 'k':
206			key = optarg;
207			break;
208		case 'n':
209			nvlist = optarg;
210			break;
211		case 'p':
212			print = true;
213			break;
214		case 't':
215			type = optarg;
216			break;
217		case 'v':
218			value = optarg;
219			break;
220		case 'z':
221			name = optarg;
222			break;
223		}
224	}
225
226	argc -= optind;
227	argv += optind;
228
229	if (argc == 1)
230		value = argv[0];
231
232	if (argc > 1) {
233		fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
234		return (1);
235	}
236
237	if (name == NULL) {
238		rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf));
239		if (rv <= 0) {
240			perror("can't get vfs.root.mountfrom");
241			return (1);
242		}
243
244		if (strncmp(buf, "zfs:", 4) == 0) {
245			name = strchr(buf + 4, '/');
246			if (name != NULL)
247				*name = '\0';
248			name = buf + 4;
249		} else {
250			perror("not a zfs root");
251			return (1);
252		}
253	}
254
255	rv = 0;
256	if (key != NULL || value != NULL) {
257		if (type == NULL)
258			type = "DATA_TYPE_STRING";
259
260		if (delete)
261			rv = delete_pair(name, nvlist, key);
262		else if (key == NULL || strcmp(key, "command") == 0)
263			rv = lzbe_set_boot_device(name, lzbe_add, value);
264		else
265			rv = add_pair(name, nvlist, key, type, value);
266
267		if (rv == 0)
268			printf("zfs bootenv is successfully written\n");
269		else
270			printf("error: %d\n", rv);
271	} else if (!print) {
272		char *ptr;
273
274		if (lzbe_get_boot_device(name, &ptr) == 0) {
275			printf("zfs:%s:\n", ptr);
276			free(ptr);
277		}
278	}
279
280	if (print) {
281		rv = lzbe_bootenv_print(name, nvlist, stdout);
282	}
283
284	return (rv);
285}
286