main.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 3 #include <unistd.h> 4 5 #include <errno.h> 6 #include <stdbool.h> 7 #include <stdint.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 11 #include <commonlib/bsd/helpers.h> 12 13 #include "data.h" 14 #include "guids.h" 15 #include "storage.h" 16 #include "udk2017.h" 17 #include "vs.h" 18 19 struct subcommand_t { 20 const char *name; 21 const char *description; 22 void (*print_help)(FILE *f, const struct subcommand_t *info); 23 int (*process)(int argc, char *argv[], const char store_file[]); 24 }; 25 26 static void help_get(FILE *f, const struct subcommand_t *info); 27 static void help_guids(FILE *f, const struct subcommand_t *info); 28 static void help_help(FILE *f, const struct subcommand_t *info); 29 static void help_list(FILE *f, const struct subcommand_t *info); 30 static void help_remove(FILE *f, const struct subcommand_t *info); 31 static void help_set(FILE *f, const struct subcommand_t *info); 32 static int process_get(int argc, char *argv[], const char store_file[]); 33 static int process_guids(int argc, char *argv[], const char store_file[]); 34 static int process_help(int argc, char *argv[], const char store_file[]); 35 static int process_list(int argc, char *argv[], const char store_file[]); 36 static int process_remove(int argc, char *argv[], const char store_file[]); 37 static int process_set(int argc, char *argv[], const char store_file[]); 38 39 static const struct subcommand_t sub_commands[] = { 40 { 41 .name = "get", 42 .description = "display current value of a variable", 43 .print_help = &help_get, 44 .process = &process_get, 45 }, 46 { 47 .name = "guids", 48 .description = "show GUID to alias mapping", 49 .print_help = &help_guids, 50 .process = &process_guids, 51 }, 52 { 53 .name = "help", 54 .description = "provide built-in help", 55 .print_help = &help_help, 56 .process = &process_help, 57 }, 58 { 59 .name = "list", 60 .description = "list variables present in the store", 61 .print_help = &help_list, 62 .process = &process_list, 63 }, 64 { 65 .name = "remove", 66 .description = "remove a variable from the store", 67 .print_help = &help_remove, 68 .process = &process_remove, 69 }, 70 { 71 .name = "set", 72 .description = "add or updates a variable in the store", 73 .print_help = &help_set, 74 .process = &process_set, 75 }, 76 }; 77 78 static const int sub_command_count = ARRAY_SIZE(sub_commands); 79 80 static const char *USAGE_FMT = "Usage: %s smm-store-file|rom sub-command\n" 81 " %s -h|--help\n"; 82 83 static const char *program_name; 84 85 static void print_program_usage(void) 86 { 87 fprintf(stderr, USAGE_FMT, program_name, program_name); 88 exit(EXIT_FAILURE); 89 } 90 91 static void print_sub_command_usage(const char sub_command[]) 92 { 93 fprintf(stderr, "\n"); 94 fprintf(stderr, USAGE_FMT, program_name, program_name); 95 fprintf(stderr, "\n"); 96 97 for (int i = 0; i < sub_command_count; ++i) { 98 const struct subcommand_t *cmd = &sub_commands[i]; 99 if (!str_eq(cmd->name, sub_command)) 100 continue; 101 102 cmd->print_help(stderr, cmd); 103 break; 104 } 105 106 exit(EXIT_FAILURE); 107 } 108 109 static void print_help(void) 110 { 111 printf(USAGE_FMT, program_name, program_name); 112 113 printf("\n"); 114 printf("Sub-commands:\n"); 115 for (int i = 0; i < sub_command_count; ++i) { 116 const struct subcommand_t *cmd = &sub_commands[i]; 117 printf(" * %-6s - %s\n", cmd->name, cmd->description); 118 } 119 } 120 121 static void print_types(FILE *f) 122 { 123 fprintf(f, "Types and their values:\n"); 124 fprintf(f, " * bool (true, false)\n"); 125 fprintf(f, " * uint8 (0..255)\n"); 126 fprintf(f, " * uint16 (0..65535)\n"); 127 fprintf(f, " * uint32 (0..4294967295)\n"); 128 fprintf(f, " * uint64 (0..2^64-1)\n"); 129 fprintf(f, " * ascii (NUL-terminated)\n"); 130 fprintf(f, " * unicode (widened and NUL-terminated)\n"); 131 fprintf(f, " * raw (output only; raw bytes on output)\n"); 132 } 133 134 static void help_set(FILE *f, const struct subcommand_t *info) 135 { 136 fprintf(f, "Create or update a variable:\n"); 137 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name); 138 fprintf(f, " -g vendor-guid \\\n"); 139 fprintf(f, " -n variable-name \\\n"); 140 fprintf(f, " -t variable-type \\\n"); 141 fprintf(f, " -v value\n"); 142 fprintf(f, "\n"); 143 print_types(f); 144 } 145 146 static int process_set(int argc, char *argv[], const char store_file[]) 147 { 148 const char *name = NULL; 149 const char *value = NULL; 150 const char *type_str = NULL; 151 const char *guid_str = NULL; 152 int opt; 153 while ((opt = getopt(argc, argv, "n:t:v:g:")) != -1) { 154 switch (opt) { 155 case 'n': 156 name = optarg; 157 break; 158 case 't': 159 type_str = optarg; 160 break; 161 case 'v': 162 value = optarg; 163 break; 164 case 'g': 165 guid_str = optarg; 166 break; 167 168 case '?': /* parsing error */ 169 print_sub_command_usage(argv[0]); 170 } 171 } 172 173 if (argv[optind] != NULL) { 174 fprintf(stderr, "First unexpected positional argument: %s\n", 175 argv[optind]); 176 print_sub_command_usage(argv[0]); 177 } 178 179 if (name == NULL || value == NULL || type_str == NULL || 180 guid_str == NULL) { 181 fprintf(stderr, "All options are required\n"); 182 print_sub_command_usage(argv[0]); 183 } 184 185 if (name[0] == '\0') { 186 fprintf(stderr, "Variable name can't be empty\n"); 187 print_sub_command_usage(argv[0]); 188 } 189 190 EFI_GUID guid; 191 if (!parse_guid(guid_str, &guid)) { 192 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str); 193 return EXIT_FAILURE; 194 } 195 196 enum data_type type; 197 if (!parse_data_type(type_str, &type)) { 198 fprintf(stderr, "Failed to parse type: %s\n", type_str); 199 return EXIT_FAILURE; 200 } 201 202 size_t data_size; 203 void *data = make_data(value, &data_size, type); 204 if (data == NULL) { 205 fprintf(stderr, "Failed to parse value \"%s\" as %s\n", 206 value, type_str); 207 return EXIT_FAILURE; 208 } 209 210 struct storage_t storage; 211 if (!storage_open(store_file, &storage, /*rw=*/true)) { 212 free(data); 213 return EXIT_FAILURE; 214 } 215 216 struct var_t *var = vs_find(&storage.vs, name, &guid); 217 if (var == NULL) { 218 var = vs_new_var(&storage.vs); 219 var->name = to_uchars(name, &var->name_size); 220 var->data = data; 221 var->data_size = data_size; 222 var->guid = guid; 223 } else { 224 free(var->data); 225 var->data = data; 226 var->data_size = data_size; 227 } 228 229 return storage_write_back(&storage) ? EXIT_SUCCESS : EXIT_FAILURE; 230 } 231 232 static void help_list(FILE *f, const struct subcommand_t *info) 233 { 234 fprintf(f, "List variables in the store:\n"); 235 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name); 236 } 237 238 static int process_list(int argc, char *argv[], const char store_file[]) 239 { 240 if (argc != 1) { 241 fprintf(stderr, "Invalid invocation\n"); 242 print_sub_command_usage(argv[0]); 243 } 244 245 struct storage_t storage; 246 if (!storage_open(store_file, &storage, /*rw=*/false)) 247 return EXIT_FAILURE; 248 249 for (struct var_t *v = storage.vs.vars; v != NULL; v = v->next) { 250 char *name = to_chars(v->name, v->name_size); 251 char *guid = format_guid(&v->guid, /*use_alias=*/true); 252 253 printf("%-*s:%s (%zu %s)\n", GUID_LEN, guid, name, v->data_size, 254 v->data_size == 1 ? "byte" : "bytes"); 255 256 free(name); 257 free(guid); 258 } 259 260 storage_drop(&storage); 261 return EXIT_SUCCESS; 262 } 263 264 static void help_get(FILE *f, const struct subcommand_t *info) 265 { 266 fprintf(f, "Read variable's value:\n"); 267 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name); 268 fprintf(f, " -g vendor-guid \\\n"); 269 fprintf(f, " -n variable-name \\\n"); 270 fprintf(f, " -t variable-type\n"); 271 fprintf(f, "\n"); 272 print_types(f); 273 } 274 275 static int process_get(int argc, char *argv[], const char store_file[]) 276 { 277 const char *name = NULL; 278 const char *type_str = NULL; 279 const char *guid_str = NULL; 280 int opt; 281 while ((opt = getopt(argc, argv, "n:g:t:")) != -1) { 282 switch (opt) { 283 case 'n': 284 name = optarg; 285 break; 286 case 'g': 287 guid_str = optarg; 288 break; 289 case 't': 290 type_str = optarg; 291 break; 292 293 case '?': /* parsing error */ 294 print_sub_command_usage(argv[0]); 295 } 296 } 297 298 if (name == NULL || type_str == NULL || guid_str == NULL) { 299 fprintf(stderr, "All options are required\n"); 300 print_sub_command_usage(argv[0]); 301 } 302 303 EFI_GUID guid; 304 if (!parse_guid(guid_str, &guid)) { 305 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str); 306 return EXIT_FAILURE; 307 } 308 309 enum data_type type; 310 if (!parse_data_type(type_str, &type)) { 311 fprintf(stderr, "Failed to parse type: %s\n", type_str); 312 return EXIT_FAILURE; 313 } 314 315 struct storage_t storage; 316 if (!storage_open(store_file, &storage, /*rw=*/false)) 317 return EXIT_FAILURE; 318 319 int result = EXIT_SUCCESS; 320 321 struct var_t *var = vs_find(&storage.vs, name, &guid); 322 if (var == NULL) { 323 result = EXIT_FAILURE; 324 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n", 325 guid_str, name); 326 } else if (var->data_size == 0) { 327 fprintf(stderr, "There is no data to show.\n"); 328 result = EXIT_FAILURE; 329 } else { 330 print_data(var->data, var->data_size, type); 331 } 332 333 storage_drop(&storage); 334 return result; 335 } 336 337 static void help_help(FILE *f, const struct subcommand_t *info) 338 { 339 fprintf(f, "Display generic help:\n"); 340 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name); 341 fprintf(f, "\n"); 342 fprintf(f, "Display sub-command help:\n"); 343 fprintf(f, " %s smm-store-file|rom %s sub-command\n", 344 program_name, info->name); 345 } 346 347 static int process_help(int argc, char *argv[], const char store_file[]) 348 { 349 (void)store_file; 350 351 if (argc == 1) { 352 print_help(); 353 return EXIT_SUCCESS; 354 } 355 356 if (argc != 2) { 357 fprintf(stderr, "Invalid invocation\n"); 358 print_sub_command_usage(argv[0]); 359 return EXIT_FAILURE; 360 } 361 362 const char *sub_command = argv[1]; 363 364 for (int i = 0; i < sub_command_count; ++i) { 365 const struct subcommand_t *cmd = &sub_commands[i]; 366 if (!str_eq(cmd->name, sub_command)) 367 continue; 368 369 cmd->print_help(stdout, cmd); 370 return EXIT_SUCCESS; 371 } 372 373 fprintf(stderr, "Unknown sub-command: %s\n", sub_command); 374 print_help(); 375 return EXIT_FAILURE; 376 } 377 378 static void help_remove(FILE *f, const struct subcommand_t *info) 379 { 380 fprintf(f, "Remove a variable:\n"); 381 fprintf(f, " %s smm-store-file|rom %s \\\n", program_name, info->name); 382 fprintf(f, " -g vendor-guid \\\n"); 383 fprintf(f, " -n variable-name\n"); 384 } 385 386 static int process_remove(int argc, char *argv[], const char store_file[]) 387 { 388 const char *name = NULL; 389 const char *guid_str = NULL; 390 int opt; 391 while ((opt = getopt(argc, argv, "n:g:")) != -1) { 392 switch (opt) { 393 case 'n': 394 name = optarg; 395 break; 396 case 'g': 397 guid_str = optarg; 398 break; 399 400 case '?': /* parsing error */ 401 print_sub_command_usage(argv[0]); 402 } 403 } 404 405 if (name == NULL || guid_str == NULL) { 406 fprintf(stderr, "All options are required\n"); 407 print_sub_command_usage(argv[0]); 408 } 409 410 EFI_GUID guid; 411 if (!parse_guid(guid_str, &guid)) { 412 fprintf(stderr, "Failed to parse GUID: %s\n", guid_str); 413 return EXIT_FAILURE; 414 } 415 416 struct storage_t storage; 417 if (!storage_open(store_file, &storage, /*rw=*/true)) 418 return EXIT_FAILURE; 419 420 int result = EXIT_SUCCESS; 421 422 struct var_t *var = vs_find(&storage.vs, name, &guid); 423 if (var == NULL) { 424 result = EXIT_FAILURE; 425 fprintf(stderr, "Couldn't find variable \"%s:%s\"\n", 426 guid_str, name); 427 } else { 428 vs_delete(&storage.vs, var); 429 } 430 431 storage_write_back(&storage); 432 return result; 433 } 434 435 static void help_guids(FILE *f, const struct subcommand_t *info) 436 { 437 fprintf(f, "List recognized GUIDS:\n"); 438 fprintf(f, " %s smm-store-file|rom %s\n", program_name, info->name); 439 } 440 441 static int process_guids(int argc, char *argv[], const char store_file[]) 442 { 443 (void)store_file; 444 445 if (argc != 1) { 446 fprintf(stderr, "Invalid invocation\n"); 447 print_sub_command_usage(argv[0]); 448 } 449 450 for (int i = 0; i < known_guid_count; ++i) { 451 char *guid = format_guid(&known_guids[i].guid, 452 /*use_alias=*/false); 453 printf("%-10s -> %s\n", known_guids[i].alias, guid); 454 free(guid); 455 } 456 return EXIT_SUCCESS; 457 } 458 459 int main(int argc, char *argv[]) 460 { 461 program_name = argv[0]; 462 463 if (argc > 1 && (str_eq(argv[1], "-h") || str_eq(argv[1], "--help"))) { 464 print_help(); 465 exit(EXIT_SUCCESS); 466 } 467 468 if (argc < 3) 469 print_program_usage(); 470 471 const char *store_file = argv[1]; 472 const char *sub_command = argv[2]; 473 474 int sub_command_argc = argc - 2; 475 char **sub_command_argv = argv + 2; 476 477 for (int i = 0; i < sub_command_count; ++i) { 478 const struct subcommand_t *cmd = &sub_commands[i]; 479 if (!str_eq(cmd->name, sub_command)) 480 continue; 481 482 return cmd->process(sub_command_argc, 483 sub_command_argv, 484 store_file); 485 } 486 487 fprintf(stderr, "Unknown sub-command: %s\n", sub_command); 488 print_help(); 489 return EXIT_FAILURE; 490 }