Skip to content

Commit

Permalink
Add optional JSON output support
Browse files Browse the repository at this point in the history
Enable JSON output for efibootmgr. This provides an interface for third
party tools so they do not need to scrap the existing textual output.

This feature is optional and is enabled by using JSON=1 when compiling:

	make ... JSON=1

Add parameter '-j'/--json' to use JSON output. If efibootmgr is not
built with JSON output support it will print out this instead:

	"JSON support is not built-in"

This feature adds a new (optional) dependency with libjansson.

Signed-off-by: Jose M. Guisado <[email protected]>
  • Loading branch information
pvxe committed Feb 21, 2024
1 parent 2d930d8 commit 9dc1f01
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 2 deletions.
3 changes: 3 additions & 0 deletions Make.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
7 changes: 7 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,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
Expand Down
15 changes: 13 additions & 2 deletions src/efibootmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "parse_loader_data.h"
#include "efibootmgr.h"
#include "error.h"
#include "json.h"

#ifndef EFIBOOTMGR_VERSION
#define EFIBOOTMGR_VERSION "unknown (fix Makefile!)"
Expand Down Expand Up @@ -1401,6 +1402,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 (IPv4+DHCP support only).\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 --uri Uri Specify an Uri (for use with --iface option).\n");
printf("\t-l | --loader name (Defaults to \""DEFAULT_LOADER"\").\n");
printf("\t-L | --label label Boot manager display label (defaults to \"Linux\").\n");
Expand Down Expand Up @@ -1472,6 +1474,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'},
{"uri", required_argument, 0, 0},
{"keep", no_argument, 0, 'k'},
{"loader", required_argument, 0, 'l'},
Expand All @@ -1498,7 +1501,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:qrt:Tuv::Vwy@:h",
"aAb:BcCd:De:E:fFgi:I:j:kl:L:m:M:n:No:Op:qrt:Tuv::Vwy@:h",
long_options, &option_index);
if (c == -1)
break;
Expand Down Expand Up @@ -1612,6 +1615,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;
Expand Down Expand Up @@ -1947,7 +1953,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");
Expand Down Expand Up @@ -1976,6 +1982,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)
Expand Down
1 change: 1 addition & 0 deletions src/include/efibootmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ typedef struct {
unsigned int driver:1;
unsigned int sysprep:1;
unsigned int explicit_label:1;
unsigned int json:1;
short int timeout;
uint16_t index;
} efibootmgr_opt_t;
Expand Down
21 changes: 21 additions & 0 deletions src/include/json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <stdio.h>

#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
173 changes: 173 additions & 0 deletions src/json.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include <efiboot.h>
#include <jansson.h>

#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);
}

0 comments on commit 9dc1f01

Please sign in to comment.