#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"
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;
fprintf(f, " RUN{program} : %s\n", command);
}
}
+
+ return 0;
}
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);
" -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' },
{}
};
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':
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;