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 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 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 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