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