1 /*- 2 * Copyright 2020 Toomas Soome <tsoome@me.com> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 /* 27 * Big Theory Statement. 28 * 29 * nvstore is abstraction layer to implement data read/write to different 30 * types of non-volatile storage. 31 * 32 * User interfaces: 33 * Provide mapping via environment: setenv/unsetenv/putenv. Access via 34 * environment functions/commands is available once nvstore has 35 * attached the backend and stored textual data is mapped to environment. 36 * 37 * Provide command "nvstore" to create new data instances. 38 * 39 * API: TBD. 40 * nvstore_init(): attach new backend and create the environment mapping. 41 * nvstore_fini: detach backend and unmap the related environment. 42 * 43 * The disk based storage, such as UFS file or ZFS bootenv label area, is 44 * only accessible after root file system is set. Root file system change 45 * will switch the back end storage. 46 */ 47 48 #include <sys/cdefs.h> 49 __FBSDID("$FreeBSD$"); 50 51 #include <stdbool.h> 52 #include <sys/queue.h> 53 #include <bootstrap.h> 54 #include "stand.h" 55 56 typedef struct nvstore { 57 char *nvs_name; 58 void *nvs_data; 59 nvs_callbacks_t *nvs_cb; 60 STAILQ_ENTRY(nvstore) nvs_next; 61 } nvstore_t; 62 63 typedef STAILQ_HEAD(store_list, nvstore) nvstore_list_t; 64 65 nvstore_list_t stores = STAILQ_HEAD_INITIALIZER(stores); 66 67 void * 68 nvstore_get_store(const char *name) 69 { 70 nvstore_t *st; 71 72 st = NULL; 73 74 STAILQ_FOREACH(st, &stores, nvs_next) { 75 if (strcmp(name, st->nvs_name) == 0) 76 break; 77 } 78 79 return (st); 80 } 81 82 int 83 nvstore_init(const char *name, nvs_callbacks_t *cb, void *data) 84 { 85 nvstore_t *st; 86 87 st = nvstore_get_store(name); 88 if (st != NULL) 89 return (EEXIST); 90 91 if ((st = malloc(sizeof (*st))) == NULL) 92 return (ENOMEM); 93 94 if ((st->nvs_name = strdup(name)) == NULL) { 95 free(st); 96 return (ENOMEM); 97 } 98 99 st->nvs_data = data; 100 st->nvs_cb = cb; 101 102 STAILQ_INSERT_TAIL(&stores, st, nvs_next); 103 return (0); 104 } 105 106 int 107 nvstore_fini(const char *name) 108 { 109 nvstore_t *st; 110 111 st = nvstore_get_store(name); 112 if (st == NULL) 113 return (ENOENT); 114 115 STAILQ_REMOVE(&stores, st, nvstore, nvs_next); 116 117 free(st->nvs_name); 118 free(st->nvs_data); 119 free(st); 120 return (0); 121 } 122 123 int 124 nvstore_print(void *ptr) 125 { 126 nvstore_t *st = ptr; 127 128 return (st->nvs_cb->nvs_iterate(st->nvs_data, st->nvs_cb->nvs_print)); 129 } 130 131 int 132 nvstore_get_var(void *ptr, const char *name, void **data) 133 { 134 nvstore_t *st = ptr; 135 136 return (st->nvs_cb->nvs_getter(st->nvs_data, name, data)); 137 } 138 139 int 140 nvstore_set_var(void *ptr, int type, const char *name, 141 void *data, size_t size) 142 { 143 nvstore_t *st = ptr; 144 145 return (st->nvs_cb->nvs_setter(st->nvs_data, type, name, data, size)); 146 } 147 148 int 149 nvstore_set_var_from_string(void *ptr, const char *type, const char *name, 150 const char *data) 151 { 152 nvstore_t *st = ptr; 153 154 return (st->nvs_cb->nvs_setter_str(st->nvs_data, type, name, data)); 155 } 156 157 int 158 nvstore_unset_var(void *ptr, const char *name) 159 { 160 nvstore_t *st = ptr; 161 162 return (st->nvs_cb->nvs_unset(st->nvs_data, name)); 163 } 164 165 COMMAND_SET(nvstore, "nvstore", "manage non-volatile data", command_nvstore); 166 167 static void 168 nvstore_usage(const char *me) 169 { 170 printf("Usage:\t%s -l\n", me); 171 printf("\t%s store -l\n", me); 172 printf("\t%s store [-t type] key value\n", me); 173 printf("\t%s store -g key\n", me); 174 printf("\t%s store -d key\n", me); 175 } 176 177 /* 178 * Usage: nvstore -l # list stores 179 * nvstore store -l # list data in store 180 * nvstore store [-t type] key value 181 * nvstore store -g key # get value 182 * nvstore store -d key # delete key 183 */ 184 static int 185 command_nvstore(int argc, char *argv[]) 186 { 187 int c; 188 bool list, get, delete; 189 nvstore_t *st; 190 char *me, *name, *type; 191 192 me = argv[0]; 193 optind = 1; 194 optreset = 1; 195 196 list = false; 197 while ((c = getopt(argc, argv, "l")) != -1) { 198 switch (c) { 199 case 'l': 200 list = true; 201 break; 202 case '?': 203 default: 204 return (CMD_ERROR); 205 } 206 } 207 208 argc -= optind; 209 argv += optind; 210 211 if (argc == 0) { 212 if (list) { 213 if (STAILQ_EMPTY(&stores)) { 214 printf("No configured nvstores\n"); 215 return (CMD_OK); 216 } 217 printf("List of configured nvstores:\n"); 218 STAILQ_FOREACH(st, &stores, nvs_next) { 219 printf("\t%s\n", st->nvs_name); 220 } 221 return (CMD_OK); 222 } 223 nvstore_usage(me); 224 return (CMD_ERROR); 225 } 226 227 if (argc == 0 || (argc != 0 && list)) { 228 nvstore_usage(me); 229 return (CMD_ERROR); 230 } 231 232 st = nvstore_get_store(argv[0]); 233 if (st == NULL) { 234 nvstore_usage(me); 235 return (CMD_ERROR); 236 } 237 238 optind = 1; 239 optreset = 1; 240 name = NULL; 241 type = NULL; 242 get = delete = false; 243 244 while ((c = getopt(argc, argv, "d:g:lt:")) != -1) { 245 switch (c) { 246 case 'd': 247 if (list || get) { 248 nvstore_usage(me); 249 return (CMD_ERROR); 250 } 251 name = optarg; 252 delete = true; 253 break; 254 case 'g': 255 if (delete || list) { 256 nvstore_usage(me); 257 return (CMD_ERROR); 258 } 259 name = optarg; 260 get = true; 261 break; 262 case 'l': 263 if (delete || get) { 264 nvstore_usage(me); 265 return (CMD_ERROR); 266 } 267 list = true; 268 break; 269 case 't': 270 type = optarg; 271 break; 272 case '?': 273 default: 274 return (CMD_ERROR); 275 } 276 } 277 278 argc -= optind; 279 argv += optind; 280 281 if (list) { 282 (void) nvstore_print(st); 283 return (CMD_OK); 284 } 285 286 if (delete && name != NULL) { 287 (void) nvstore_unset_var(st, name); 288 return (CMD_OK); 289 } 290 291 if (get && name != NULL) { 292 char *ptr = NULL; 293 294 if (nvstore_get_var(st, name, (void **)&ptr) == 0) 295 printf("%s = %s\n", name, ptr); 296 return (CMD_OK); 297 } 298 299 if (argc == 2) { 300 c = nvstore_set_var_from_string(st, type, argv[0], argv[1]); 301 if (c != 0) { 302 printf("error: %s\n", strerror(c)); 303 return (CMD_ERROR); 304 } 305 return (CMD_OK); 306 } 307 308 nvstore_usage(me); 309 return (CMD_OK); 310 } 311