diff options
-rw-r--r-- | udevd.c | 12 | ||||
-rw-r--r-- | udevtrigger.8 | 18 | ||||
-rw-r--r-- | udevtrigger.c | 184 | ||||
-rw-r--r-- | udevtrigger.xml | 41 |
4 files changed, 228 insertions, 27 deletions
@@ -926,12 +926,6 @@ int main(int argc, char *argv[], char *envp[]) selinux_init(); dbg("version %s", UDEV_VERSION); - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - err("root privileges required"); - goto exit; - } - /* parse commandline options */ for (i = 1 ; i < argc; i++) { char *arg = argv[i]; @@ -946,6 +940,12 @@ int main(int argc, char *argv[], char *envp[]) } } + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + err("root privileges required"); + goto exit; + } + /* init sockets to receive events */ if (init_udevd_socket() < 0) { if (errno == EADDRINUSE) { diff --git a/udevtrigger.8 b/udevtrigger.8 index 1e14a7b101..4525aeeddf 100644 --- a/udevtrigger.8 +++ b/udevtrigger.8 @@ -14,20 +14,32 @@ udevtrigger \- request kernel devices events for coldplug .SH "SYNOPSIS" .HP 12 -\fBudevtrigger\fR [\fB\-\-verbose\fR] [\fB\-\-dry\-run\fR] [\fB\-\-retry\-failed\fR] +\fBudevtrigger\fR [\fB\-\-verbose\fR] [\fB\-\-dry\-run\fR] [\fB\-\-retry\-failed\fR] [\fB\-\-help\fR] [\fB\-\-subsystem\-match=\fR\fB\fIsubsystem\fR\fR] [\fB\-\-subsystem\-nomatch=\fR\fB\fIsubsystem\fR\fR] [\fB\-\-attr\-match=\fR\fB\fIattribute=value\fR\fR] [\fB\-\-attr\-nomatch=\fR\fB\fIattribute=value\fR\fR] .SH "DESCRIPTION" .PP Trigger kernel device uevents to replay missing events at system coldplug. .SH "OPTIONS" .TP 3n \fB\-\-verbose\fR -Print the list of devices found in sysfs. +Print the list of devices which will be triggered. .TP 3n \fB\-\-dry\-run\fR Do not actually trigger the event. .TP 3n \fB\-\-retry\-failed\fR -Trigger events which are failed during a previous run. +Trigger only the events which are failed during a previous run. +.TP 3n +\fB\-\-subsystem\-match=\fR\fB\fIsubsystem\fR\fR +Trigger events for devices which belong to a matching subsystem. This option can be specified multiple times and supports shell style pattern matching. +.TP 3n +\fB\-\-subsystem\-nomatch=\fR\fB\fIsubsystem\fR\fR +Do not trigger events for devices which belong to a matching subsystem. This option can be specified multiple times and supports shell style pattern matching. +.TP 3n +\fB\-\-attr\-match=\fR\fB\fIattribute=value\fR\fR +Trigger events for devices with a matching sysfs attribute. If a value is specified along with the attribute name, the content of the attribute is matched against the given value using shell style pattern matching. If no value is specified, the existence of the sysfs attribute is checked. This option can be specified multiple times. +.TP 3n +\fB\-\-attr\-nomatch\fR\fB\fIattribute=value\fR\fR +Do not trigger events for devices with a matching sysfs attribute. If a value is specified along with the attribute name, the content of the attribute is matched against the given value using shell style pattern matching. If no value is specified, the existence of the sysfs attribute is checked. This option can be specified multiple times. .SH "ENVIRONMENT" .TP 3n \fBUDEV_LOG\fR diff --git a/udevtrigger.c b/udevtrigger.c index 09ef60addb..9a1e82887b 100644 --- a/udevtrigger.c +++ b/udevtrigger.c @@ -22,19 +22,18 @@ #include <string.h> #include <stdio.h> #include <unistd.h> +#include <getopt.h> #include <errno.h> #include <dirent.h> #include <fcntl.h> #include <syslog.h> +#include <fnmatch.h> #include <sys/stat.h> #include <sys/types.h> #include "udev.h" #include "udevd.h" -static int verbose; -static int dry_run; - #ifdef USE_LOG void log_message(int priority, const char *format, ...) { @@ -49,6 +48,9 @@ void log_message(int priority, const char *format, ...) } #endif +static int verbose; +static int dry_run; + /* list of devices that we should run last due to any one of a number of reasons */ static char *last_list[] = { "/class/block/md", @@ -69,6 +71,11 @@ LIST_HEAD(device_first_list); LIST_HEAD(device_default_list); LIST_HEAD(device_last_list); +LIST_HEAD(filter_subsytem_match_list); +LIST_HEAD(filter_subsytem_nomatch_list); +LIST_HEAD(filter_attr_match_list); +LIST_HEAD(filter_attr_nomatch_list); + static int device_list_insert(const char *path) { struct list_head *device_list = &device_default_list; @@ -164,6 +171,93 @@ static int is_device(const char *path) return 1; } +static int subsystem_filtered(const char *subsystem) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed subsystems */ + list_for_each_entry(loop_name, &filter_subsytem_nomatch_list, node) + if (fnmatch(subsystem, loop_name->name, 0) == 0) + return 1; + + /* skip devices not matching the listed subsystems */ + if (!list_empty(&filter_subsytem_match_list)) { + list_for_each_entry(loop_name, &filter_subsytem_match_list, node) + if (fnmatch(subsystem, loop_name->name, 0) == 0) + return 0; + return 1; + } + + return 0; +} + +static int attr_match(const char *path, const char *attr_value) +{ + char attr[NAME_SIZE]; + char file[PATH_SIZE]; + char *match_value; + + strlcpy(attr, attr_value, sizeof(attr)); + + /* separate attr and match value */ + match_value = strchr(attr, '='); + if (match_value != NULL) { + match_value[0] = '\0'; + match_value = &match_value[1]; + } + + strlcpy(file, path, sizeof(file)); + strlcat(file, "/", sizeof(file)); + strlcat(file, attr, sizeof(file)); + + if (match_value != NULL) { + /* match file content */ + char value[NAME_SIZE]; + int fd; + ssize_t size; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + return 0; + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + + /* match if attribute value matches */ + if (fnmatch(match_value, value, 0) == 0) + return 1; + } else { + /* match if attribute exists */ + struct stat statbuf; + + if (stat(file, &statbuf) == 0) + return 1; + } + return 0; +} + +static int attr_filtered(const char *path) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed sysfs attributes */ + list_for_each_entry(loop_name, &filter_attr_nomatch_list, node) + if (attr_match(path, loop_name->name)) + return 1; + + /* skip devices not matching the listed sysfs attributes */ + if (!list_empty(&filter_attr_match_list)) { + list_for_each_entry(loop_name, &filter_attr_match_list, node) + if (attr_match(path, loop_name->name)) + return 0; + return 1; + } + return 0; +} + static void scan_bus(void) { char base[PATH_SIZE]; @@ -183,6 +277,9 @@ static void scan_bus(void) if (dent->d_name[0] == '.') continue; + if (subsystem_filtered(dent->d_name)) + continue; + strlcpy(dirname, base, sizeof(dirname)); strlcat(dirname, "/", sizeof(dirname)); strlcat(dirname, dent->d_name, sizeof(dirname)); @@ -200,7 +297,8 @@ static void scan_bus(void) strlcpy(dirname2, dirname, sizeof(dirname2)); strlcat(dirname2, "/", sizeof(dirname2)); strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - + if (attr_filtered(dirname2)) + continue; if (is_device(dirname2)) device_list_insert(dirname2); } @@ -224,6 +322,9 @@ static void scan_block(void) if (stat(base, &statbuf) == 0) return; + if (subsystem_filtered("block")) + return; + strlcpy(base, sysfs_path, sizeof(base)); strlcat(base, "/block", sizeof(base)); @@ -240,6 +341,8 @@ static void scan_block(void) strlcpy(dirname, base, sizeof(dirname)); strlcat(dirname, "/", sizeof(dirname)); strlcat(dirname, dent->d_name, sizeof(dirname)); + if (attr_filtered(dirname)) + continue; if (is_device(dirname)) device_list_insert(dirname); else @@ -260,6 +363,8 @@ static void scan_block(void) strlcpy(dirname2, dirname, sizeof(dirname2)); strlcat(dirname2, "/", sizeof(dirname2)); strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; if (is_device(dirname2)) device_list_insert(dirname2); } @@ -289,6 +394,9 @@ static void scan_class(void) if (dent->d_name[0] == '.') continue; + if (subsystem_filtered(dent->d_name)) + continue; + strlcpy(dirname, base, sizeof(dirname)); strlcat(dirname, "/", sizeof(dirname)); strlcat(dirname, dent->d_name, sizeof(dirname)); @@ -306,6 +414,8 @@ static void scan_class(void) strlcpy(dirname2, dirname, sizeof(dirname2)); strlcat(dirname2, "/", sizeof(dirname2)); strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; if (is_device(dirname2)) device_list_insert(dirname2); } @@ -356,36 +466,73 @@ static void scan_failed(void) int main(int argc, char *argv[], char *envp[]) { - int i; int failed = 0; + int option; + int longindex; + struct option options[] = { + { "verbose", 0, NULL, 'v' }, + { "dry-run", 0, NULL, 'n' }, + { "retry-failed", 0, NULL, 'F' }, + { "help", 0, NULL, 'h' }, + { "subsystem-match", 1, NULL, 's' }, + { "subsystem-nomatch", 1, NULL, 'S' }, + { "attr-match", 1, NULL, 'a' }, + { "attr-nomatch", 1, NULL, 'A' }, + {} + }; logging_init("udevtrigger"); udev_config_init(); dbg("version %s", UDEV_VERSION); sysfs_init(); - for (i = 1 ; i < argc; i++) { - char *arg = argv[i]; + while (1) { + option = getopt_long(argc, argv, "vnFhs:S:a:A:", options, &longindex); + if (option == -1) + break; - if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0) { + switch (option) { + case 'v': verbose = 1; - } else if (strcmp(arg, "--dry-run") == 0 || strcmp(arg, "-n") == 0) { + break; + case 'n': dry_run = 1; - } else if (strcmp(arg, "--retry-failed") == 0 || strcmp(arg, "-F") == 0) { + break; + case 'F': failed = 1; - } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { - printf("Usage: udevtrigger [--help] [--verbose] [--dry-run] [--retry-failed]\n"); + break; + case 's': + name_list_add(&filter_subsytem_match_list, optarg, 0); + break; + case 'S': + name_list_add(&filter_subsytem_nomatch_list, optarg, 0); + break; + case 'a': + name_list_add(&filter_attr_match_list, optarg, 0); + break; + case 'A': + name_list_add(&filter_attr_nomatch_list, optarg, 0); + break; + case 'h': + printf("Usage: udevtrigger OPTIONS\n" + " --verbose print the list of devices which will be triggered\n" + " --dry-run do not actually trigger the event\n" + " --retry-failed trigger only the events which are failed during a previous run\n" + " --subsystem-match select only devices from the specified subystem\n" + " --subsystem-nomatch exclude devices from the specified subystem\n" + " --attr-match=<file[=<value>]> select only devices with a matching sysfs attribute\n" + " --attr-nomatch=<file[=<value>]> exclude devices with a matching sysfs attribute\n" + " --help print this text\n" + "\n"); + goto exit; + default: goto exit; - } else { - fprintf(stderr, "unrecognized option '%s'\n", arg); - err("unrecognized option '%s'\n", arg); } } if (failed) scan_failed(); else { - /* default action */ scan_bus(); scan_class(); scan_block(); @@ -393,6 +540,11 @@ int main(int argc, char *argv[], char *envp[]) exec_lists(); exit: + name_list_cleanup(&filter_subsytem_match_list); + name_list_cleanup(&filter_subsytem_nomatch_list); + name_list_cleanup(&filter_attr_match_list); + name_list_cleanup(&filter_attr_nomatch_list); + sysfs_cleanup(); logging_close(); return 0; diff --git a/udevtrigger.xml b/udevtrigger.xml index 170fcd6291..60a7b1eaf7 100644 --- a/udevtrigger.xml +++ b/udevtrigger.xml @@ -28,6 +28,11 @@ <arg><option>--verbose</option></arg> <arg><option>--dry-run</option></arg> <arg><option>--retry-failed</option></arg> + <arg><option>--help</option></arg> + <arg><option>--subsystem-match=<replaceable>subsystem</replaceable></option></arg> + <arg><option>--subsystem-nomatch=<replaceable>subsystem</replaceable></option></arg> + <arg><option>--attr-match=<replaceable>attribute=value</replaceable></option></arg> + <arg><option>--attr-nomatch=<replaceable>attribute=value</replaceable></option></arg> </cmdsynopsis> </refsynopsisdiv> @@ -40,7 +45,7 @@ <varlistentry> <term><option>--verbose</option></term> <listitem> - <para>Print the list of devices found in sysfs.</para> + <para>Print the list of devices which will be triggered.</para> </listitem> </varlistentry> <varlistentry> @@ -52,7 +57,39 @@ <varlistentry> <term><option>--retry-failed</option></term> <listitem> - <para>Trigger events which are failed during a previous run.</para> + <para>Trigger only the events which are failed during a previous run.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--subsystem-match=<replaceable>subsystem</replaceable></option></term> + <listitem> + <para>Trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--subsystem-nomatch=<replaceable>subsystem</replaceable></option></term> + <listitem> + <para>Do not trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--attr-match=<replaceable>attribute=value</replaceable></option></term> + <listitem> + <para>Trigger events for devices with a matching sysfs attribute. If a value is specified + along with the attribute name, the content of the attribute is matched against the given + value using shell style pattern matching. If no value is specified, the existence of the + sysfs attribute is checked. This option can be specified multiple times.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--attr-nomatch<replaceable>attribute=value</replaceable></option></term> + <listitem> + <para>Do not trigger events for devices with a matching sysfs attribute. If a value is + specified along with the attribute name, the content of the attribute is matched against + the given value using shell style pattern matching. If no value is specified, the existence + of the sysfs attribute is checked. This option can be specified multiple times.</para> </listitem> </varlistentry> </variablelist> |