xref: /freebsd/sbin/zfsbootcfg/zfsbootcfg.c (revision edf8578117e8844e02c0121147f45e4609b30680)
1 /*-
2  * Copyright (c) 2016 Andriy Gapon <avg@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <limits.h>
30 #include <inttypes.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdbool.h>
34 #include <string.h>
35 #include <kenv.h>
36 #include <unistd.h>
37 
38 #include <libzfsbootenv.h>
39 
40 #ifndef ZFS_MAXNAMELEN
41 #define	ZFS_MAXNAMELEN	256
42 #endif
43 
44 static int
45 add_pair(const char *name, const char *nvlist, const char *key,
46     const char *type, const char *value)
47 {
48 	void *data, *nv;
49 	size_t size;
50 	int rv;
51 	char *end;
52 
53 	rv = lzbe_nvlist_get(name, nvlist, &nv);
54 	if (rv != 0)
55 		return (rv);
56 
57 	data = NULL;
58 	rv = EINVAL;
59 	if (strcmp(type, "DATA_TYPE_STRING") == 0) {
60 		data = __DECONST(void *, value);
61 		size = strlen(data) + 1;
62 		rv = lzbe_add_pair(nv, key, type, data, size);
63 	} else if (strcmp(type, "DATA_TYPE_UINT64") == 0) {
64 		uint64_t v;
65 
66 		v = strtoull(value, &end, 0);
67 		if (errno != 0 || *end != '\0')
68 			goto done;
69 		size = sizeof (v);
70 		rv = lzbe_add_pair(nv, key, type, &v, size);
71 	} else if (strcmp(type, "DATA_TYPE_INT64") == 0) {
72 		int64_t v;
73 
74 		v = strtoll(value, &end, 0);
75 		if (errno != 0 || *end != '\0')
76 			goto done;
77 		size = sizeof (v);
78 		rv = lzbe_add_pair(nv, key, type, &v, size);
79 	} else if (strcmp(type, "DATA_TYPE_UINT32") == 0) {
80 		uint32_t v;
81 
82 		v = strtoul(value, &end, 0);
83 		if (errno != 0 || *end != '\0')
84 			goto done;
85 		size = sizeof (v);
86 		rv = lzbe_add_pair(nv, key, type, &v, size);
87 	} else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
88 		int32_t v;
89 
90 		v = strtol(value, &end, 0);
91 		if (errno != 0 || *end != '\0')
92 			goto done;
93 		size = sizeof (v);
94 		rv = lzbe_add_pair(nv, key, type, &v, size);
95 	} else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
96 		uint16_t v;
97 
98 		v = strtoul(value, &end, 0);
99 		if (errno != 0 || *end != '\0')
100 			goto done;
101 		size = sizeof (v);
102 		rv = lzbe_add_pair(nv, key, type, &v, size);
103 	} else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
104 		int16_t v;
105 
106 		v = strtol(value, &end, 0);
107 		if (errno != 0 || *end != '\0')
108 			goto done;
109 		size = sizeof (v);
110 		rv = lzbe_add_pair(nv, key, type, &v, size);
111 	} else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
112 		uint8_t v;
113 
114 		v = strtoul(value, &end, 0);
115 		if (errno != 0 || *end != '\0')
116 			goto done;
117 		size = sizeof (v);
118 		rv = lzbe_add_pair(nv, key, type, &v, size);
119 	} else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
120 		int8_t v;
121 
122 		v = strtol(value, &end, 0);
123 		if (errno != 0 || *end != '\0')
124 			goto done;
125 		size = sizeof (v);
126 		rv = lzbe_add_pair(nv, key, type, &v, size);
127 	} else if (strcmp(type, "DATA_TYPE_BYTE") == 0) {
128 		uint8_t v;
129 
130 		v = strtoul(value, &end, 0);
131 		if (errno != 0 || *end != '\0')
132 			goto done;
133 		size = sizeof (v);
134 		rv = lzbe_add_pair(nv, key, type, &v, size);
135 	} else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) {
136 		int32_t v;
137 
138 		v = strtol(value, &end, 0);
139 		if (errno != 0 || *end != '\0') {
140 			if (strcasecmp(value, "YES") == 0)
141 				v = 1;
142 			else if (strcasecmp(value, "NO") == 0)
143 				v = 0;
144 			if (strcasecmp(value, "true") == 0)
145 				v = 1;
146 			else if (strcasecmp(value, "false") == 0)
147 				v = 0;
148 			else goto done;
149 		}
150 		size = sizeof (v);
151 		rv = lzbe_add_pair(nv, key, type, &v, size);
152 	}
153 
154 	if (rv == 0)
155 		rv = lzbe_nvlist_set(name, nvlist, nv);
156 
157 done:
158 	lzbe_nvlist_free(nv);
159 	return (rv);
160 }
161 
162 static int
163 delete_pair(const char *name, const char *nvlist, const char *key)
164 {
165 	void *nv;
166 	int rv;
167 
168 	rv = lzbe_nvlist_get(name, nvlist, &nv);
169 	if (rv == 0) {
170 		rv = lzbe_remove_pair(nv, key);
171 	}
172 	if (rv == 0)
173 		rv = lzbe_nvlist_set(name, nvlist, nv);
174 
175 	lzbe_nvlist_free(nv);
176 	return (rv);
177 }
178 
179 /*
180  * Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p]
181  *	zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]
182  *
183  * if nvlist is set, we will update nvlist in bootenv.
184  * if nvlist is not set, we update pairs in bootenv.
185  */
186 int
187 main(int argc, char * const *argv)
188 {
189 	char buf[ZFS_MAXNAMELEN], *name;
190 	const char *key, *value, *type, *nvlist;
191 	int rv;
192 	bool print, delete;
193 
194 	nvlist = NULL;
195 	name = NULL;
196 	key = NULL;
197 	type = NULL;
198 	value = NULL;
199 	print = delete = false;
200 	while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) {
201 		switch (rv) {
202 		case 'd':
203 			delete = true;
204 			key = optarg;
205 			break;
206 		case 'k':
207 			key = optarg;
208 			break;
209 		case 'n':
210 			nvlist = optarg;
211 			break;
212 		case 'p':
213 			print = true;
214 			break;
215 		case 't':
216 			type = optarg;
217 			break;
218 		case 'v':
219 			value = optarg;
220 			break;
221 		case 'z':
222 			name = optarg;
223 			break;
224 		}
225 	}
226 
227 	argc -= optind;
228 	argv += optind;
229 
230 	if (argc == 1)
231 		value = argv[0];
232 
233 	if (argc > 1) {
234 		fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
235 		return (1);
236 	}
237 
238 	if (name == NULL) {
239 		rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf));
240 		if (rv <= 0) {
241 			perror("can't get vfs.root.mountfrom");
242 			return (1);
243 		}
244 
245 		if (strncmp(buf, "zfs:", 4) == 0) {
246 			name = strchr(buf + 4, '/');
247 			if (name != NULL)
248 				*name = '\0';
249 			name = buf + 4;
250 		} else {
251 			perror("not a zfs root");
252 			return (1);
253 		}
254 	}
255 
256 	rv = 0;
257 	if (key != NULL || value != NULL) {
258 		if (type == NULL)
259 			type = "DATA_TYPE_STRING";
260 
261 		if (delete)
262 			rv = delete_pair(name, nvlist, key);
263 		else if (key == NULL || strcmp(key, "command") == 0)
264 			rv = lzbe_set_boot_device(name, lzbe_add, value);
265 		else
266 			rv = add_pair(name, nvlist, key, type, value);
267 
268 		if (rv == 0)
269 			printf("zfs bootenv is successfully written\n");
270 		else
271 			printf("error: %d\n", rv);
272 	} else if (!print) {
273 		char *ptr;
274 
275 		if (lzbe_get_boot_device(name, &ptr) == 0) {
276 			printf("zfs:%s:\n", ptr);
277 			free(ptr);
278 		}
279 	}
280 
281 	if (print) {
282 		rv = lzbe_bootenv_print(name, nvlist, stdout);
283 	}
284 
285 	return (rv);
286 }
287