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 __FBSDID("$FreeBSD$"); 28 29 #include <sys/types.h> 30 #include <errno.h> 31 #include <limits.h> 32 #include <inttypes.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <stdbool.h> 36 #include <string.h> 37 #include <kenv.h> 38 #include <unistd.h> 39 40 #include <libzfsbootenv.h> 41 42 #ifndef ZFS_MAXNAMELEN 43 #define ZFS_MAXNAMELEN 256 44 #endif 45 46 static int 47 add_pair(const char *name, const char *nvlist, const char *key, 48 const char *type, const char *value) 49 { 50 void *data, *nv; 51 size_t size; 52 int rv; 53 char *end; 54 55 rv = lzbe_nvlist_get(name, nvlist, &nv); 56 if (rv != 0) 57 return (rv); 58 59 data = NULL; 60 rv = EINVAL; 61 if (strcmp(type, "DATA_TYPE_STRING") == 0) { 62 data = __DECONST(void *, value); 63 size = strlen(data) + 1; 64 rv = lzbe_add_pair(nv, key, type, data, size); 65 } else if (strcmp(type, "DATA_TYPE_UINT64") == 0) { 66 uint64_t v; 67 68 v = strtoull(value, &end, 0); 69 if (errno != 0 || *end != '\0') 70 goto done; 71 size = sizeof (v); 72 rv = lzbe_add_pair(nv, key, type, &v, size); 73 } else if (strcmp(type, "DATA_TYPE_INT64") == 0) { 74 int64_t v; 75 76 v = strtoll(value, &end, 0); 77 if (errno != 0 || *end != '\0') 78 goto done; 79 size = sizeof (v); 80 rv = lzbe_add_pair(nv, key, type, &v, size); 81 } else if (strcmp(type, "DATA_TYPE_UINT32") == 0) { 82 uint32_t v; 83 84 v = strtoul(value, &end, 0); 85 if (errno != 0 || *end != '\0') 86 goto done; 87 size = sizeof (v); 88 rv = lzbe_add_pair(nv, key, type, &v, size); 89 } else if (strcmp(type, "DATA_TYPE_INT32") == 0) { 90 int32_t v; 91 92 v = strtol(value, &end, 0); 93 if (errno != 0 || *end != '\0') 94 goto done; 95 size = sizeof (v); 96 rv = lzbe_add_pair(nv, key, type, &v, size); 97 } else if (strcmp(type, "DATA_TYPE_UINT16") == 0) { 98 uint16_t v; 99 100 v = strtoul(value, &end, 0); 101 if (errno != 0 || *end != '\0') 102 goto done; 103 size = sizeof (v); 104 rv = lzbe_add_pair(nv, key, type, &v, size); 105 } else if (strcmp(type, "DATA_TYPE_INT16") == 0) { 106 int16_t v; 107 108 v = strtol(value, &end, 0); 109 if (errno != 0 || *end != '\0') 110 goto done; 111 size = sizeof (v); 112 rv = lzbe_add_pair(nv, key, type, &v, size); 113 } else if (strcmp(type, "DATA_TYPE_UINT8") == 0) { 114 uint8_t v; 115 116 v = strtoul(value, &end, 0); 117 if (errno != 0 || *end != '\0') 118 goto done; 119 size = sizeof (v); 120 rv = lzbe_add_pair(nv, key, type, &v, size); 121 } else if (strcmp(type, "DATA_TYPE_INT8") == 0) { 122 int8_t v; 123 124 v = strtol(value, &end, 0); 125 if (errno != 0 || *end != '\0') 126 goto done; 127 size = sizeof (v); 128 rv = lzbe_add_pair(nv, key, type, &v, size); 129 } else if (strcmp(type, "DATA_TYPE_BYTE") == 0) { 130 uint8_t v; 131 132 v = strtoul(value, &end, 0); 133 if (errno != 0 || *end != '\0') 134 goto done; 135 size = sizeof (v); 136 rv = lzbe_add_pair(nv, key, type, &v, size); 137 } else if (strcmp(type, "DATA_TYPE_BOOLEAN_VALUE") == 0) { 138 int32_t v; 139 140 v = strtol(value, &end, 0); 141 if (errno != 0 || *end != '\0') { 142 if (strcasecmp(value, "YES") == 0) 143 v = 1; 144 else if (strcasecmp(value, "NO") == 0) 145 v = 0; 146 if (strcasecmp(value, "true") == 0) 147 v = 1; 148 else if (strcasecmp(value, "false") == 0) 149 v = 0; 150 else goto done; 151 } 152 size = sizeof (v); 153 rv = lzbe_add_pair(nv, key, type, &v, size); 154 } 155 156 if (rv == 0) 157 rv = lzbe_nvlist_set(name, nvlist, nv); 158 159 done: 160 lzbe_nvlist_free(nv); 161 return (rv); 162 } 163 164 static int 165 delete_pair(const char *name, const char *nvlist, const char *key) 166 { 167 void *nv; 168 int rv; 169 170 rv = lzbe_nvlist_get(name, nvlist, &nv); 171 if (rv == 0) { 172 rv = lzbe_remove_pair(nv, key); 173 } 174 if (rv == 0) 175 rv = lzbe_nvlist_set(name, nvlist, nv); 176 177 lzbe_nvlist_free(nv); 178 return (rv); 179 } 180 181 /* 182 * Usage: zfsbootcfg [-z pool] [-d key] [-k key -t type -v value] [-p] 183 * zfsbootcfg [-z pool] -n nvlist [-d key] [-k key -t type -v value] [-p] 184 * 185 * if nvlist is set, we will update nvlist in bootenv. 186 * if nvlist is not set, we update pairs in bootenv. 187 */ 188 int 189 main(int argc, char * const *argv) 190 { 191 char buf[ZFS_MAXNAMELEN], *name; 192 const char *key, *value, *type, *nvlist; 193 int rv; 194 bool print, delete; 195 196 nvlist = NULL; 197 name = NULL; 198 key = NULL; 199 type = NULL; 200 value = NULL; 201 print = delete = false; 202 while ((rv = getopt(argc, argv, "d:k:n:pt:v:z:")) != -1) { 203 switch (rv) { 204 case 'd': 205 delete = true; 206 key = optarg; 207 break; 208 case 'k': 209 key = optarg; 210 break; 211 case 'n': 212 nvlist = optarg; 213 break; 214 case 'p': 215 print = true; 216 break; 217 case 't': 218 type = optarg; 219 break; 220 case 'v': 221 value = optarg; 222 break; 223 case 'z': 224 name = optarg; 225 break; 226 } 227 } 228 229 argc -= optind; 230 argv += optind; 231 232 if (argc == 1) 233 value = argv[0]; 234 235 if (argc > 1) { 236 fprintf(stderr, "usage: zfsbootcfg <boot.config(5) options>\n"); 237 return (1); 238 } 239 240 if (name == NULL) { 241 rv = kenv(KENV_GET, "vfs.root.mountfrom", buf, sizeof(buf)); 242 if (rv <= 0) { 243 perror("can't get vfs.root.mountfrom"); 244 return (1); 245 } 246 247 if (strncmp(buf, "zfs:", 4) == 0) { 248 name = strchr(buf + 4, '/'); 249 if (name != NULL) 250 *name = '\0'; 251 name = buf + 4; 252 } else { 253 perror("not a zfs root"); 254 return (1); 255 } 256 } 257 258 rv = 0; 259 if (key != NULL || value != NULL) { 260 if (type == NULL) 261 type = "DATA_TYPE_STRING"; 262 263 if (delete) 264 rv = delete_pair(name, nvlist, key); 265 else if (key == NULL || strcmp(key, "command") == 0) 266 rv = lzbe_set_boot_device(name, lzbe_add, value); 267 else 268 rv = add_pair(name, nvlist, key, type, value); 269 270 if (rv == 0) 271 printf("zfs bootenv is successfully written\n"); 272 else 273 printf("error: %d\n", rv); 274 } else if (!print) { 275 char *ptr; 276 277 if (lzbe_get_boot_device(name, &ptr) == 0) { 278 printf("zfs:%s:\n", ptr); 279 free(ptr); 280 } 281 } 282 283 if (print) { 284 rv = lzbe_bootenv_print(name, nvlist, stdout); 285 } 286 287 return (rv); 288 } 289