Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON output support #190

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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
Expand Down
30 changes: 15 additions & 15 deletions src/efibootmgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,18 @@
#include <efiboot.h>
#include <inttypes.h>

#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!)"
#endif

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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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'},
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions src/efibootmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#define EFIBOOTMGR_PATH_ABBREV_NONE 3
#define EFIBOOTMGR_PATH_ABBREV_FILE 4

#include "list.h"

typedef enum {
boot,
driver,
Expand Down Expand Up @@ -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);
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);
}
21 changes: 21 additions & 0 deletions src/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