1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-25 18:50:18 +03:00

udevadm-test: show result in json format (#36284)

This adds `--json=MODE` command line option to `udevadm test`.
This may be useful for parsing e.g. network interface name or device
node symlinks.

Closes #23661.
This commit is contained in:
Yu Watanabe 2025-02-08 15:50:04 +09:00 committed by GitHub
commit ed8063d143
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 510 additions and 115 deletions

View File

@ -879,7 +879,10 @@
<optional><replaceable>devpath</replaceable>|<replaceable>file</replaceable>|<replaceable>unit</replaceable></optional>
</title>
<para>Simulate a udev event run for the given device, and print debug output.</para>
<para>
Simulate a udev event run for the given device, and print debug output. The option
<option>--json=</option> may be useful for parsing the final result. See also Example section.
</para>
<variablelist>
<varlistentry>
@ -930,6 +933,8 @@
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect2>
@ -1291,6 +1296,36 @@
<programlisting># udevadm lock -d /dev/sda1 dd if=fs.raw of=/dev/sda1</programlisting>
</example>
<example>
<title>Predict Network Interface Renaming</title>
<para>To predict a network interface name, <command>udevadm test</command> can be used:</para>
<programlisting>$ udevadm test /sys/class/net/wlan0 --json=pretty 2>/dev/null | jq .networkInterface.name
"wlp59s0"</programlisting>
</example>
<example>
<title>Predict Symbolic links of a Device Node</title>
<para>To predict symbolic links to a device node, <command>udevadm test</command> can be used:</para>
<programlisting>$ udevadm test /dev/nvme0n1p1 --json=pretty 2>/dev/null | jq .node.symlinks
[
"/dev/disk/by-diskseq/1-part1",
"/dev/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247-part1",
"/dev/disk/by-id/nvme-WDC_PC_SN720_SDAQNTW-512G-1001_192290802247_1-part1",
"/dev/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6-part1",
"/dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part/by-partnum/1",
"/dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part1"
]
$ udevadm test /dev/input/event3 --json=pretty 2>/dev/null | jq .node.symlinks
[
"/dev/input/by-path/platform-i8042-serio-0-event-kbd"
]</programlisting>
</example>
</refsect1>
<refsect1>

View File

@ -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]}"

View File

@ -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'
}

View File

@ -18,6 +18,7 @@ int device_new_from_strv(sd_device **ret, char **strv);
int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_sysnum_unsigned(sd_device *device, unsigned *ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);

View File

@ -1445,6 +1445,26 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
return 0;
}
int device_get_sysnum_unsigned(sd_device *device, unsigned *ret) {
int r;
assert(device);
const char *s;
r = sd_device_get_sysnum(device, &s);
if (r < 0)
return r;
unsigned n;
r = safe_atou_full(s, SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_WHITESPACE | 10, &n);
if (r < 0)
return r;
if (ret)
*ret = n;
return 0;
}
_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
assert_return(device, -EINVAL);

View File

@ -182,6 +182,8 @@ static void test_sd_device_one(sd_device *d) {
assert_se(val < sysname + strlen(sysname));
assert_se(in_charset(val, DIGITS));
assert_se(!ascii_isdigit(val[-1]));
r = device_get_sysnum_unsigned(d, NULL);
ASSERT_TRUE(r >= 0 || r == -ERANGE); /* sysnum may be too large. */
} else
assert_se(r == -ENOENT);

View File

@ -1237,7 +1237,7 @@ static int names_mac(UdevEvent *event, const char *prefix) {
static int names_netdevsim(UdevEvent *event, const char *prefix) {
sd_device *netdevsimdev, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
const char *sysnum, *phys_port_name;
const char *phys_port_name;
unsigned addr;
int r;
@ -1252,14 +1252,10 @@ static int names_netdevsim(UdevEvent *event, const char *prefix) {
if (r < 0)
return r;
r = sd_device_get_sysnum(netdevsimdev, &sysnum);
r = device_get_sysnum_unsigned(netdevsimdev, &addr);
if (r < 0)
return log_device_debug_errno(netdevsimdev, r, "Failed to get device sysnum: %m");
r = safe_atou(sysnum, &addr);
if (r < 0)
return log_device_debug_errno(netdevsimdev, r, "Failed to parse device sysnum: %m");
r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get 'phys_port_name' attribute: %m");

View File

@ -59,25 +59,18 @@ static void path_prepend(char **path, const char *fmt, ...) {
** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
*/
static int format_lun_number(sd_device *dev, char **path) {
const char *sysnum;
unsigned long lun;
unsigned lun;
int r;
r = sd_device_get_sysnum(dev, &sysnum);
if (r < 0)
return r;
if (!sysnum)
return -ENOENT;
r = safe_atolu_full(sysnum, 10, &lun);
r = device_get_sysnum_unsigned(dev, &lun);
if (r < 0)
return r;
if (lun < 256)
/* address method 0, peripheral device addressing with bus id of zero */
path_prepend(path, "lun-%lu", lun);
path_prepend(path, "lun-%u", lun);
else
/* handle all other lun addressing methods by using a variant of the original lun format */
path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff);
path_prepend(path, "lun-0x%04x%04x00000000", lun & 0xffff, (lun >> 16) & 0xffff);
return 0;
}
@ -241,7 +234,7 @@ static sd_device* handle_scsi_iscsi(sd_device *parent, char **path) {
if (sd_device_get_sysattr_value(sessiondev, "targetname", &target) < 0)
return NULL;
if (sd_device_get_sysnum(transportdev, &sysnum) < 0 || !sysnum)
if (sd_device_get_sysnum(transportdev, &sysnum) < 0)
return NULL;
connname = strjoina("connection", sysnum, ":0");
if (sd_device_new_from_subsystem_sysname(&conndev, "iscsi_connection", connname) < 0)
@ -723,7 +716,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[]) {
} else if (device_in_subsystem(parent, "serio")) {
const char *sysnum;
if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
if (sd_device_get_sysnum(parent, &sysnum) >= 0) {
path_prepend(&path, "serio-%s", sysnum);
parent = skip_subsystem(parent, "serio");
}
@ -817,7 +810,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[]) {
} else if (device_in_subsystem(parent, "spi")) {
const char *sysnum;
if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
if (sd_device_get_sysnum(parent, &sysnum) >= 0) {
path_prepend(&path, "cs-%s", sysnum);
parent = skip_subsystem(parent, "spi");
}

View File

@ -3,8 +3,11 @@
#include "ansi-color.h"
#include "device-private.h"
#include "device-util.h"
#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"
@ -28,12 +31,368 @@ 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;
if (!f)
f = stdout;
if (sd_device_get_devpath(dev, &str) >= 0)
fprintf(f, "%sDevice path:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_sysname(dev, &str) >= 0)
fprintf(f, "%sDevice name:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_sysnum(dev, &str) >= 0)
fprintf(f, "%sDevice number:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_device_id(dev, &str) >= 0)
fprintf(f, "%sDevice ID:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_subsystem(dev, &subsys) >= 0) {
const char *driver_subsys = NULL;
(void) sd_device_get_driver_subsystem(dev, &driver_subsys);
fprintf(f, "%sSubsystem:%s\n %s%s%s%s\n", ansi_highlight(), ansi_normal(), subsys,
driver_subsys ? " (" : "",
strempty(driver_subsys),
driver_subsys ? ")" : "");
}
if (sd_device_get_devtype(dev, &str) >= 0)
fprintf(f, "%sDevice type:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_driver(dev, &str) >= 0)
fprintf(f, "%sDevice driver:%s\n %s\n", ansi_highlight(), ansi_normal(), str);
if (sd_device_get_devname(dev, &str) >= 0) {
dev_t devnum;
if (sd_device_get_devnum(dev, &devnum) >= 0)
fprintf(f, "%sDevice node:%s\n %s (%s "DEVNUM_FORMAT_STR")\n", ansi_highlight(), ansi_normal(), str,
streq_ptr(subsys, "block") ? "block" : "char",
DEVNUM_FORMAT_VAL(devnum));
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);
fprintf(f, "%sDevice node owner user:%s\n %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid);
}
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);
fprintf(f, "%sDevice node owner group:%s\n %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid);
}
mode_t mode = event->mode;
if (mode == MODE_INVALID)
(void) device_get_devnode_mode(dev, &mode);
if (mode != MODE_INVALID)
fprintf(f, "%sDevice node permission:%s\n %04o\n", ansi_highlight(), ansi_normal(), mode);
if (sd_device_get_devlink_first(dev)) {
int prio = 0;
(void) device_get_devlink_priority(dev, &prio);
fprintf(f, "%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio);
FOREACH_DEVICE_DEVLINK(dev, devlink)
fprintf(f, " %s\n", devlink);
}
if (!ordered_hashmap_isempty(event->seclabel_list)) {
const char *name, *label;
fprintf(f, "%sDevice node security label:%s\n", ansi_highlight(), ansi_normal());
ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list)
fprintf(f, " %s : %s\n", name, label);
}
fprintf(f, "%sInotify watch:%s\n %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch));
}
int ifindex;
if (sd_device_get_ifindex(dev, &ifindex) >= 0) {
fprintf(f, "%sNetwork interface index:%s\n %i\n", ansi_highlight(), ansi_normal(), ifindex);
if (!isempty(event->name))
fprintf(f, "%sNetwork interface name:%s\n %s\n", ansi_highlight(), ansi_normal(), event->name);
if (!strv_isempty(event->altnames)) {
bool space = true;
fprintf(f, "%sAlternative interface names:%s", ansi_highlight(), ansi_normal());
fputstrv(f, strv_sort(event->altnames), "\n ", &space);
fputs("\n", f);
}
}
if (!hashmap_isempty(event->written_sysattrs)) {
const char *key, *value;
@ -50,68 +409,18 @@ void dump_event(UdevEvent *event, FILE *f) {
fprintf(f, " %s : %s\n", key, value);
}
fprintf(f, "%sProperties:%s\n", ansi_highlight(), ansi_normal());
FOREACH_DEVICE_PROPERTY(dev, key, value)
fprintf(f, " %s=%s\n", key, value);
if (sd_device_get_tag_first(dev)) {
fprintf(f, "%sTags:%s\n", ansi_highlight(), ansi_normal());
FOREACH_DEVICE_TAG(dev, tag)
fprintf(f, " %s\n", tag);
}
if (sd_device_get_devnum(dev, NULL) >= 0) {
if (sd_device_get_devlink_first(dev)) {
int prio = 0;
(void) device_get_devlink_priority(dev, &prio);
fprintf(f, "%sDevice node symlinks:%s (priority=%i)\n", ansi_highlight(), ansi_normal(), prio);
FOREACH_DEVICE_DEVLINK(dev, devlink)
fprintf(f, " %s\n", devlink);
}
fprintf(f, "%sInotify watch:%s\n %s\n", ansi_highlight(), ansi_normal(), enabled_disabled(event->inotify_watch));
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);
fprintf(f, "%sDevice node owner:%s\n %s (uid="UID_FMT")\n", ansi_highlight(), ansi_normal(), strna(user), uid);
}
gid_t gid = event->gid;
if (!gid_is_valid(uid))
(void) device_get_devnode_gid(dev, &gid);
if (gid_is_valid(gid)) {
_cleanup_free_ char *group = gid_to_name(gid);
fprintf(f, "%sDevice node group:%s\n %s (gid="GID_FMT")\n", ansi_highlight(), ansi_normal(), strna(group), gid);
}
mode_t mode = event->mode;
if (mode == MODE_INVALID)
(void) device_get_devnode_mode(dev, &mode);
if (mode != MODE_INVALID)
fprintf(f, "%sDevice node permission:%s\n %04o\n", ansi_highlight(), ansi_normal(), mode);
if (!ordered_hashmap_isempty(event->seclabel_list)) {
const char *name, *label;
fprintf(f, "%sDevice node security label:%s\n", ansi_highlight(), ansi_normal());
ORDERED_HASHMAP_FOREACH_KEY(label, name, event->seclabel_list)
fprintf(f, " %s : %s\n", name, label);
}
}
if (sd_device_get_ifindex(dev, NULL) >= 0) {
if (!isempty(event->name))
fprintf(f, "%sNetwork interface name:%s\n %s\n", ansi_highlight(), ansi_normal(), event->name);
if (!strv_isempty(event->altnames)) {
bool space = true;
fprintf(f, "%sAlternative interface names:%s", ansi_highlight(), ansi_normal());
fputstrv(f, event->altnames, "\n ", &space);
fputs("\n", f);
}
char **properties;
if (device_get_properties_strv(dev, &properties) >= 0 && !strv_isempty(properties)) {
bool space = true;
fprintf(f, "%sProperties:%s", ansi_highlight(), ansi_normal());
fputstrv(f, strv_sort(properties), "\n ", &space);
fputs("\n", f);
}
if (!ordered_hashmap_isempty(event->run_list)) {
@ -127,4 +436,6 @@ void dump_event(UdevEvent *event, FILE *f) {
fprintf(f, " RUN{program} : %s\n", command);
}
}
return 0;
}

View File

@ -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);

View File

@ -2586,25 +2586,19 @@ static int udev_rule_apply_token_to_event(
case TK_A_OPTIONS_DUMP: {
log_event_info(event, token, "Dumping current state:");
if (event->event_mode == EVENT_UDEV_WORKER) {
_cleanup_(memstream_done) MemStream m = {};
FILE *f = memstream_init(&m);
if (!f)
return log_oom();
_cleanup_(memstream_done) MemStream m = {};
FILE *f = memstream_init(&m);
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);
if (r < 0)
log_event_warning_errno(event, token, r, "Failed to finalize memory stream, ignoring: %m");
else
log_info("%s", buf);
} else {
puts("============================");
dump_event(event, NULL);
puts("============================");
}
_cleanup_free_ char *buf = NULL;
r = memstream_finalize(&m, &buf, NULL);
if (r < 0)
log_event_warning_errno(event, token, r, "Failed to finalize memory stream, ignoring: %m");
else
log_info("%s", buf);
log_event_info(event, token, "DONE");
return true;

View File

@ -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':
@ -100,6 +112,20 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
static void maybe_insert_empty_line(void) {
if (log_get_max_level() < LOG_INFO)
return;
LogTarget target = log_get_target();
if (!IN_SET(log_get_target(), LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE_PREFIXED, LOG_TARGET_AUTO))
return;
if (target == LOG_TARGET_AUTO && stderr_is_journal())
return;
fputs("\n", stderr);
}
int test_main(int argc, char *argv[], void *userdata) {
_cleanup_(udev_rules_freep) UdevRules *rules = NULL;
_cleanup_(udev_event_unrefp) UdevEvent *event = NULL;
@ -114,22 +140,24 @@ int test_main(int argc, char *argv[], void *userdata) {
if (r <= 0)
return r;
puts("This program is for debugging only, it does not run any program\n"
"specified by a RUN key. It may show incorrect results, because\n"
"some values may be different, or not available at a simulation run.");
log_info("This program is for debugging only, it does not run any program\n"
"specified by a RUN key. It may show incorrect results, because\n"
"some values may be different, or not available at a simulation run.");
assert_se(sigprocmask(SIG_SETMASK, NULL, &sigmask_orig) >= 0);
puts("\nLoading builtins...");
maybe_insert_empty_line();
log_info("Loading builtins...");
udev_builtin_init();
UDEV_BUILTIN_DESTRUCTOR;
puts("Loading builtins done.");
log_info("Loading builtins done.");
puts("\nLoading udev rules files...");
maybe_insert_empty_line();
log_info("Loading udev rules files...");
r = udev_rules_load(&rules, arg_resolve_name_timing, arg_extra_rules_dir);
if (r < 0)
return log_error_errno(r, "Failed to read udev rules: %m");
puts("Loading udev rules files done.");
log_info("Loading udev rules files done.");
r = find_device_with_action(arg_syspath, arg_action, &dev);
if (r < 0)
@ -146,12 +174,16 @@ int test_main(int argc, char *argv[], void *userdata) {
assert_se(sigfillset(&mask) >= 0);
assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
printf("\nProcessing udev rules%s...\n", arg_verbose ? "" : " (verbose logs can be shown by -v/--verbose)");
maybe_insert_empty_line();
log_info("Processing udev rules%s...", arg_verbose ? "" : " (verbose logs can be shown by -v/--verbose)");
udev_event_execute_rules(event, rules);
puts("Processing udev rules done.");
log_info("Processing udev rules done.");
puts("");
dump_event(event, NULL);
maybe_insert_empty_line();
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;
}

View File

@ -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"