diff --git a/Make.defaults b/Make.defaults index c4c0592..381526b 100644 --- a/Make.defaults +++ b/Make.defaults @@ -46,6 +46,9 @@ cflags = $(EXTRALIBDIR) $(EXTRAINCDIR) $(CFLAGS) $(SUBDIR_CFLAGS) \ $(if $(findstring clang,$(CC)),$(clang_cflags),) \ $(if $(findstring gcc,$(CC)),$(gcc_cflags),) \ $(call pkg-config-cflags) +ifdef JSON + cflags += -DJSON +endif clang_ccldflags = gcc_ccldflags = -fno-merge-constants \ -Wl,--fatal-warnings,--no-allow-shlib-undefined \ diff --git a/src/Makefile b/src/Makefile index 2f1bec4..7e2612f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,14 +11,21 @@ TARGETS=$(BINTARGETS) efibootmgr.8 efibootdump.8 all : deps $(TARGETS) EFIBOOTMGR_SOURCES = efibootmgr.c efi.c parse_loader_data.c +ifdef JSON +EFIBOOTMGR_SOURCES += json.c +endif EFICONMAN_SOURCES = eficonman.c EFIBOOTDUMP_SOURCES = efibootdump.c parse_loader_data.c EFIBOOTNEXT_SOURCES = efibootnext.c ALL_SOURCES=$(EFIBOOTMGR_SOURCES) -include $(call deps-of,$(ALL_SOURCES)) + efibootmgr : $(call objects-of,$(EFIBOOTMGR_SOURCES)) efibootmgr : PKGS=efivar efiboot +ifdef JSON +efibootmgr : PKGS+=jansson +endif eficonman : $(call objects-of,$(EFICONMAN_SOURCES)) eficonman : PKGS=efivar efiboot popt diff --git a/src/efibootmgr.c b/src/efibootmgr.c index 0094e05..1e57e99 100644 --- a/src/efibootmgr.c +++ b/src/efibootmgr.c @@ -51,11 +51,11 @@ #include #include -#include "list.h" #include "efi.h" #include "parse_loader_data.h" #include "efibootmgr.h" #include "error.h" +#include "json.h" #ifndef EFIBOOTMGR_VERSION #define EFIBOOTMGR_VERSION "unknown (fix Makefile!)" @@ -63,16 +63,6 @@ int verbose; -typedef struct _var_entry { - char *name; - efi_guid_t guid; - uint8_t *data; - size_t data_size; - uint32_t attributes; - uint16_t num; - list_t list; -} var_entry_t; - /* global variables */ static LIST_HEAD(entry_list); static LIST_HEAD(blk_list); @@ -348,7 +338,7 @@ make_var(const char *prefix, list_t *var_list) return NULL; } -static int +int read_order(const char *name, var_entry_t **order) { int rc; @@ -548,7 +538,7 @@ remove_from_order(const char *name, uint16_t num) return rc; } -static int +int read_u16(const char *name) { efi_guid_t guid = EFI_GLOBAL_GUID; @@ -1467,6 +1457,7 @@ usage() printf("\t-g | --gpt Force disk with invalid PMBR to be treated as GPT.\n"); printf("\t-i | --iface name Create a netboot entry for the named interface.\n"); printf("\t-I | --index number When creating an entry, insert it in bootorder at specified position (default: 0).\n"); + printf("\t-j | --json Enable JSON output\n"); printf("\t-l | --loader name (Defaults to \""DEFAULT_LOADER"\").\n"); printf("\t-L | --label label Boot manager display label (defaults to \"Linux\").\n"); printf("\t-m | --mirror-below-4G t|f Mirror memory below 4GB.\n"); @@ -1537,6 +1528,7 @@ parse_opts(int argc, char **argv) {"gpt", no_argument, 0, 'g'}, {"iface", required_argument, 0, 'i'}, {"index", required_argument, 0, 'I'}, + {"json", no_argument, 0, 'j'}, {"keep", no_argument, 0, 'k'}, {"loader", required_argument, 0, 'l'}, {"label", required_argument, 0, 'L'}, @@ -1563,7 +1555,7 @@ parse_opts(int argc, char **argv) }; c = getopt_long(argc, argv, - "aAb:BcCd:De:E:fFgi:I:kl:L:m:M:n:No:Op:qrst:Tuv::Vwy@:h", + "aAb:BcCd:De:E:fFgi:I:jkl:L:m:M:n:No:Op:qrst:Tuv::Vwy@:h", long_options, &option_index); if (c == -1) break; @@ -1677,6 +1669,9 @@ parse_opts(int argc, char **argv) } opts.index = (uint16_t)lindex; break; + case 'j': + opts.json = 1; + break; case 'k': opts.keep_old_entries = 1; break; @@ -2017,7 +2012,7 @@ main(int argc, char **argv) ret=set_mirror(opts.below4g, opts.above4g); } - if (!opts.quiet && ret == 0) { + if (!opts.quiet && !opts.json && ret == 0) { switch (mode) { case boot: num = read_u16("BootNext"); @@ -2046,6 +2041,11 @@ main(int argc, char **argv) break; } } + + if (!opts.quiet && opts.json && ret == 0) { + print_json(&entry_list, mode, prefices, order_name); + } + free_vars(&entry_list); free_array(names); if (ret) diff --git a/src/efibootmgr.h b/src/efibootmgr.h index 34fa56c..0f93f98 100644 --- a/src/efibootmgr.h +++ b/src/efibootmgr.h @@ -35,6 +35,8 @@ #define EFIBOOTMGR_PATH_ABBREV_NONE 3 #define EFIBOOTMGR_PATH_ABBREV_FILE 4 +#include "list.h" + typedef enum { boot, driver, @@ -93,8 +95,22 @@ typedef struct { unsigned int sysprep:1; unsigned int explicit_label:1; unsigned int list_supported_signature_types:1; + unsigned int json:1; short int timeout; uint16_t index; } efibootmgr_opt_t; +typedef struct _var_entry { + char *name; + efi_guid_t guid; + uint8_t *data; + size_t data_size; + uint32_t attributes; + uint16_t num; + list_t list; +} var_entry_t; + extern efibootmgr_opt_t opts; + +int read_u16(const char *name); +int read_order(const char *name, var_entry_t **order); diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..e599389 --- /dev/null +++ b/src/json.c @@ -0,0 +1,173 @@ +#include +#include + +#include "parse_loader_data.h" +#include "efibootmgr.h" +#include "error.h" +#include "json.h" + +static void +json_fill_bootnext(json_t *root) +{ + char s[5] = {}; + json_t *value; + int num; + + num = read_u16("BootNext"); + cond_warning(opts.verbose >= 2 && num < 0, + "Could not read variable 'BootNext'"); + if (num >= 0) { + snprintf(s, sizeof(s), "%04X", num); + value = json_string(s); + json_object_set_new(root, "BootNext", value); + } +} + +static void +json_fill_bootcurrent(json_t *root) +{ + char s[5] = {}; + json_t *value; + int num; + + num = read_u16("BootCurrent"); + cond_warning(opts.verbose >= 2 && num < 0, + "Could not read variable 'BootCurrent'"); + if (num >= 0) { + snprintf(s, sizeof(s), "%04X", num); + value = json_string(s); + json_object_set_new(root, "BootCurrent", value); + } +} + +static void +json_fill_timeout(json_t *root) +{ + json_t *value; + int num; + + num = read_u16("Timeout"); + cond_warning(opts.verbose >= 2 && num < 0, + "Could not read variable 'Timeout'"); + if (num >= 0) { + value = json_integer(num); + json_object_set_new(root, "Timeout", value); + } +} + +static json_t * +bootorder_json_array(uint16_t *order, int length) +{ + json_t *value, *array; + char s[5] = {}; + int i; + + array = json_array(); + if (!array) + return NULL; + + for (i = 0; i < length; i++) { + snprintf(s, sizeof(s), "%04X", order[i]); + value = json_string(s); + json_array_append_new(array, value); + } + + return array; +} + +static void +json_fill_order(json_t *root, const char *name) +{ + var_entry_t *order = NULL; + uint16_t *data; + json_t *array; + int rc; + + rc = read_order(name, &order); + cond_warning(opts.verbose >= 2 && rc < 0, + "Could not read variable '%s'", name); + + if (rc < 0) { + if (errno == ENOENT) { + if (!strcmp(name, "BootOrder")) + printf("No BootOrder is set; firmware will attempt recovery\n"); + else + printf("No %s is set\n", name); + } else + perror("json_fill_order()"); + return; + } + + data = (uint16_t *)order->data; + if (order->data_size) { + array = bootorder_json_array(data, + order->data_size / sizeof(uint16_t)); + if (array != NULL) + json_object_set_new(root, name, array); + free(order->data); + } + free(order); +} + +static void +json_fill_vars(json_t *root, const char *prefix, list_t *entry_list) +{ + const unsigned char *description; + json_t *boot_json, *vars_json; + efi_load_option *load_option; + char name[16] = {'\0'}; + var_entry_t *boot; + list_t *pos; + int active; + + vars_json = json_array(); + if (!vars_json) + return; + + list_for_each(pos, entry_list) { + boot_json = json_object(); + boot = list_entry(pos, var_entry_t, list); + load_option = (efi_load_option *)boot->data; + description = efi_loadopt_desc(load_option, boot->data_size); + if (boot->name) + json_object_set_new(boot_json, "name", json_string(boot->name)); + else { + snprintf(name, sizeof(name), "%s%04X", prefix, boot->num); + json_object_set_new(boot_json, "name", json_string(boot->name)); + } + + active = efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE ? 1 : 0; + json_object_set_new(boot_json, "active", json_boolean(active)); + json_object_set_new(boot_json, "description", + json_string((char *)description)); + json_array_append_new(vars_json, boot_json); + } + + json_object_set_new(root, "vars", vars_json); +} + +void +__print_json(list_t *entry_list, ebm_mode mode, char **prefices, char **order_name) +{ + json_t *root = json_object(); + char *json_str = NULL; + + switch (mode) { + case boot: + json_fill_bootnext(root); + json_fill_bootcurrent(root); + json_fill_timeout(root); + json_fill_order(root, order_name[mode]); + json_fill_vars(root, prefices[mode], entry_list); + break; + case driver: + case sysprep: + json_fill_order(root, order_name[mode]); + json_fill_vars(root, prefices[mode], entry_list); + break; + } + json_str = json_dumps(root, JSON_COMPACT); + printf("%s\n", json_str); + free(json_str); + json_decref(root); +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..75c1d01 --- /dev/null +++ b/src/json.h @@ -0,0 +1,21 @@ +#include + +#include "list.h" + +void __print_json(list_t *entry_list, ebm_mode mode, + char **prefices, char **order_name); + +#ifndef JSON +#define __unused __attribute__((unused)) +void print_json(list_t __unused *entry_list, ebm_mode __unused mode, + char __unused **prefices, char __unused **order_name) +{ + printf("JSON support is not built-in\n"); +} +#else +static inline void print_json(list_t *entry_list, ebm_mode mode, + char **prefices, char **order_name) +{ + __print_json(entry_list, mode, prefices, order_name); +} +#endif