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/types.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <inttypes.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <kenv.h>
35 #include <unistd.h>
36
37 #include <libzfsbootenv.h>
38
39 #ifndef ZFS_MAXNAMELEN
40 #define ZFS_MAXNAMELEN 256
41 #endif
42
43 static int
add_pair(const char * name,const char * nvlist,const char * key,const char * type,const char * value)44 add_pair(const char *name, const char *nvlist, const char *key,
45 const char *type, const char *value)
46 {
47 void *data, *nv;
48 size_t size;
49 int rv;
50 char *end;
51
52 rv = lzbe_nvlist_get(name, nvlist, &nv);
53 if (rv != 0)
54 return (rv);
55
56 data = NULL;
57 rv = EINVAL;
58 if (strcmp(type, "DATA_TYPE_STRING") == 0) {
59 data = __DECONST(void *, value);
60 size = strlen(data) + 1;
61 rv = lzbe_add_pair(nv, key, type, data, size);
62 } else if (strcmp(type, "DATA_TYPE_UINT64") == 0) {
63 uint64_t v;
64
65 v = strtoull(value, &end, 0);
66 if (errno != 0 || *end != '\0')
67 goto done;
68 size = sizeof (v);
69 rv = lzbe_add_pair(nv, key, type, &v, size);
70 } else if (strcmp(type, "DATA_TYPE_INT64") == 0) {
71 int64_t v;
72
73 v = strtoll(value, &end, 0);
74 if (errno != 0 || *end != '\0')
75 goto done;
76 size = sizeof (v);
77 rv = lzbe_add_pair(nv, key, type, &v, size);
78 } else if (strcmp(type, "DATA_TYPE_UINT32") == 0) {
79 uint32_t v;
80
81 v = strtoul(value, &end, 0);
82 if (errno != 0 || *end != '\0')
83 goto done;
84 size = sizeof (v);
85 rv = lzbe_add_pair(nv, key, type, &v, size);
86 } else if (strcmp(type, "DATA_TYPE_INT32") == 0) {
87 int32_t v;
88
89 v = strtol(value, &end, 0);
90 if (errno != 0 || *end != '\0')
91 goto done;
92 size = sizeof (v);
93 rv = lzbe_add_pair(nv, key, type, &v, size);
94 } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) {
95 uint16_t v;
96
97 v = strtoul(value, &end, 0);
98 if (errno != 0 || *end != '\0')
99 goto done;
100 size = sizeof (v);
101 rv = lzbe_add_pair(nv, key, type, &v, size);
102 } else if (strcmp(type, "DATA_TYPE_INT16") == 0) {
103 int16_t v;
104
105 v = strtol(value, &end, 0);
106 if (errno != 0 || *end != '\0')
107 goto done;
108 size = sizeof (v);
109 rv = lzbe_add_pair(nv, key, type, &v, size);
110 } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) {
111 uint8_t v;
112
113 v = strtoul(value, &end, 0);
114 if (errno != 0 || *end != '\0')
115 goto done;
116 size = sizeof (v);
117 rv = lzbe_add_pair(nv, key, type, &v, size);
118 } else if (strcmp(type, "DATA_TYPE_INT8") == 0) {
119 int8_t v;
120
121 v = strtol(value, &end, 0);
122 if (errno != 0 || *end != '\0')
123 goto done;
124 size = sizeof (v);
125 rv = lzbe_add_pair(nv, key, type, &v, size);
126 } 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
156 done:
157 lzbe_nvlist_free(nv);
158 return (rv);
159 }
160
161 static int
delete_pair(const char * name,const char * nvlist,const char * key)162 delete_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 */
185 int
main(int argc,char * const * argv)186 main(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