Skip to content

Commit 9dc1f01

Browse files
committed
Add optional JSON output support
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]>
1 parent 2d930d8 commit 9dc1f01

File tree

6 files changed

+218
-2
lines changed

6 files changed

+218
-2
lines changed

Make.defaults

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ cflags = $(EXTRALIBDIR) $(EXTRAINCDIR) $(CFLAGS) $(SUBDIR_CFLAGS) \
4646
$(if $(findstring clang,$(CC)),$(clang_cflags),) \
4747
$(if $(findstring gcc,$(CC)),$(gcc_cflags),) \
4848
$(call pkg-config-cflags)
49+
ifdef JSON
50+
cflags += -DJSON
51+
endif
4952
clang_ccldflags =
5053
gcc_ccldflags = -fno-merge-constants \
5154
-Wl,--fatal-warnings,--no-allow-shlib-undefined \

src/Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ TARGETS=$(BINTARGETS) efibootmgr.8 efibootdump.8
1313
all : deps $(TARGETS)
1414

1515
EFIBOOTMGR_SOURCES = efibootmgr.c efi.c parse_loader_data.c
16+
ifdef JSON
17+
EFIBOOTMGR_SOURCES += json.c
18+
endif
1619
EFICONMAN_SOURCES = eficonman.c
1720
EFIBOOTDUMP_SOURCES = efibootdump.c parse_loader_data.c
1821
EFIBOOTNEXT_SOURCES = efibootnext.c
1922
ALL_SOURCES=$(EFIBOOTMGR_SOURCES)
2023
-include $(call deps-of,$(ALL_SOURCES))
2124

25+
2226
efibootmgr : $(call objects-of,$(EFIBOOTMGR_SOURCES))
2327
efibootmgr : PKGS=efivar efiboot
28+
ifdef JSON
29+
efibootmgr : PKGS+=jansson
30+
endif
2431

2532
eficonman : $(call objects-of,$(EFICONMAN_SOURCES))
2633
eficonman : PKGS=efivar efiboot popt

src/efibootmgr.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "parse_loader_data.h"
5656
#include "efibootmgr.h"
5757
#include "error.h"
58+
#include "json.h"
5859

5960
#ifndef EFIBOOTMGR_VERSION
6061
#define EFIBOOTMGR_VERSION "unknown (fix Makefile!)"
@@ -1401,6 +1402,7 @@ usage()
14011402
printf("\t-g | --gpt Force disk with invalid PMBR to be treated as GPT.\n");
14021403
printf("\t-i | --iface name Create a netboot entry for the named interface (IPv4+DHCP support only).\n");
14031404
printf("\t-I | --index number When creating an entry, insert it in bootorder at specified position (default: 0).\n");
1405+
printf("\t-j | --json Enable JSON output\n");
14041406
printf("\t --uri Uri Specify an Uri (for use with --iface option).\n");
14051407
printf("\t-l | --loader name (Defaults to \""DEFAULT_LOADER"\").\n");
14061408
printf("\t-L | --label label Boot manager display label (defaults to \"Linux\").\n");
@@ -1472,6 +1474,7 @@ parse_opts(int argc, char **argv)
14721474
{"gpt", no_argument, 0, 'g'},
14731475
{"iface", required_argument, 0, 'i'},
14741476
{"index", required_argument, 0, 'I'},
1477+
{"json", no_argument, 0, 'j'},
14751478
{"uri", required_argument, 0, 0},
14761479
{"keep", no_argument, 0, 'k'},
14771480
{"loader", required_argument, 0, 'l'},
@@ -1498,7 +1501,7 @@ parse_opts(int argc, char **argv)
14981501
};
14991502

15001503
c = getopt_long(argc, argv,
1501-
"aAb:BcCd:De:E:fFgi:I:kl:L:m:M:n:No:Op:qrt:Tuv::Vwy@:h",
1504+
"aAb:BcCd:De:E:fFgi:I:j:kl:L:m:M:n:No:Op:qrt:Tuv::Vwy@:h",
15021505
long_options, &option_index);
15031506
if (c == -1)
15041507
break;
@@ -1612,6 +1615,9 @@ parse_opts(int argc, char **argv)
16121615
}
16131616
opts.index = (uint16_t)lindex;
16141617
break;
1618+
case 'j':
1619+
opts.json = 1;
1620+
break;
16151621
case 'k':
16161622
opts.keep_old_entries = 1;
16171623
break;
@@ -1947,7 +1953,7 @@ main(int argc, char **argv)
19471953
ret=set_mirror(opts.below4g, opts.above4g);
19481954
}
19491955

1950-
if (!opts.quiet && ret == 0) {
1956+
if (!opts.quiet && !opts.json && ret == 0) {
19511957
switch (mode) {
19521958
case boot:
19531959
num = read_u16("BootNext");
@@ -1976,6 +1982,11 @@ main(int argc, char **argv)
19761982
break;
19771983
}
19781984
}
1985+
1986+
if (!opts.quiet && opts.json && ret == 0) {
1987+
print_json(&entry_list, mode, prefices, order_name);
1988+
}
1989+
19791990
free_vars(&entry_list);
19801991
free_array(names);
19811992
if (ret)

src/include/efibootmgr.h

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef struct {
9595
unsigned int driver:1;
9696
unsigned int sysprep:1;
9797
unsigned int explicit_label:1;
98+
unsigned int json:1;
9899
short int timeout;
99100
uint16_t index;
100101
} efibootmgr_opt_t;

src/include/json.h

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <stdio.h>
2+
3+
#include "list.h"
4+
5+
void __print_json(list_t *entry_list, ebm_mode mode,
6+
char **prefices, char **order_name);
7+
8+
#ifndef JSON
9+
#define __unused __attribute__((unused))
10+
void print_json(list_t __unused *entry_list, ebm_mode __unused mode,
11+
char __unused **prefices, char __unused **order_name)
12+
{
13+
printf("JSON support is not built-in\n");
14+
}
15+
#else
16+
static inline void print_json(list_t *entry_list, ebm_mode mode,
17+
char **prefices, char **order_name)
18+
{
19+
__print_json(entry_list, mode, prefices, order_name);
20+
}
21+
#endif

src/json.c

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#include <efiboot.h>
2+
#include <jansson.h>
3+
4+
#include "parse_loader_data.h"
5+
#include "efibootmgr.h"
6+
#include "error.h"
7+
#include "json.h"
8+
9+
static void
10+
json_fill_bootnext(json_t *root)
11+
{
12+
char s[5] = {};
13+
json_t *value;
14+
int num;
15+
16+
num = read_u16("BootNext");
17+
cond_warning(opts.verbose >= 2 && num < 0,
18+
"Could not read variable 'BootNext'");
19+
if (num >= 0) {
20+
snprintf(s, sizeof(s), "%04X", num);
21+
value = json_string(s);
22+
json_object_set_new(root, "BootNext", value);
23+
}
24+
}
25+
26+
static void
27+
json_fill_bootcurrent(json_t *root)
28+
{
29+
char s[5] = {};
30+
json_t *value;
31+
int num;
32+
33+
num = read_u16("BootCurrent");
34+
cond_warning(opts.verbose >= 2 && num < 0,
35+
"Could not read variable 'BootCurrent'");
36+
if (num >= 0) {
37+
snprintf(s, sizeof(s), "%04X", num);
38+
value = json_string(s);
39+
json_object_set_new(root, "BootCurrent", value);
40+
}
41+
}
42+
43+
static void
44+
json_fill_timeout(json_t *root)
45+
{
46+
json_t *value;
47+
int num;
48+
49+
num = read_u16("Timeout");
50+
cond_warning(opts.verbose >= 2 && num < 0,
51+
"Could not read variable 'Timeout'");
52+
if (num >= 0) {
53+
value = json_integer(num);
54+
json_object_set_new(root, "Timeout", value);
55+
}
56+
}
57+
58+
static json_t *
59+
bootorder_json_array(uint16_t *order, int length)
60+
{
61+
json_t *value, *array;
62+
char s[5] = {};
63+
int i;
64+
65+
array = json_array();
66+
if (!array)
67+
return NULL;
68+
69+
for (i = 0; i < length; i++) {
70+
snprintf(s, sizeof(s), "%04X", order[i]);
71+
value = json_string(s);
72+
json_array_append_new(array, value);
73+
}
74+
75+
return array;
76+
}
77+
78+
static void
79+
json_fill_order(json_t *root, const char *name)
80+
{
81+
var_entry_t *order = NULL;
82+
uint16_t *data;
83+
json_t *array;
84+
int rc;
85+
86+
rc = read_order(name, &order);
87+
cond_warning(opts.verbose >= 2 && rc < 0,
88+
"Could not read variable '%s'", name);
89+
90+
if (rc < 0) {
91+
if (errno == ENOENT) {
92+
if (!strcmp(name, "BootOrder"))
93+
printf("No BootOrder is set; firmware will attempt recovery\n");
94+
else
95+
printf("No %s is set\n", name);
96+
} else
97+
perror("json_fill_order()");
98+
return;
99+
}
100+
101+
data = (uint16_t *)order->data;
102+
if (order->data_size) {
103+
array = bootorder_json_array(data,
104+
order->data_size / sizeof(uint16_t));
105+
if (array != NULL)
106+
json_object_set_new(root, name, array);
107+
free(order->data);
108+
}
109+
free(order);
110+
}
111+
112+
static void
113+
json_fill_vars(json_t *root, const char *prefix, list_t *entry_list)
114+
{
115+
const unsigned char *description;
116+
json_t *boot_json, *vars_json;
117+
efi_load_option *load_option;
118+
char name[16] = {'\0'};
119+
var_entry_t *boot;
120+
list_t *pos;
121+
int active;
122+
123+
vars_json = json_array();
124+
if (!vars_json)
125+
return;
126+
127+
list_for_each(pos, entry_list) {
128+
boot_json = json_object();
129+
boot = list_entry(pos, var_entry_t, list);
130+
load_option = (efi_load_option *)boot->data;
131+
description = efi_loadopt_desc(load_option, boot->data_size);
132+
if (boot->name)
133+
json_object_set_new(boot_json, "name", json_string(boot->name));
134+
else {
135+
snprintf(name, sizeof(name), "%s%04X", prefix, boot->num);
136+
json_object_set_new(boot_json, "name", json_string(boot->name));
137+
}
138+
139+
active = efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE ? 1 : 0;
140+
json_object_set_new(boot_json, "active", json_boolean(active));
141+
json_object_set_new(boot_json, "description",
142+
json_string((char *)description));
143+
json_array_append_new(vars_json, boot_json);
144+
}
145+
146+
json_object_set_new(root, "vars", vars_json);
147+
}
148+
149+
void
150+
__print_json(list_t *entry_list, ebm_mode mode, char **prefices, char **order_name)
151+
{
152+
json_t *root = json_object();
153+
char *json_str = NULL;
154+
155+
switch (mode) {
156+
case boot:
157+
json_fill_bootnext(root);
158+
json_fill_bootcurrent(root);
159+
json_fill_timeout(root);
160+
json_fill_order(root, order_name[mode]);
161+
json_fill_vars(root, prefices[mode], entry_list);
162+
break;
163+
case driver:
164+
case sysprep:
165+
json_fill_order(root, order_name[mode]);
166+
json_fill_vars(root, prefices[mode], entry_list);
167+
break;
168+
}
169+
json_str = json_dumps(root, JSON_COMPACT);
170+
printf("%s\n", json_str);
171+
free(json_str);
172+
json_decref(root);
173+
}

0 commit comments

Comments
 (0)