Skip to content

Commit b535b07

Browse files
pvxeAlejandro Sirgo Rica
andcommitted
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 the following to stderr: "JSON support is not built-in" This feature adds a new optional dependency with libjansson. Signed-off-by: Jose M. Guisado <jguisado@soleta.eu> Co-authored-by: Alejandro Sirgo Rica <asirgo@soleta.eu>
1 parent e0b2ea6 commit b535b07

6 files changed

Lines changed: 259 additions & 2 deletions

File tree

Make.defaults

Lines changed: 3 additions & 0 deletions
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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,21 @@ TARGETS=$(BINTARGETS) efibootmgr.8 efibootdump.8
1111
all : deps $(TARGETS)
1212

1313
EFIBOOTMGR_SOURCES = efibootmgr.c efi.c parse_loader_data.c
14+
ifdef JSON
15+
EFIBOOTMGR_SOURCES += json.c
16+
endif
1417
EFICONMAN_SOURCES = eficonman.c
1518
EFIBOOTDUMP_SOURCES = efibootdump.c parse_loader_data.c
1619
EFIBOOTNEXT_SOURCES = efibootnext.c
1720
ALL_SOURCES=$(EFIBOOTMGR_SOURCES)
1821
-include $(call deps-of,$(ALL_SOURCES))
1922

23+
2024
efibootmgr : $(call objects-of,$(EFIBOOTMGR_SOURCES))
2125
efibootmgr : PKGS=efivar efiboot
26+
ifdef JSON
27+
efibootmgr : PKGS+=jansson
28+
endif
2229

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

src/efibootmgr.c

Lines changed: 15 additions & 2 deletions
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!)"
@@ -1456,6 +1457,9 @@ usage()
14561457
printf("\t-g | --gpt Force disk with invalid PMBR to be treated as GPT.\n");
14571458
printf("\t-i | --iface name Create a netboot entry for the named interface.\n");
14581459
printf("\t-I | --index number When creating an entry, insert it in bootorder at specified position (default: 0).\n");
1460+
#ifdef JSON
1461+
printf("\t-j | --json Enable JSON output\n");
1462+
#endif
14591463
printf("\t-l | --loader name (Defaults to \""DEFAULT_LOADER"\").\n");
14601464
printf("\t-L | --label label Boot manager display label (defaults to \"Linux\").\n");
14611465
printf("\t-m | --mirror-below-4G t|f Mirror memory below 4GB.\n");
@@ -1526,6 +1530,7 @@ parse_opts(int argc, char **argv)
15261530
{"gpt", no_argument, 0, 'g'},
15271531
{"iface", required_argument, 0, 'i'},
15281532
{"index", required_argument, 0, 'I'},
1533+
{"json", no_argument, 0, 'j'},
15291534
{"keep", no_argument, 0, 'k'},
15301535
{"loader", required_argument, 0, 'l'},
15311536
{"label", required_argument, 0, 'L'},
@@ -1552,7 +1557,7 @@ parse_opts(int argc, char **argv)
15521557
};
15531558

15541559
c = getopt_long(argc, argv,
1555-
"aAb:BcCd:De:E:fFgi:I:kl:L:m:M:n:No:Op:qrst:Tuv::Vwy@:h",
1560+
"aAb:BcCd:De:E:fFgi:I:jkl:L:m:M:n:No:Op:qrst:Tuv::Vwy@:h",
15561561
long_options, &option_index);
15571562
if (c == -1)
15581563
break;
@@ -1666,6 +1671,9 @@ parse_opts(int argc, char **argv)
16661671
}
16671672
opts.index = (uint16_t)lindex;
16681673
break;
1674+
case 'j':
1675+
opts.json = 1;
1676+
break;
16691677
case 'k':
16701678
opts.keep_old_entries = 1;
16711679
break;
@@ -2006,7 +2014,7 @@ main(int argc, char **argv)
20062014
ret=set_mirror(opts.below4g, opts.above4g);
20072015
}
20082016

2009-
if (!opts.quiet && ret == 0) {
2017+
if (!opts.quiet && !opts.json && ret == 0) {
20102018
switch (mode) {
20112019
case boot:
20122020
num = read_u16("BootNext");
@@ -2035,6 +2043,11 @@ main(int argc, char **argv)
20352043
break;
20362044
}
20372045
}
2046+
2047+
if (!opts.quiet && opts.json && ret == 0) {
2048+
print_json(&entry_list, mode, prefices, order_name);
2049+
}
2050+
20382051
free_vars(&entry_list);
20392052
free_array(names);
20402053
if (ret)

src/efibootmgr.h

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

src/json.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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_path(json_t *root, efi_load_option *load_option,
114+
size_t boot_data_size)
115+
{
116+
size_t text_path_len = 0;
117+
char *text_path = NULL;
118+
uint16_t pathlen;
119+
efidp dp = NULL;
120+
ssize_t rc;
121+
122+
pathlen = efi_loadopt_pathlen(load_option, boot_data_size);
123+
dp = efi_loadopt_path(load_option, boot_data_size);
124+
125+
rc = efidp_format_device_path(NULL, 0, dp, pathlen);
126+
if (rc < 0)
127+
return;
128+
129+
text_path_len = rc + 1;
130+
131+
text_path = calloc(1, text_path_len);
132+
if (!text_path)
133+
return;
134+
135+
rc = efidp_format_device_path((unsigned char *)text_path,
136+
text_path_len,
137+
dp,
138+
pathlen);
139+
if (rc < 0) {
140+
free(text_path);
141+
return;
142+
}
143+
144+
json_object_set_new(root, "device_path", json_string(text_path));
145+
146+
free(text_path);
147+
}
148+
149+
static void
150+
json_fill_vars(json_t *root, const char *prefix, list_t *entry_list)
151+
{
152+
const unsigned char *description;
153+
json_t *boot_json, *vars_json;
154+
efi_load_option *load_option;
155+
char name[16] = {'\0'};
156+
var_entry_t *boot;
157+
list_t *pos;
158+
int active;
159+
160+
vars_json = json_array();
161+
if (!vars_json)
162+
return;
163+
164+
list_for_each(pos, entry_list) {
165+
boot_json = json_object();
166+
boot = list_entry(pos, var_entry_t, list);
167+
load_option = (efi_load_option *)boot->data;
168+
description = efi_loadopt_desc(load_option, boot->data_size);
169+
if (boot->name)
170+
json_object_set_new(boot_json, "name", json_string(boot->name));
171+
else {
172+
snprintf(name, sizeof(name), "%s%04X", prefix, boot->num);
173+
json_object_set_new(boot_json, "name", json_string(name));
174+
}
175+
176+
active = efi_loadopt_attrs(load_option) & LOAD_OPTION_ACTIVE ? 1 : 0;
177+
json_object_set_new(boot_json, "active", json_boolean(active));
178+
json_object_set_new(boot_json, "description",
179+
json_string((char *)description));
180+
json_fill_path(boot_json, load_option, boot->data_size);
181+
json_array_append_new(vars_json, boot_json);
182+
}
183+
184+
json_object_set_new(root, "vars", vars_json);
185+
}
186+
187+
void
188+
__print_json(list_t *entry_list, ebm_mode mode, char **prefices, char **order_name)
189+
{
190+
json_t *root = json_object();
191+
char *json_str = NULL;
192+
193+
switch (mode) {
194+
case boot:
195+
json_fill_bootnext(root);
196+
json_fill_bootcurrent(root);
197+
json_fill_timeout(root);
198+
json_fill_order(root, order_name[mode]);
199+
json_fill_vars(root, prefices[mode], entry_list);
200+
break;
201+
case driver:
202+
case sysprep:
203+
json_fill_order(root, order_name[mode]);
204+
json_fill_vars(root, prefices[mode], entry_list);
205+
break;
206+
}
207+
json_str = json_dumps(root, JSON_COMPACT);
208+
printf("%s\n", json_str);
209+
free(json_str);
210+
json_decref(root);
211+
}

src/json.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
fprintf(stderr, "JSON support is not built-in\n");
14+
exit(1);
15+
}
16+
#else
17+
static inline void print_json(list_t *entry_list, ebm_mode mode,
18+
char **prefices, char **order_name)
19+
{
20+
__print_json(entry_list, mode, prefices, order_name);
21+
}
22+
#endif

0 commit comments

Comments
 (0)