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
add_pair(const char * name,const char * nvlist,const char * key,const char * type,const char * value)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
delete_pair(const char * name,const char * nvlist,const char * key)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
main(int argc,char * const * argv)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