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