xref: /freebsd/sbin/zfsbootcfg/zfsbootcfg.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
197371ba2SAndriy Gapon /*-
297371ba2SAndriy Gapon  * Copyright (c) 2016 Andriy Gapon <avg@FreeBSD.org>
397371ba2SAndriy Gapon  * All rights reserved.
497371ba2SAndriy Gapon  *
597371ba2SAndriy Gapon  * Redistribution and use in source and binary forms, with or without
697371ba2SAndriy Gapon  * modification, are permitted provided that the following conditions
797371ba2SAndriy Gapon  * are met:
897371ba2SAndriy Gapon  * 1. Redistributions of source code must retain the above copyright
997371ba2SAndriy Gapon  *    notice, this list of conditions and the following disclaimer.
1097371ba2SAndriy Gapon  * 2. Redistributions in binary form must reproduce the above copyright
1197371ba2SAndriy Gapon  *    notice, this list of conditions and the following disclaimer in the
1297371ba2SAndriy Gapon  *    documentation and/or other materials provided with the distribution.
1397371ba2SAndriy Gapon  *
1497371ba2SAndriy Gapon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1597371ba2SAndriy Gapon  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1697371ba2SAndriy Gapon  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1797371ba2SAndriy Gapon  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1897371ba2SAndriy Gapon  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1997371ba2SAndriy Gapon  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2097371ba2SAndriy Gapon  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2197371ba2SAndriy Gapon  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2297371ba2SAndriy Gapon  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2397371ba2SAndriy Gapon  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2497371ba2SAndriy Gapon  */
2597371ba2SAndriy Gapon 
2697371ba2SAndriy Gapon #include <sys/types.h>
2797371ba2SAndriy Gapon #include <errno.h>
2897371ba2SAndriy Gapon #include <limits.h>
2997371ba2SAndriy Gapon #include <inttypes.h>
3097371ba2SAndriy Gapon #include <stdio.h>
3197371ba2SAndriy Gapon #include <stdlib.h>
32*e307eb94SToomas Soome #include <stdbool.h>
3397371ba2SAndriy Gapon #include <string.h>
3497371ba2SAndriy Gapon #include <kenv.h>
35*e307eb94SToomas Soome #include <unistd.h>
3697371ba2SAndriy Gapon 
37*e307eb94SToomas Soome #include <libzfsbootenv.h>
3897371ba2SAndriy Gapon 
39*e307eb94SToomas Soome #ifndef ZFS_MAXNAMELEN
40*e307eb94SToomas Soome #define	ZFS_MAXNAMELEN	256
41*e307eb94SToomas Soome #endif
4297371ba2SAndriy Gapon 
43*e307eb94SToomas Soome static int
add_pair(const char * name,const char * nvlist,const char * key,const char * type,const char * value)44*e307eb94SToomas Soome add_pair(const char *name, const char *nvlist, const char *key,
45*e307eb94SToomas Soome     const char *type, const char *value)
46007278ebSToomas Soome {
47*e307eb94SToomas Soome 	void *data, *nv;
48*e307eb94SToomas Soome 	size_t size;
49007278ebSToomas Soome 	int rv;
50*e307eb94SToomas Soome 	char *end;
51007278ebSToomas Soome 
52*e307eb94SToomas Soome 	rv = lzbe_nvlist_get(name, nvlist, &nv);
53*e307eb94SToomas Soome 	if (rv != 0)
54007278ebSToomas Soome 		return (rv);
55007278ebSToomas Soome 
56*e307eb94SToomas Soome 	data = NULL;
57*e307eb94SToomas Soome 	rv = EINVAL;
58*e307eb94SToomas Soome 	if (strcmp(type, "DATA_TYPE_STRING") == 0) {
59*e307eb94SToomas Soome 		data = __DECONST(void *, value);
60*e307eb94SToomas Soome 		size = strlen(data) + 1;
61*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, data, size);
62*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_UINT64") == 0) {
63*e307eb94SToomas Soome 		uint64_t v;
64*e307eb94SToomas Soome 
65*e307eb94SToomas Soome 		v = strtoull(value, &end, 0);
66*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
67*e307eb94SToomas Soome 			goto done;
68*e307eb94SToomas Soome 		size = sizeof (v);
69*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
70*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_INT64") == 0) {
71*e307eb94SToomas Soome 		int64_t v;
72*e307eb94SToomas Soome 
73*e307eb94SToomas Soome 		v = strtoll(value, &end, 0);
74*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
75*e307eb94SToomas Soome 			goto done;
76*e307eb94SToomas Soome 		size = sizeof (v);
77*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
78*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_UINT32") == 0) {
79*e307eb94SToomas Soome 		uint32_t v;
80*e307eb94SToomas Soome 
81*e307eb94SToomas Soome 		v = strtoul(value, &end, 0);
82*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
83*e307eb94SToomas Soome 			goto done;
84*e307eb94SToomas Soome 		size = sizeof (v);
85*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
86*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
87*e307eb94SToomas Soome 		int32_t v;
88*e307eb94SToomas Soome 
89*e307eb94SToomas Soome 		v = strtol(value, &end, 0);
90*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
91*e307eb94SToomas Soome 			goto done;
92*e307eb94SToomas Soome 		size = sizeof (v);
93*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
94*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
95*e307eb94SToomas Soome 		uint16_t v;
96*e307eb94SToomas Soome 
97*e307eb94SToomas Soome 		v = strtoul(value, &end, 0);
98*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
99*e307eb94SToomas Soome 			goto done;
100*e307eb94SToomas Soome 		size = sizeof (v);
101*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
102*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
103*e307eb94SToomas Soome 		int16_t v;
104*e307eb94SToomas Soome 
105*e307eb94SToomas Soome 		v = strtol(value, &end, 0);
106*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
107*e307eb94SToomas Soome 			goto done;
108*e307eb94SToomas Soome 		size = sizeof (v);
109*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
110*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
111*e307eb94SToomas Soome 		uint8_t v;
112*e307eb94SToomas Soome 
113*e307eb94SToomas Soome 		v = strtoul(value, &end, 0);
114*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
115*e307eb94SToomas Soome 			goto done;
116*e307eb94SToomas Soome 		size = sizeof (v);
117*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
118*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
119*e307eb94SToomas Soome 		int8_t v;
120*e307eb94SToomas Soome 
121*e307eb94SToomas Soome 		v = strtol(value, &end, 0);
122*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
123*e307eb94SToomas Soome 			goto done;
124*e307eb94SToomas Soome 		size = sizeof (v);
125*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
126*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_BYTE") == 0) {
127*e307eb94SToomas Soome 		uint8_t v;
128*e307eb94SToomas Soome 
129*e307eb94SToomas Soome 		v = strtoul(value, &end, 0);
130*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0')
131*e307eb94SToomas Soome 			goto done;
132*e307eb94SToomas Soome 		size = sizeof (v);
133*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
134*e307eb94SToomas Soome 	} else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) {
135*e307eb94SToomas Soome 		int32_t v;
136*e307eb94SToomas Soome 
137*e307eb94SToomas Soome 		v = strtol(value, &end, 0);
138*e307eb94SToomas Soome 		if (errno != 0 || *end != '\0') {
139*e307eb94SToomas Soome 			if (strcasecmp(value, "YES") == 0)
140*e307eb94SToomas Soome 				v = 1;
141*e307eb94SToomas Soome 			else if (strcasecmp(value, "NO") == 0)
142*e307eb94SToomas Soome 				v = 0;
143*e307eb94SToomas Soome 			if (strcasecmp(value, "true") == 0)
144*e307eb94SToomas Soome 				v = 1;
145*e307eb94SToomas Soome 			else if (strcasecmp(value, "false") == 0)
146*e307eb94SToomas Soome 				v = 0;
147*e307eb94SToomas Soome 			else goto done;
148007278ebSToomas Soome 		}
149*e307eb94SToomas Soome 		size = sizeof (v);
150*e307eb94SToomas Soome 		rv = lzbe_add_pair(nv, key, type, &v, size);
151007278ebSToomas Soome 	}
152007278ebSToomas Soome 
153*e307eb94SToomas Soome 	if (rv == 0)
154*e307eb94SToomas Soome 		rv = lzbe_nvlist_set(name, nvlist, nv);
155*e307eb94SToomas Soome 
156*e307eb94SToomas Soome done:
157*e307eb94SToomas Soome 	lzbe_nvlist_free(nv);
158*e307eb94SToomas Soome 	return (rv);
159*e307eb94SToomas Soome }
160*e307eb94SToomas Soome 
161*e307eb94SToomas Soome static int
delete_pair(const char * name,const char * nvlist,const char * key)162*e307eb94SToomas Soome delete_pair(const char *name, const char *nvlist, const char *key)
16397371ba2SAndriy Gapon {
164*e307eb94SToomas Soome 	void *nv;
165007278ebSToomas Soome 	int rv;
16697371ba2SAndriy Gapon 
167*e307eb94SToomas Soome 	rv = lzbe_nvlist_get(name, nvlist, &nv);
168*e307eb94SToomas Soome 	if (rv == 0) {
169*e307eb94SToomas Soome 		rv = lzbe_remove_pair(nv, key);
170*e307eb94SToomas Soome 	}
171*e307eb94SToomas Soome 	if (rv == 0)
172*e307eb94SToomas Soome 		rv = lzbe_nvlist_set(name, nvlist, nv);
173*e307eb94SToomas Soome 
174*e307eb94SToomas Soome 	lzbe_nvlist_free(nv);
175*e307eb94SToomas Soome 	return (rv);
176*e307eb94SToomas Soome }
177*e307eb94SToomas Soome 
178*e307eb94SToomas Soome /*
179*e307eb94SToomas Soome  * Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p]
180*e307eb94SToomas Soome  *	zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p]
181*e307eb94SToomas Soome  *
182*e307eb94SToomas Soome  * if nvlist is set, we will update nvlist in bootenv.
183*e307eb94SToomas Soome  * if nvlist is not set, we update pairs in bootenv.
184*e307eb94SToomas Soome  */
185*e307eb94SToomas Soome int
main(int argc,char * const * argv)186*e307eb94SToomas Soome main(int argc, char * const *argv)
187*e307eb94SToomas Soome {
188*e307eb94SToomas Soome 	char buf[ZFS_MAXNAMELEN], *name;
189*e307eb94SToomas Soome 	const char *key, *value, *type, *nvlist;
190*e307eb94SToomas Soome 	int rv;
191*e307eb94SToomas Soome 	bool print, delete;
192*e307eb94SToomas Soome 
193*e307eb94SToomas Soome 	nvlist = NULL;
194*e307eb94SToomas Soome 	name = NULL;
195*e307eb94SToomas Soome 	key = NULL;
196*e307eb94SToomas Soome 	type = NULL;
197*e307eb94SToomas Soome 	value = NULL;
198*e307eb94SToomas Soome 	print = delete = false;
199*e307eb94SToomas Soome 	while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) {
200*e307eb94SToomas Soome 		switch (rv) {
201*e307eb94SToomas Soome 		case 'd':
202*e307eb94SToomas Soome 			delete = true;
203*e307eb94SToomas Soome 			key = optarg;
204*e307eb94SToomas Soome 			break;
205*e307eb94SToomas Soome 		case 'k':
206*e307eb94SToomas Soome 			key = optarg;
207*e307eb94SToomas Soome 			break;
208*e307eb94SToomas Soome 		case 'n':
209*e307eb94SToomas Soome 			nvlist = optarg;
210*e307eb94SToomas Soome 			break;
211*e307eb94SToomas Soome 		case 'p':
212*e307eb94SToomas Soome 			print = true;
213*e307eb94SToomas Soome 			break;
214*e307eb94SToomas Soome 		case 't':
215*e307eb94SToomas Soome 			type = optarg;
216*e307eb94SToomas Soome 			break;
217*e307eb94SToomas Soome 		case 'v':
218*e307eb94SToomas Soome 			value = optarg;
219*e307eb94SToomas Soome 			break;
220*e307eb94SToomas Soome 		case 'z':
221*e307eb94SToomas Soome 			name = optarg;
222*e307eb94SToomas Soome 			break;
223*e307eb94SToomas Soome 		}
224*e307eb94SToomas Soome 	}
225*e307eb94SToomas Soome 
226*e307eb94SToomas Soome 	argc -= optind;
227*e307eb94SToomas Soome 	argv += optind;
228*e307eb94SToomas Soome 
229*e307eb94SToomas Soome 	if (argc == 1)
230*e307eb94SToomas Soome 		value = argv[0];
231*e307eb94SToomas Soome 
232*e307eb94SToomas Soome 	if (argc > 1) {
23397371ba2SAndriy Gapon 		fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n");
23497371ba2SAndriy Gapon 		return (1);
23597371ba2SAndriy Gapon 	}
23697371ba2SAndriy Gapon 
237*e307eb94SToomas Soome 	if (name == NULL) {
238*e307eb94SToomas Soome 		rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf));
239*e307eb94SToomas Soome 		if (rv <= 0) {
240007278ebSToomas Soome 			perror("can't get vfs.root.mountfrom");
24197371ba2SAndriy Gapon 			return (1);
24297371ba2SAndriy Gapon 		}
24397371ba2SAndriy Gapon 
244007278ebSToomas Soome 		if (strncmp(buf, "zfs:", 4) == 0) {
245007278ebSToomas Soome 			name = strchr(buf + 4, '/');
246007278ebSToomas Soome 			if (name != NULL)
247007278ebSToomas Soome 				*name = '\0';
248007278ebSToomas Soome 			name = buf + 4;
249007278ebSToomas Soome 		} else {
250007278ebSToomas Soome 			perror("not a zfs root");
25197371ba2SAndriy Gapon 			return (1);
25297371ba2SAndriy Gapon 		}
25397371ba2SAndriy Gapon 	}
25497371ba2SAndriy Gapon 
255*e307eb94SToomas Soome 	rv = 0;
256*e307eb94SToomas Soome 	if (key != NULL || value != NULL) {
257*e307eb94SToomas Soome 		if (type == NULL)
258*e307eb94SToomas Soome 			type = "DATA_TYPE_STRING";
25997371ba2SAndriy Gapon 
260*e307eb94SToomas Soome 		if (delete)
261*e307eb94SToomas Soome 			rv = delete_pair(name, nvlist, key);
262*e307eb94SToomas Soome 		else if (key == NULL || strcmp(key, "command") == 0)
263*e307eb94SToomas Soome 			rv = lzbe_set_boot_device(name, lzbe_add, value);
264*e307eb94SToomas Soome 		else
265*e307eb94SToomas Soome 			rv = add_pair(name, nvlist, key, type, value);
266007278ebSToomas Soome 
267007278ebSToomas Soome 		if (rv == 0)
268*e307eb94SToomas Soome 			printf("zfs bootenv is successfully written\n");
269*e307eb94SToomas Soome 		else
270*e307eb94SToomas Soome 			printf("error: %d\n", rv);
271*e307eb94SToomas Soome 	} else if (!print) {
272*e307eb94SToomas Soome 		char *ptr;
273*e307eb94SToomas Soome 
274*e307eb94SToomas Soome 		if (lzbe_get_boot_device(name, &ptr) == 0) {
275*e307eb94SToomas Soome 			printf("zfs:%s:\n", ptr);
276*e307eb94SToomas Soome 			free(ptr);
277*e307eb94SToomas Soome 		}
278*e307eb94SToomas Soome 	}
279*e307eb94SToomas Soome 
280*e307eb94SToomas Soome 	if (print) {
281*e307eb94SToomas Soome 		rv = lzbe_bootenv_print(name, nvlist, stdout);
282*e307eb94SToomas Soome 	}
283*e307eb94SToomas Soome 
284007278ebSToomas Soome 	return (rv);
28597371ba2SAndriy Gapon }
286