Commit a5dd4982 authored by David S. Miller's avatar David S. Miller

Merge branch 'bpftool-JSON'

Jakub Kicinski says:

====================
tools: bpftool: Add JSON output to bpftool

Quentin says:

This series introduces support for JSON output to all bpftool commands. It
adds option parsing, and several options are created:

  * -j, --json     Switch to JSON output.
  * -p, --pretty   Switch to JSON and print it in a human-friendly fashion.
  * -h, --help     Print generic help message.
  * -V, --version  Print version number.

This code uses a "json_writer", which is a copy of the one written by
Stephen Hemminger in iproute2.
---
I don't know if there is an easy way to share the code for json_write
without copying the file, so I am very open to suggestions on this matter.
====================
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4c4fde21 0641c3c8
...@@ -10,26 +10,29 @@ tool for inspection and simple manipulation of eBPF maps ...@@ -10,26 +10,29 @@ tool for inspection and simple manipulation of eBPF maps
SYNOPSIS SYNOPSIS
======== ========
**bpftool** **map** *COMMAND* **bpftool** [*OPTIONS*] **map** *COMMAND*
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
*COMMANDS* := *COMMANDS* :=
{ show | dump | update | lookup | getnext | delete | pin | help } { **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
| **pin** | **help** }
MAP COMMANDS MAP COMMANDS
============= =============
| **bpftool** map show [*MAP*] | **bpftool** **map show** [*MAP*]
| **bpftool** map dump *MAP* | **bpftool** **map dump** *MAP*
| **bpftool** map update *MAP* key *BYTES* value *VALUE* [*UPDATE_FLAGS*] | **bpftool** **map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
| **bpftool** map lookup *MAP* key *BYTES* | **bpftool** **map lookup** *MAP* **key** *BYTES*
| **bpftool** map getnext *MAP* [key *BYTES*] | **bpftool** **map getnext** *MAP* [**key** *BYTES*]
| **bpftool** map delete *MAP* key *BYTES* | **bpftool** **map delete** *MAP* **key** *BYTES*
| **bpftool** map pin *MAP* *FILE* | **bpftool** **map pin** *MAP* *FILE*
| **bpftool** map help | **bpftool** **map help**
| |
| *MAP* := { id MAP_ID | pinned FILE } | *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *VALUE* := { BYTES | MAP | PROGRAM } | *VALUE* := { *BYTES* | *MAP* | *PROGRAM* }
| *UPDATE_FLAGS* := { any | exist | noexist } | *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
DESCRIPTION DESCRIPTION
=========== ===========
...@@ -68,6 +71,21 @@ DESCRIPTION ...@@ -68,6 +71,21 @@ DESCRIPTION
**bpftool map help** **bpftool map help**
Print short help message. Print short help message.
OPTIONS
=======
-h, --help
Print short generic help message (similar to **bpftool help**).
-v, --version
Print version number (similar to **bpftool version**).
-j, --json
Generate JSON output. For commands that cannot produce JSON, this
option has no effect.
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
EXAMPLES EXAMPLES
======== ========
**# bpftool map show** **# bpftool map show**
......
...@@ -10,13 +10,23 @@ tool for inspection and simple manipulation of eBPF progs ...@@ -10,13 +10,23 @@ tool for inspection and simple manipulation of eBPF progs
SYNOPSIS SYNOPSIS
======== ========
| **bpftool** prog show [*PROG*] **bpftool** [*OPTIONS*] **prog** *COMMAND*
| **bpftool** prog dump xlated *PROG* [{file *FILE* | opcodes }]
| **bpftool** prog dump jited *PROG* [{file *FILE* | opcodes }] *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
| **bpftool** prog pin *PROG* *FILE*
| **bpftool** prog help *COMMANDS* :=
{ **show** | **dump xlated** | **dump jited** | **pin** | **help** }
MAP COMMANDS
=============
| **bpftool** **prog show** [*PROG*]
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}]
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
| **bpftool** **prog pin** *PROG* *FILE*
| **bpftool** **prog help**
| |
| *PROG* := { id *PROG_ID* | pinned *FILE* | tag *PROG_TAG* } | *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
DESCRIPTION DESCRIPTION
=========== ===========
...@@ -50,6 +60,21 @@ DESCRIPTION ...@@ -50,6 +60,21 @@ DESCRIPTION
**bpftool prog help** **bpftool prog help**
Print short help message. Print short help message.
OPTIONS
=======
-h, --help
Print short generic help message (similar to **bpftool help**).
-v, --version
Print version number (similar to **bpftool version**).
-j, --json
Generate JSON output. For commands that cannot produce JSON, this
option has no effect.
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
EXAMPLES EXAMPLES
======== ========
**# bpftool prog show** **# bpftool prog show**
...@@ -59,13 +84,33 @@ EXAMPLES ...@@ -59,13 +84,33 @@ EXAMPLES
loaded_at Sep 29/20:11 uid 0 loaded_at Sep 29/20:11 uid 0
xlated 528B jited 370B memlock 4096B map_ids 10 xlated 528B jited 370B memlock 4096B map_ids 10
**# bpftool --json --pretty prog show**
::
{
"programs": [{
"id": 10,
"type": "xdp",
"tag": "005a3d2123620c8b",
"loaded_at": "Sep 29/20:11",
"uid": 0,
"bytes_xlated": 528,
"jited": true,
"bytes_jited": 370,
"bytes_memlock": 4096,
"map_ids": [10
]
}
]
}
| |
| **# bpftool prog dump xlated id 10 file /tmp/t** | **# bpftool prog dump xlated id 10 file /tmp/t**
| **# ls -l /tmp/t** | **# ls -l /tmp/t**
| -rw------- 1 root root 560 Jul 22 01:42 /tmp/t | -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
| **# bpftool prog dum jited tag 005a3d2123620c8b**
| **# bpftool prog dum jited pinned /sys/fs/bpf/prog**
:: ::
...@@ -75,6 +120,26 @@ EXAMPLES ...@@ -75,6 +120,26 @@ EXAMPLES
sub $0x28,%rbp sub $0x28,%rbp
mov %rbx,0x0(%rbp) mov %rbx,0x0(%rbp)
|
| **# mount -t bpf none /sys/fs/bpf/**
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
| **# ls -l /sys/fs/bpf/**
| -rw------- 1 root root 0 Jul 22 01:43 prog
**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes**
::
push %rbp
55
mov %rsp,%rbp
48 89 e5
sub $0x228,%rsp
48 81 ec 28 02 00 00
sub $0x28,%rbp
48 83 ed 28
mov %rbx,0x0(%rbp)
48 89 5d 00
SEE ALSO SEE ALSO
......
...@@ -10,18 +10,23 @@ tool for inspection and simple manipulation of eBPF programs and maps ...@@ -10,18 +10,23 @@ tool for inspection and simple manipulation of eBPF programs and maps
SYNOPSIS SYNOPSIS
======== ========
**bpftool** *OBJECT* { *COMMAND* | help } **bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** }
**bpftool** batch file *FILE* **bpftool** **batch file** *FILE*
**bpftool** version **bpftool** **version**
*OBJECT* := { **map** | **program** } *OBJECT* := { **map** | **program** }
*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
*MAP-COMMANDS* := *MAP-COMMANDS* :=
{ show | dump | update | lookup | getnext | delete | pin | help } { **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
| **pin** | **help** }
*PROG-COMMANDS* := { show | dump jited | dump xlated | pin | help } *PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin**
| **help** }
DESCRIPTION DESCRIPTION
=========== ===========
...@@ -31,6 +36,21 @@ DESCRIPTION ...@@ -31,6 +36,21 @@ DESCRIPTION
Note that format of the output of all tools is not guaranteed to be Note that format of the output of all tools is not guaranteed to be
stable and should not be depended upon. stable and should not be depended upon.
OPTIONS
=======
-h, --help
Print short help message (similar to **bpftool help**).
-v, --version
Print version number (similar to **bpftool version**).
-j, --json
Generate JSON output. For commands that cannot produce JSON, this
option has no effect.
-p, --pretty
Generate human-readable JSON output. Implies **-j**.
SEE ALSO SEE ALSO
======== ========
**bpftool-map**\ (8), **bpftool-prog**\ (8) **bpftool-map**\ (8), **bpftool-prog**\ (8)
...@@ -66,8 +66,8 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) ...@@ -66,8 +66,8 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
fd = bpf_obj_get(path); fd = bpf_obj_get(path);
if (fd < 0) { if (fd < 0) {
err("bpf obj get (%s): %s\n", path, p_err("bpf obj get (%s): %s", path,
errno == EACCES && !is_bpffs(dirname(path)) ? errno == EACCES && !is_bpffs(dirname(path)) ?
"directory not in bpf file system (bpffs)" : "directory not in bpf file system (bpffs)" :
strerror(errno)); strerror(errno));
return -1; return -1;
...@@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) ...@@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
return type; return type;
} }
if (type != exp_type) { if (type != exp_type) {
err("incorrect object type: %s\n", get_fd_type_name(type)); p_err("incorrect object type: %s", get_fd_type_name(type));
close(fd); close(fd);
return -1; return -1;
} }
...@@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) ...@@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
int fd; int fd;
if (!is_prefix(*argv, "id")) { if (!is_prefix(*argv, "id")) {
err("expected 'id' got %s\n", *argv); p_err("expected 'id' got %s", *argv);
return -1; return -1;
} }
NEXT_ARG(); NEXT_ARG();
id = strtoul(*argv, &endptr, 0); id = strtoul(*argv, &endptr, 0);
if (*endptr) { if (*endptr) {
err("can't parse %s as ID\n", *argv); p_err("can't parse %s as ID", *argv);
return -1; return -1;
} }
NEXT_ARG(); NEXT_ARG();
...@@ -112,15 +112,15 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)) ...@@ -112,15 +112,15 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
fd = get_fd_by_id(id); fd = get_fd_by_id(id);
if (fd < 0) { if (fd < 0) {
err("can't get prog by id (%u): %s\n", id, strerror(errno)); p_err("can't get prog by id (%u): %s", id, strerror(errno));
return -1; return -1;
} }
err = bpf_obj_pin(fd, *argv); err = bpf_obj_pin(fd, *argv);
close(fd); close(fd);
if (err) { if (err) {
err("can't pin the object (%s): %s\n", *argv, p_err("can't pin the object (%s): %s", *argv,
errno == EACCES && !is_bpffs(dirname(*argv)) ? errno == EACCES && !is_bpffs(dirname(*argv)) ?
"directory not in bpf file system (bpffs)" : "directory not in bpf file system (bpffs)" :
strerror(errno)); strerror(errno));
return -1; return -1;
...@@ -153,11 +153,11 @@ int get_fd_type(int fd) ...@@ -153,11 +153,11 @@ int get_fd_type(int fd)
n = readlink(path, buf, sizeof(buf)); n = readlink(path, buf, sizeof(buf));
if (n < 0) { if (n < 0) {
err("can't read link type: %s\n", strerror(errno)); p_err("can't read link type: %s", strerror(errno));
return -1; return -1;
} }
if (n == sizeof(path)) { if (n == sizeof(path)) {
err("can't read link type: path too long!\n"); p_err("can't read link type: path too long!");
return -1; return -1;
} }
...@@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key) ...@@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key)
fdi = fopen(path, "r"); fdi = fopen(path, "r");
if (!fdi) { if (!fdi) {
err("can't open fdinfo: %s\n", strerror(errno)); p_err("can't open fdinfo: %s", strerror(errno));
return NULL; return NULL;
} }
...@@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key) ...@@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key)
value = strchr(line, '\t'); value = strchr(line, '\t');
if (!value || !value[1]) { if (!value || !value[1]) {
err("malformed fdinfo!?\n"); p_err("malformed fdinfo!?");
free(line); free(line);
return NULL; return NULL;
} }
...@@ -209,8 +209,18 @@ char *get_fdinfo(int fd, const char *key) ...@@ -209,8 +209,18 @@ char *get_fdinfo(int fd, const char *key)
return line; return line;
} }
err("key '%s' not found in fdinfo\n", key); p_err("key '%s' not found in fdinfo", key);
free(line); free(line);
fclose(fdi); fclose(fdi);
return NULL; return NULL;
} }
void print_hex_data_json(uint8_t *data, size_t len)
{
unsigned int i;
jsonw_start_array(json_wtr);
for (i = 0; i < len; i++)
jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
jsonw_end_array(json_wtr);
}
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* Licensed under the GNU General Public License, version 2.0 (GPLv2) * Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/ */
#include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -21,6 +22,9 @@ ...@@ -21,6 +22,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "json_writer.h"
#include "main.h"
static void get_exec_path(char *tpath, size_t size) static void get_exec_path(char *tpath, size_t size)
{ {
ssize_t len; ssize_t len;
...@@ -39,6 +43,38 @@ static void get_exec_path(char *tpath, size_t size) ...@@ -39,6 +43,38 @@ static void get_exec_path(char *tpath, size_t size)
free(path); free(path);
} }
static int oper_count;
static int fprintf_json(void *out, const char *fmt, ...)
{
va_list ap;
char *s;
va_start(ap, fmt);
if (!oper_count) {
int i;
s = va_arg(ap, char *);
/* Strip trailing spaces */
i = strlen(s) - 1;
while (s[i] == ' ')
s[i--] = '\0';
jsonw_string_field(json_wtr, "operation", s);
jsonw_name(json_wtr, "operands");
jsonw_start_array(json_wtr);
oper_count++;
} else if (!strcmp(fmt, ",")) {
/* Skip */
} else {
s = va_arg(ap, char *);
jsonw_string(json_wtr, s);
oper_count++;
}
va_end(ap);
return 0;
}
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes) void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
{ {
disassembler_ftype disassemble; disassembler_ftype disassemble;
...@@ -57,7 +93,12 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes) ...@@ -57,7 +93,12 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
assert(bfdf); assert(bfdf);
assert(bfd_check_format(bfdf, bfd_object)); assert(bfd_check_format(bfdf, bfd_object));
init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf); if (json_output)
init_disassemble_info(&info, stdout,
(fprintf_ftype) fprintf_json);
else
init_disassemble_info(&info, stdout,
(fprintf_ftype) fprintf);
info.arch = bfd_get_arch(bfdf); info.arch = bfd_get_arch(bfdf);
info.mach = bfd_get_mach(bfdf); info.mach = bfd_get_mach(bfdf);
info.buffer = image; info.buffer = image;
...@@ -68,20 +109,53 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes) ...@@ -68,20 +109,53 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
disassemble = disassembler(bfdf); disassemble = disassembler(bfdf);
assert(disassemble); assert(disassemble);
if (json_output)
jsonw_start_array(json_wtr);
do { do {
printf("%4x:\t", pc); if (json_output) {
jsonw_start_object(json_wtr);
oper_count = 0;
jsonw_name(json_wtr, "pc");
jsonw_printf(json_wtr, "\"0x%x\"", pc);
} else {
printf("%4x:\t", pc);
}
count = disassemble(pc, &info); count = disassemble(pc, &info);
if (json_output) {
/* Operand array, was started in fprintf_json. Before
* that, make sure we have a _null_ value if no operand
* other than operation code was present.
*/
if (oper_count == 1)
jsonw_null(json_wtr);
jsonw_end_array(json_wtr);
}
if (opcodes) { if (opcodes) {
printf("\n\t"); if (json_output) {
for (i = 0; i < count; ++i) jsonw_name(json_wtr, "opcodes");
printf("%02x ", (uint8_t) image[pc + i]); jsonw_start_array(json_wtr);
for (i = 0; i < count; ++i)
jsonw_printf(json_wtr, "\"0x%02hhx\"",
(uint8_t)image[pc + i]);
jsonw_end_array(json_wtr);
} else {
printf("\n\t");
for (i = 0; i < count; ++i)
printf("%02x ",
(uint8_t)image[pc + i]);
}
} }
printf("\n"); if (json_output)
jsonw_end_object(json_wtr);
else
printf("\n");
pc += count; pc += count;
} while (count > 0 && pc < len); } while (count > 0 && pc < len);
if (json_output)
jsonw_end_array(json_wtr);
bfd_close(bfdf); bfd_close(bfdf);
} }
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Stephen Hemminger <stephen@networkplumber.org>
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <assert.h>
#include <malloc.h>
#include <inttypes.h>
#include <stdint.h>
#include "json_writer.h"
struct json_writer {
FILE *out; /* output file */
unsigned depth; /* nesting */
bool pretty; /* optional whitepace */
char sep; /* either nul or comma */
};
/* indentation for pretty print */
static void jsonw_indent(json_writer_t *self)
{
unsigned i;
for (i = 0; i < self->depth; ++i)
fputs(" ", self->out);
}
/* end current line and indent if pretty printing */
static void jsonw_eol(json_writer_t *self)
{
if (!self->pretty)
return;
putc('\n', self->out);
jsonw_indent(self);
}
/* If current object is not empty print a comma */
static void jsonw_eor(json_writer_t *self)
{
if (self->sep != '\0')
putc(self->sep, self->out);
self->sep = ',';
}
/* Output JSON encoded string */
/* Handles C escapes, does not do Unicode */
static void jsonw_puts(json_writer_t *self, const char *str)
{
putc('"', self->out);
for (; *str; ++str)
switch (*str) {
case '\t':
fputs("\\t", self->out);
break;
case '\n':
fputs("\\n", self->out);
break;
case '\r':
fputs("\\r", self->out);
break;
case '\f':
fputs("\\f", self->out);
break;
case '\b':
fputs("\\b", self->out);
break;
case '\\':
fputs("\\n", self->out);
break;
case '"':
fputs("\\\"", self->out);
break;
case '\'':
fputs("\\\'", self->out);
break;
default:
putc(*str, self->out);
}
putc('"', self->out);
}
/* Create a new JSON stream */
json_writer_t *jsonw_new(FILE *f)
{
json_writer_t *self = malloc(sizeof(*self));
if (self) {
self->out = f;
self->depth = 0;
self->pretty = false;
self->sep = '\0';
}
return self;
}
/* End output to JSON stream */
void jsonw_destroy(json_writer_t **self_p)
{
json_writer_t *self = *self_p;
assert(self->depth == 0);
fputs("\n", self->out);
fflush(self->out);
free(self);
*self_p = NULL;
}
void jsonw_pretty(json_writer_t *self, bool on)
{
self->pretty = on;
}
/* Basic blocks */
static void jsonw_begin(json_writer_t *self, int c)
{
jsonw_eor(self);
putc(c, self->out);
++self->depth;
self->sep = '\0';
}
static void jsonw_end(json_writer_t *self, int c)
{
assert(self->depth > 0);
--self->depth;
if (self->sep != '\0')
jsonw_eol(self);
putc(c, self->out);
self->sep = ',';
}
/* Add a JSON property name */
void jsonw_name(json_writer_t *self, const char *name)
{
jsonw_eor(self);
jsonw_eol(self);
self->sep = '\0';
jsonw_puts(self, name);
putc(':', self->out);
if (self->pretty)
putc(' ', self->out);
}
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
{
jsonw_eor(self);
putc('"', self->out);
vfprintf(self->out, fmt, ap);
putc('"', self->out);
}
void jsonw_printf(json_writer_t *self, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
jsonw_eor(self);
vfprintf(self->out, fmt, ap);
va_end(ap);
}
/* Collections */
void jsonw_start_object(json_writer_t *self)
{
jsonw_begin(self, '{');
}
void jsonw_end_object(json_writer_t *self)
{
jsonw_end(self, '}');
}
void jsonw_start_array(json_writer_t *self)
{
jsonw_begin(self, '[');
}
void jsonw_end_array(json_writer_t *self)
{
jsonw_end(self, ']');
}
/* JSON value types */
void jsonw_string(json_writer_t *self, const char *value)
{
jsonw_eor(self);
jsonw_puts(self, value);
}
void jsonw_bool(json_writer_t *self, bool val)
{
jsonw_printf(self, "%s", val ? "true" : "false");
}
void jsonw_null(json_writer_t *self)
{
jsonw_printf(self, "null");
}
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
{
jsonw_printf(self, fmt, num);
}
#ifdef notused
void jsonw_float(json_writer_t *self, double num)
{
jsonw_printf(self, "%g", num);
}
#endif
void jsonw_hu(json_writer_t *self, unsigned short num)
{
jsonw_printf(self, "%hu", num);
}
void jsonw_uint(json_writer_t *self, uint64_t num)
{
jsonw_printf(self, "%"PRIu64, num);
}
void jsonw_lluint(json_writer_t *self, unsigned long long int num)
{
jsonw_printf(self, "%llu", num);
}
void jsonw_int(json_writer_t *self, int64_t num)
{
jsonw_printf(self, "%"PRId64, num);
}
/* Basic name/value objects */
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
{
jsonw_name(self, prop);
jsonw_string(self, val);
}
void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
{
jsonw_name(self, prop);
jsonw_bool(self, val);
}
#ifdef notused
void jsonw_float_field(json_writer_t *self, const char *prop, double val)
{
jsonw_name(self, prop);
jsonw_float(self, val);
}
#endif
void jsonw_float_field_fmt(json_writer_t *self,
const char *prop,
const char *fmt,
double val)
{
jsonw_name(self, prop);
jsonw_float_fmt(self, fmt, val);
}
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
{
jsonw_name(self, prop);
jsonw_uint(self, num);
}
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
{
jsonw_name(self, prop);
jsonw_hu(self, num);
}
void jsonw_lluint_field(json_writer_t *self,
const char *prop,
unsigned long long int num)
{
jsonw_name(self, prop);
jsonw_lluint(self, num);
}
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
{
jsonw_name(self, prop);
jsonw_int(self, num);
}
void jsonw_null_field(json_writer_t *self, const char *prop)
{
jsonw_name(self, prop);
jsonw_null(self);
}
#ifdef TEST
int main(int argc, char **argv)
{
json_writer_t *wr = jsonw_new(stdout);
jsonw_start_object(wr);
jsonw_pretty(wr, true);
jsonw_name(wr, "Vyatta");
jsonw_start_object(wr);
jsonw_string_field(wr, "url", "http://vyatta.com");
jsonw_uint_field(wr, "downloads", 2000000ul);
jsonw_float_field(wr, "stock", 8.16);
jsonw_name(wr, "ARGV");
jsonw_start_array(wr);
while (--argc)
jsonw_string(wr, *++argv);
jsonw_end_array(wr);
jsonw_name(wr, "empty");
jsonw_start_array(wr);
jsonw_end_array(wr);
jsonw_name(wr, "NIL");
jsonw_start_object(wr);
jsonw_end_object(wr);
jsonw_null_field(wr, "my_null");
jsonw_name(wr, "special chars");
jsonw_start_array(wr);
jsonw_string_field(wr, "slash", "/");
jsonw_string_field(wr, "newline", "\n");
jsonw_string_field(wr, "tab", "\t");
jsonw_string_field(wr, "ff", "\f");
jsonw_string_field(wr, "quote", "\"");
jsonw_string_field(wr, "tick", "\'");
jsonw_string_field(wr, "backslash", "\\");
jsonw_end_array(wr);
jsonw_end_object(wr);
jsonw_end_object(wr);
jsonw_destroy(&wr);
return 0;
}
#endif
/*
* Simple streaming JSON writer
*
* This takes care of the annoying bits of JSON syntax like the commas
* after elements
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Stephen Hemminger <stephen@networkplumber.org>
*/
#ifndef _JSON_WRITER_H_
#define _JSON_WRITER_H_
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
/* Opaque class structure */
typedef struct json_writer json_writer_t;
/* Create a new JSON stream */
json_writer_t *jsonw_new(FILE *f);
/* End output to JSON stream */
void jsonw_destroy(json_writer_t **self_p);
/* Cause output to have pretty whitespace */
void jsonw_pretty(json_writer_t *self, bool on);
/* Add property name */
void jsonw_name(json_writer_t *self, const char *name);
/* Add value */
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap);
void jsonw_printf(json_writer_t *self, const char *fmt, ...);
void jsonw_string(json_writer_t *self, const char *value);
void jsonw_bool(json_writer_t *self, bool value);
void jsonw_float(json_writer_t *self, double number);
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
void jsonw_uint(json_writer_t *self, uint64_t number);
void jsonw_hu(json_writer_t *self, unsigned short number);
void jsonw_int(json_writer_t *self, int64_t number);
void jsonw_null(json_writer_t *self);
void jsonw_lluint(json_writer_t *self, unsigned long long int num);
/* Useful Combinations of name and value */
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
void jsonw_float_field(json_writer_t *self, const char *prop, double num);
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
void jsonw_null_field(json_writer_t *self, const char *prop);
void jsonw_lluint_field(json_writer_t *self, const char *prop,
unsigned long long int num);
void jsonw_float_field_fmt(json_writer_t *self, const char *prop,
const char *fmt, double val);
/* Collections */
void jsonw_start_object(json_writer_t *self);
void jsonw_end_object(json_writer_t *self);
void jsonw_start_array(json_writer_t *self);
void jsonw_end_array(json_writer_t *self);
/* Override default exception handling */
typedef void (jsonw_err_handler_fn)(const char *);
#endif /* _JSON_WRITER_H_ */
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <bfd.h> #include <bfd.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <getopt.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/version.h> #include <linux/version.h>
#include <stdio.h> #include <stdio.h>
...@@ -50,6 +51,9 @@ const char *bin_name; ...@@ -50,6 +51,9 @@ const char *bin_name;
static int last_argc; static int last_argc;
static char **last_argv; static char **last_argv;
static int (*last_do_help)(int argc, char **argv); static int (*last_do_help)(int argc, char **argv);
json_writer_t *json_wtr;
bool pretty_output;
bool json_output;
void usage(void) void usage(void)
{ {
...@@ -60,12 +64,19 @@ void usage(void) ...@@ -60,12 +64,19 @@ void usage(void)
static int do_help(int argc, char **argv) static int do_help(int argc, char **argv)
{ {
if (json_output) {
jsonw_null(json_wtr);
return 0;
}
fprintf(stderr, fprintf(stderr,
"Usage: %s OBJECT { COMMAND | help }\n" "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
" %s batch file FILE\n" " %s batch file FILE\n"
" %s version\n" " %s version\n"
"\n" "\n"
" OBJECT := { prog | map }\n", " OBJECT := { prog | map }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, bin_name, bin_name); bin_name, bin_name, bin_name);
return 0; return 0;
...@@ -73,10 +84,22 @@ static int do_help(int argc, char **argv) ...@@ -73,10 +84,22 @@ static int do_help(int argc, char **argv)
static int do_version(int argc, char **argv) static int do_version(int argc, char **argv)
{ {
printf("%s v%d.%d.%d\n", bin_name, unsigned int version[3];
LINUX_VERSION_CODE >> 16,
LINUX_VERSION_CODE >> 8 & 0xf, version[0] = LINUX_VERSION_CODE >> 16;
LINUX_VERSION_CODE & 0xf); version[1] = LINUX_VERSION_CODE >> 8 & 0xf;
version[2] = LINUX_VERSION_CODE & 0xf;
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "version");
jsonw_printf(json_wtr, "\"%u.%u.%u\"",
version[0], version[1], version[2]);
jsonw_end_object(json_wtr);
} else {
printf("%s v%u.%u.%u\n", bin_name,
version[0], version[1], version[2]);
}
return 0; return 0;
} }
...@@ -151,25 +174,28 @@ static int do_batch(int argc, char **argv) ...@@ -151,25 +174,28 @@ static int do_batch(int argc, char **argv)
int n_argc; int n_argc;
FILE *fp; FILE *fp;
int err; int err;
int i;
if (argc < 2) { if (argc < 2) {
err("too few parameters for batch\n"); p_err("too few parameters for batch");
return -1; return -1;
} else if (!is_prefix(*argv, "file")) { } else if (!is_prefix(*argv, "file")) {
err("expected 'file', got: %s\n", *argv); p_err("expected 'file', got: %s", *argv);
return -1; return -1;
} else if (argc > 2) { } else if (argc > 2) {
err("too many parameters for batch\n"); p_err("too many parameters for batch");
return -1; return -1;
} }
NEXT_ARG(); NEXT_ARG();
fp = fopen(*argv, "r"); fp = fopen(*argv, "r");
if (!fp) { if (!fp) {
err("Can't open file (%s): %s\n", *argv, strerror(errno)); p_err("Can't open file (%s): %s", *argv, strerror(errno));
return -1; return -1;
} }
if (json_output)
jsonw_start_array(json_wtr);
while (fgets(buf, sizeof(buf), fp)) { while (fgets(buf, sizeof(buf), fp)) {
if (strlen(buf) == sizeof(buf) - 1) { if (strlen(buf) == sizeof(buf) - 1) {
errno = E2BIG; errno = E2BIG;
...@@ -182,8 +208,8 @@ static int do_batch(int argc, char **argv) ...@@ -182,8 +208,8 @@ static int do_batch(int argc, char **argv)
while (n_argv[n_argc]) { while (n_argv[n_argc]) {
n_argc++; n_argc++;
if (n_argc == ARRAY_SIZE(n_argv)) { if (n_argc == ARRAY_SIZE(n_argv)) {
err("line %d has too many arguments, skip\n", p_err("line %d has too many arguments, skip",
lines); lines);
n_argc = 0; n_argc = 0;
break; break;
} }
...@@ -193,7 +219,21 @@ static int do_batch(int argc, char **argv) ...@@ -193,7 +219,21 @@ static int do_batch(int argc, char **argv)
if (!n_argc) if (!n_argc)
continue; continue;
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "command");
jsonw_start_array(json_wtr);
for (i = 0; i < n_argc; i++)
jsonw_string(json_wtr, n_argv[i]);
jsonw_end_array(json_wtr);
jsonw_name(json_wtr, "output");
}
err = cmd_select(cmds, n_argc, n_argv, do_help); err = cmd_select(cmds, n_argc, n_argv, do_help);
if (json_output)
jsonw_end_object(json_wtr);
if (err) if (err)
goto err_close; goto err_close;
...@@ -204,21 +244,72 @@ static int do_batch(int argc, char **argv) ...@@ -204,21 +244,72 @@ static int do_batch(int argc, char **argv)
perror("reading batch file failed"); perror("reading batch file failed");
err = -1; err = -1;
} else { } else {
info("processed %d lines\n", lines); p_info("processed %d lines", lines);
err = 0; err = 0;
} }
err_close: err_close:
fclose(fp); fclose(fp);
if (json_output)
jsonw_end_array(json_wtr);
return err; return err;
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
static const struct option options[] = {
{ "json", no_argument, NULL, 'j' },
{ "help", no_argument, NULL, 'h' },
{ "pretty", no_argument, NULL, 'p' },
{ "version", no_argument, NULL, 'V' },
{ 0 }
};
int opt, ret;
last_do_help = do_help;
pretty_output = false;
json_output = false;
bin_name = argv[0]; bin_name = argv[0];
NEXT_ARG();
while ((opt = getopt_long(argc, argv, "Vhpj",
options, NULL)) >= 0) {
switch (opt) {
case 'V':
return do_version(argc, argv);
case 'h':
return do_help(argc, argv);
case 'p':
pretty_output = true;
/* fall through */
case 'j':
json_output = true;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc < 0)
usage();
if (json_output) {
json_wtr = jsonw_new(stdout);
if (!json_wtr) {
p_err("failed to create JSON writer");
return -1;
}
jsonw_pretty(json_wtr, pretty_output);
}
bfd_init(); bfd_init();
return cmd_select(cmds, argc, argv, do_help); ret = cmd_select(cmds, argc, argv, do_help);
if (json_output)
jsonw_destroy(&json_wtr);
return ret;
} }
...@@ -43,20 +43,20 @@ ...@@ -43,20 +43,20 @@
#include <linux/bpf.h> #include <linux/bpf.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#define err(msg...) fprintf(stderr, "Error: " msg) #include "json_writer.h"
#define warn(msg...) fprintf(stderr, "Warning: " msg)
#define info(msg...) fprintf(stderr, msg)
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); }) #define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); }) #define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; }) #define BAD_ARG() ({ p_err("what is '%s'?\n", *argv); -1; })
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
#define HELP_SPEC_PROGRAM \ #define HELP_SPEC_PROGRAM \
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }" "PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
#define HELP_SPEC_OPTIONS \
"OPTIONS := { {-j|--json} [{-p|--pretty}] }"
enum bpf_obj_type { enum bpf_obj_type {
BPF_OBJ_UNKNOWN, BPF_OBJ_UNKNOWN,
...@@ -66,6 +66,9 @@ enum bpf_obj_type { ...@@ -66,6 +66,9 @@ enum bpf_obj_type {
extern const char *bin_name; extern const char *bin_name;
extern json_writer_t *json_wtr;
extern bool json_output;
bool is_prefix(const char *pfx, const char *str); bool is_prefix(const char *pfx, const char *str);
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep); void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
void usage(void) __attribute__((noreturn)); void usage(void) __attribute__((noreturn));
...@@ -90,5 +93,37 @@ int do_map(int argc, char **arg); ...@@ -90,5 +93,37 @@ int do_map(int argc, char **arg);
int prog_parse_fd(int *argc, char ***argv); int prog_parse_fd(int *argc, char ***argv);
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes); void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes);
void print_hex_data_json(uint8_t *data, size_t len);
static inline void p_err(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_name(json_wtr, "error");
jsonw_vprintf_enquote(json_wtr, fmt, ap);
jsonw_end_object(json_wtr);
} else {
fprintf(stderr, "Error: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
va_end(ap);
}
static inline void p_info(const char *fmt, ...)
{
va_list ap;
if (json_output)
return;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
#endif #endif
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment