mirror of
https://github.com/systemd/systemd.git
synced 2025-03-11 20:58:27 +03:00
udevadm-test: allow to dump result in json format
This adds --json=MODE option for 'udevadm test' command. When specified, all messages, except for the final result, will be written to stderr, and the final result is shown in JSON format to stdout. It may be useful for parsing the test result.
This commit is contained in:
parent
0879fa4bc3
commit
ac722389a7
@ -930,6 +930,8 @@
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
|
@ -99,7 +99,7 @@ _udevadm() {
|
||||
[MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
|
||||
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
|
||||
[TEST_STANDALONE]='-v --verbose'
|
||||
[TEST_ARG]='-a --action -N --resolve-names -D --extra-rules-dir'
|
||||
[TEST_ARG]='-a --action -N --resolve-names -D --extra-rules-dir --json'
|
||||
[TEST_BUILTIN]='-a --action'
|
||||
[VERIFY_STANDALONE]='--no-summary --no-style'
|
||||
[VERIFY_ARG]='-N --resolve-names --root'
|
||||
@ -263,6 +263,10 @@ _udevadm() {
|
||||
-D|--extra-rules-dir)
|
||||
comps=''
|
||||
compopt -o dirnames
|
||||
;;
|
||||
--json)
|
||||
comps=$( udevadm test --json help )
|
||||
;;
|
||||
esac
|
||||
elif [[ $cur = -* ]]; then
|
||||
comps="${OPTS[COMMON]} ${OPTS[TEST_ARG]} ${OPTS[TEST_STANDALONE]}"
|
||||
|
@ -92,6 +92,7 @@ _udevadm_test(){
|
||||
'--subsystem=[The subsystem string.]' \
|
||||
'(-D --extra-rules-dir=)'{-D,--extra-rules-dir=}'[Also load rules from the directory.]' \
|
||||
'(-v --verbose)'{-v,--verbose}'[Show verbose logs.]' \
|
||||
'--json=[Generate JSON output]:MODE:(pretty short off)' \
|
||||
'*::devpath:_files -P /sys/ -W /sys'
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "devnum-util.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "json-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "udev-builtin.h"
|
||||
#include "udev-dump.h"
|
||||
#include "udev-event.h"
|
||||
@ -29,7 +31,273 @@ void event_cache_written_sysctl(UdevEvent *event, const char *attr, const char *
|
||||
event_cache_written_value(&event->written_sysctls, attr, value);
|
||||
}
|
||||
|
||||
void dump_event(UdevEvent *event, FILE *f) {
|
||||
static int dump_event_json(UdevEvent *event, sd_json_format_flags_t flags, FILE *f) {
|
||||
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
const char *str;
|
||||
int r;
|
||||
|
||||
if (sd_device_get_devpath(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "path", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_sysname(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "name", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned sysnum;
|
||||
if (device_get_sysnum_unsigned(dev, &sysnum) >= 0) {
|
||||
r = sd_json_variant_set_field_unsigned(&v, "number", sysnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_device_id(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "id", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
const char *subsys = NULL;
|
||||
if (sd_device_get_subsystem(dev, &subsys) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "subsystem", subsys);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_driver_subsystem(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "driverSubsystem", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_devtype(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "type", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_driver(dev, &str) >= 0) {
|
||||
r = sd_json_variant_set_field_string(&v, "driver", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (sd_device_get_devname(dev, &str) >= 0) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *node = NULL;
|
||||
|
||||
r = sd_json_variant_set_field_string(&node, "path", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_set_field_string(&node, "type", streq_ptr(subsys, "block") ? "block" : "char");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dev_t devnum;
|
||||
if (sd_device_get_devnum(dev, &devnum) >= 0) {
|
||||
r = sd_json_variant_set_fieldb(&node, "rdev", JSON_BUILD_DEVNUM(devnum));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *owner = NULL;
|
||||
|
||||
uid_t uid = event->uid;
|
||||
if (!uid_is_valid(uid))
|
||||
(void) device_get_devnode_uid(dev, &uid);
|
||||
if (uid_is_valid(uid)) {
|
||||
_cleanup_free_ char *user = uid_to_name(uid);
|
||||
if (!user)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_json_variant_set_field_unsigned(&owner, "uid", uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_set_field_string(&owner, "userName", user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
gid_t gid = event->gid;
|
||||
if (!gid_is_valid(gid))
|
||||
(void) device_get_devnode_gid(dev, &gid);
|
||||
if (gid_is_valid(gid)) {
|
||||
_cleanup_free_ char *group = gid_to_name(gid);
|
||||
if (!group)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_json_variant_set_field_unsigned(&owner, "gid", gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_set_field_string(&owner, "groupName", group);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&node, "owner", owner);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
mode_t mode = event->mode;
|
||||
if (mode == MODE_INVALID)
|
||||
(void) device_get_devnode_mode(dev, &mode);
|
||||
if (mode != MODE_INVALID) {
|
||||
char mode_str[STRLEN("0755")+1];
|
||||
xsprintf(mode_str, "%04o", mode & ~S_IFMT);
|
||||
|
||||
r = sd_json_variant_set_field_string(&node, "mode", mode_str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_strv_free_ char **links = NULL;
|
||||
FOREACH_DEVICE_DEVLINK(dev, devlink) {
|
||||
r = strv_extend(&links, devlink);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!strv_isempty(links)) {
|
||||
int prio = 0;
|
||||
(void) device_get_devlink_priority(dev, &prio);
|
||||
|
||||
r = sd_json_variant_set_field_integer(&node, "symlinkPriority", prio);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_set_field_strv(&node, "symlinks", strv_sort(links));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *labels = NULL;
|
||||
const char *name, *label;
|
||||
ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&labels,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", name),
|
||||
SD_JSON_BUILD_PAIR_STRING("label", label));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&node, "securityLabels", labels);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_json_variant_set_field_boolean(&node, "inotifyWatch", event->inotify_watch);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "node", node);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
int ifindex;
|
||||
if (sd_device_get_ifindex(dev, &ifindex) >= 0) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *netif = NULL;
|
||||
|
||||
r = sd_json_variant_set_field_integer(&netif, "index", ifindex);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!isempty(event->name)) {
|
||||
r = sd_json_variant_set_field_string(&netif, "name", event->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!strv_isempty(event->altnames)) {
|
||||
r = sd_json_variant_set_field_strv(&netif, "alternativeNames", strv_sort(event->altnames));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "networkInterface", netif);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *sysattrs = NULL;
|
||||
const char *key, *value;
|
||||
HASHMAP_FOREACH_KEY(value, key, event->written_sysattrs) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&sysattrs,
|
||||
SD_JSON_BUILD_PAIR_STRING("path", key),
|
||||
SD_JSON_BUILD_PAIR_STRING("value", value));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "sysfsAttributes", sysattrs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *sysctls = NULL;
|
||||
HASHMAP_FOREACH_KEY(value, key, event->written_sysctls) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&sysctls,
|
||||
SD_JSON_BUILD_PAIR_STRING("path", key),
|
||||
SD_JSON_BUILD_PAIR_STRING("value", value));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "sysctl", sysctls);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_strv_free_ char **tags = NULL;
|
||||
FOREACH_DEVICE_TAG(dev, tag) {
|
||||
r = strv_extend(&tags, tag);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!strv_isempty(tags)) {
|
||||
r = sd_json_variant_set_field_strv(&v, "tags", strv_sort(tags));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
char **properties;
|
||||
if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) {
|
||||
r = sd_json_variant_set_field_strv(&v, "properties", strv_sort(properties));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *commands = NULL;
|
||||
void *val;
|
||||
const char *command;
|
||||
ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&commands,
|
||||
SD_JSON_BUILD_PAIR_STRING("type", PTR_TO_UDEV_BUILTIN_CMD(val) >= 0 ? "builtin" : "program"),
|
||||
SD_JSON_BUILD_PAIR_STRING("command", command));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = json_variant_set_field_non_null(&v, "queuedCommands", commands);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_json_variant_dump(v, flags, f, /* prefix = */ NULL);
|
||||
}
|
||||
|
||||
int dump_event(UdevEvent *event, sd_json_format_flags_t flags, FILE *f) {
|
||||
if (sd_json_format_enabled(flags))
|
||||
return dump_event_json(event, flags, f);
|
||||
|
||||
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
|
||||
const char *subsys = NULL, *str;
|
||||
|
||||
@ -168,4 +436,6 @@ void dump_event(UdevEvent *event, FILE *f) {
|
||||
fprintf(f, " RUN{program} : %s\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,8 +3,10 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
typedef struct UdevEvent UdevEvent;
|
||||
|
||||
void event_cache_written_sysattr(UdevEvent *event, const char *attr, const char *value);
|
||||
void event_cache_written_sysctl(UdevEvent *event, const char *attr, const char *value);
|
||||
void dump_event(UdevEvent *event, FILE *f);
|
||||
int dump_event(UdevEvent *event, sd_json_format_flags_t flags, FILE *f);
|
||||
|
@ -2591,7 +2591,7 @@ static int udev_rule_apply_token_to_event(
|
||||
if (!f)
|
||||
return log_oom();
|
||||
|
||||
dump_event(event, f);
|
||||
(void) dump_event(event, SD_JSON_FORMAT_OFF, f);
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
r = memstream_finalize(&m, &buf, NULL);
|
||||
|
@ -25,6 +25,7 @@ static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
|
||||
static const char *arg_syspath = NULL;
|
||||
static char **arg_extra_rules_dir = NULL;
|
||||
static bool arg_verbose = false;
|
||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_extra_rules_dir, strv_freep);
|
||||
|
||||
@ -37,20 +38,26 @@ static int help(void) {
|
||||
" -a --action=ACTION|help Set action string\n"
|
||||
" -N --resolve-names=early|late|never When to resolve names\n"
|
||||
" -D --extra-rules-dir=DIR Also load rules from the directory\n"
|
||||
" -v --verbose Show verbose logs\n",
|
||||
" -v --verbose Show verbose logs\n"
|
||||
" --json=pretty|short|off Generate JSON output\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_JSON = 0x100,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "action", required_argument, NULL, 'a' },
|
||||
{ "resolve-names", required_argument, NULL, 'N' },
|
||||
{ "extra-rules-dir", required_argument, NULL, 'D' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "action", required_argument, NULL, 'a' },
|
||||
{ "resolve-names", required_argument, NULL, 'N' },
|
||||
{ "extra-rules-dir", required_argument, NULL, 'D' },
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -83,6 +90,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case 'v':
|
||||
arg_verbose = true;
|
||||
break;
|
||||
case ARG_JSON:
|
||||
r = parse_json_argument(optarg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
break;
|
||||
case 'V':
|
||||
return print_version();
|
||||
case 'h':
|
||||
@ -168,7 +180,9 @@ int test_main(int argc, char *argv[], void *userdata) {
|
||||
log_info("Processing udev rules done.");
|
||||
|
||||
maybe_insert_empty_line();
|
||||
dump_event(event, NULL);
|
||||
r = dump_event(event, arg_json_format_flags, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to dump result: %m");
|
||||
maybe_insert_empty_line();
|
||||
|
||||
return 0;
|
||||
|
@ -153,6 +153,10 @@ udevadm test -N late /sys/class/net/$netdev
|
||||
udevadm test --resolve-names never /sys/class/net/$netdev
|
||||
(! udevadm test -N hello /sys/class/net/$netdev)
|
||||
udevadm test -v /sys/class/net/$netdev
|
||||
udevadm test --json=off /sys/class/net/$netdev
|
||||
udevadm test --json=pretty /sys/class/net/$netdev | jq . >/dev/null
|
||||
udevadm test --json=short /sys/class/net/$netdev | jq . >/dev/null
|
||||
udevadm test --json=help
|
||||
udevadm test -h
|
||||
|
||||
# udevadm test-builtin path_id "$loopdev"
|
||||
|
Loading…
x
Reference in New Issue
Block a user