From 6bf0ffe8fd312c1e6549cb1721d7a7efeee77185 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 24 Jun 2005 18:05:32 +0200 Subject: [PATCH] allow rules to be compiled to one binary file All the rule files can be compiled into a single file, which can be mapped into the udev process to avoid parsing the rules with every event. Signed-off-by: Kay Sievers --- Makefile | 11 +++- test/devd_test | 0 test/net_test | 0 test/replace_test | 0 test/show_all_devices.sh | 0 test/test.all | 0 test/test.block | 0 test/test.tty | 0 test/testd.block | 0 udev_rules.c | 15 ++++- udev_rules.h | 10 +-- udev_rules_parse.c | 89 +++++++++++++++++++++++---- udev_utils.c | 2 +- udev_utils.h | 2 +- udevrulescompile.c | 127 +++++++++++++++++++++++++++++++++++++++ 15 files changed, 233 insertions(+), 23 deletions(-) mode change 100644 => 100755 test/devd_test mode change 100644 => 100755 test/net_test mode change 100644 => 100755 test/replace_test mode change 100644 => 100755 test/show_all_devices.sh mode change 100644 => 100755 test/test.all mode change 100644 => 100755 test/test.block mode change 100644 => 100755 test/test.tty mode change 100644 => 100755 test/testd.block create mode 100644 udevrulescompile.c diff --git a/Makefile b/Makefile index 8694b917a2..32e057b1cb 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ V=false ROOT = udev DAEMON = udevd SENDER = udevsend +COMPILE = udevrulescompile INITSENDER = udevinitsend RECORDER = udeveventrecorder CONTROL = udevcontrol @@ -202,7 +203,8 @@ endif # config files automatically generated GEN_CONFIGS = $(LOCAL_CFG_DIR)/udev.conf -all: $(ROOT) $(SENDER) $(INITSENDER) $(RECORDER) $(CONTROL) $(DAEMON) $(INFO) $(TESTER) $(STARTER) $(GEN_CONFIGS) $(KLCC) +all: $(ROOT) $(SENDER) $(COMPILE) $(INITSENDER) $(RECORDER) $(CONTROL) \ + $(DAEMON) $(COMPILE) $(INFO) $(TESTER) $(STARTER) $(GEN_CONFIGS) $(KLCC) @extras="$(EXTRAS)" ; for target in $$extras ; do \ echo $$target ; \ $(MAKE) prefix=$(prefix) \ @@ -268,6 +270,7 @@ $(TESTER).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(INFO).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(DAEMON).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(SENDER).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) +$(COMPILE).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(INITSENDER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(RECORDER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC) $(CONTROL).o: $(HEADERS) $( $(HEADERS)GEN_HEADERS) $(HOST_PROGS) $(KLCC) @@ -293,6 +296,10 @@ $(SENDER): $(KLCC) $(SENDER).o $(OBJS) udevd.h $(QUIET) $(LD) $(LDFLAGS) -o $@ $(SENDER).o $(OBJS) $(LIB_OBJS) $(QUIET) $(STRIPCMD) $@ +$(COMPILE): $(KLCC) $(COMPILE).o $(OBJS) + $(QUIET) $(LD) $(LDFLAGS) -o $@ $(COMPILE).o $(OBJS) $(LIB_OBJS) + $(QUIET) $(STRIPCMD) $@ + $(INITSENDER): $(KLCC) $(INITSENDER).o $(OBJS) udevd.h $(QUIET) $(LD) $(LDFLAGS) -o $@ $(INITSENDER).o $(OBJS) $(LIB_OBJS) $(QUIET) $(STRIPCMD) $@ @@ -316,7 +323,7 @@ clean: -find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \ | xargs rm -f -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(GEN_MANPAGES) $(INFO) $(DAEMON) \ - $(SENDER) $(INITSENDER) $(RECORDER) $(CONTROL) $(TESTER) $(STARTER) + $(SENDER) $(COMPILE) $(INITSENDER) $(RECORDER) $(CONTROL) $(TESTER) $(STARTER) -rm -f ccdv $(MAKE) -C klibc SUBDIRS=klibc clean @extras="$(EXTRAS)" ; for target in $$extras ; do \ diff --git a/test/devd_test b/test/devd_test old mode 100644 new mode 100755 diff --git a/test/net_test b/test/net_test old mode 100644 new mode 100755 diff --git a/test/replace_test b/test/replace_test old mode 100644 new mode 100755 diff --git a/test/show_all_devices.sh b/test/show_all_devices.sh old mode 100644 new mode 100755 diff --git a/test/test.all b/test/test.all old mode 100644 new mode 100755 diff --git a/test/test.block b/test/test.block old mode 100644 new mode 100755 diff --git a/test/test.tty b/test/test.tty old mode 100644 new mode 100755 diff --git a/test/testd.block b/test/testd.block old mode 100644 new mode 100755 diff --git a/udev_rules.c b/udev_rules.c index 52e0712d35..c416128b62 100644 --- a/udev_rules.c +++ b/udev_rules.c @@ -846,7 +846,12 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d dbg("udev->kernel_name='%s'", udev->kernel_name); /* look for a matching rule to apply */ - list_for_each_entry(rule, &udev_rule_list, node) { + udev_rules_iter_init(); + while (1) { + rule = udev_rules_iter_next(); + if (rule == NULL) + break; + if (udev->name_set && rule->name_operation != KEY_OP_UNSET) { dbg("node name already set, rule ignored"); continue; @@ -1000,9 +1005,13 @@ int udev_rules_get_run(struct udevice *udev, struct sysfs_device *sysfs_device) struct udev_rule *rule; /* look for a matching rule to apply */ - list_for_each_entry(rule, &udev_rule_list, node) { - dbg("process rule"); + udev_rules_iter_init(); + while (1) { + rule = udev_rules_iter_next(); + if (rule == NULL) + break; + dbg("process rule"); if (rule->run_operation == KEY_OP_UNSET) continue; diff --git a/udev_rules.h b/udev_rules.h index 2bf8107543..cb62839802 100644 --- a/udev_rules.h +++ b/udev_rules.h @@ -123,11 +123,13 @@ struct udev_rule { int config_line; }; -extern struct list_head udev_rule_list; - extern int udev_rules_init(void); -extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev); -extern int udev_rules_get_run(struct udevice *udev, struct sysfs_device *sysfs_device); extern void udev_rules_close(void); +extern int udev_rules_iter_init(void); +extern struct udev_rule *udev_rules_iter_next(void); + +extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev); +extern int udev_rules_get_run(struct udevice *udev, struct sysfs_device *sysfs_device); + #endif diff --git a/udev_rules_parse.c b/udev_rules_parse.c index 89925a35e4..36bf97177d 100644 --- a/udev_rules_parse.c +++ b/udev_rules_parse.c @@ -37,9 +37,45 @@ #include "logging.h" #include "udev_rules.h" -LIST_HEAD(udev_rule_list); +/* rules parsed from .rules files*/ +static LIST_HEAD(rules_list); +static struct list_head *rules_list_current; -static int add_config_dev(struct udev_rule *rule) +/* mapped compiled rules stored on disk */ +static struct udev_rule *rules_array = NULL; +static size_t rules_array_current; +static size_t rules_array_size = 0; + +static size_t rules_count = 0; + +int udev_rules_iter_init(void) +{ + rules_list_current = rules_list.next; + rules_array_current = 0; + + return 0; +} + +struct udev_rule *udev_rules_iter_next(void) +{ + static struct udev_rule *rule; + + if (rules_array) { + if (rules_array_current >= rules_count) + return NULL; + rule = &rules_array[rules_array_current]; + rules_array_current++; + } else { + dbg("head=%p current=%p next=%p", &rules_list, rules_list_current, rules_list_current->next); + if (rules_list_current == &rules_list) + return NULL; + rule = list_entry(rules_list_current, struct udev_rule, node); + rules_list_current = rules_list_current->next; + } + return rule; +} + +static int add_rule_to_list(struct udev_rule *rule) { struct udev_rule *tmp_rule; @@ -47,7 +83,7 @@ static int add_config_dev(struct udev_rule *rule) if (tmp_rule == NULL) return -ENOMEM; memcpy(tmp_rule, rule, sizeof(struct udev_rule)); - list_add_tail(&tmp_rule->node, &udev_rule_list); + list_add_tail(&tmp_rule->node, &rules_list); dbg("name='%s', symlink='%s', bus='%s', id='%s', " "sysfs_file[0]='%s', sysfs_value[0]='%s', " @@ -451,9 +487,9 @@ static int rules_parse(const char *filename) rule.config_line = lineno; strlcpy(rule.config_file, filename, sizeof(rule.config_file)); - retval = add_config_dev(&rule); + retval = add_rule_to_list(&rule); if (retval) { - dbg("add_config_dev returned with error %d", retval); + dbg("add_rule_to_list returned with error %d", retval); continue; error: err("parse error %s, line %d:%d, rule skipped", @@ -465,20 +501,47 @@ error: return retval; } +static int rules_map(const char *filename) +{ + char *buf; + size_t size; + + if (file_map(filename, &buf, &size)) + return -1; + if (size == 0) + return -1; + rules_array = (struct udev_rule *) buf; + rules_array_size = size; + rules_count = size / sizeof(struct udev_rule); + dbg("found %zi compiled rules", rules_count); + + return 0; +} + int udev_rules_init(void) { + char comp[PATH_SIZE]; struct stat stats; int retval; + strlcpy(comp, udev_rules_filename, sizeof(comp)); + strlcat(comp, ".compiled", sizeof(comp)); + if (stat(comp, &stats) == 0) { + dbg("parse compiled rules '%s'", comp); + return rules_map(comp); + } + if (stat(udev_rules_filename, &stats) != 0) return -1; - if ((stats.st_mode & S_IFMT) != S_IFDIR) + if ((stats.st_mode & S_IFMT) != S_IFDIR) { + dbg("parse single rules file '%s'", udev_rules_filename); retval = rules_parse(udev_rules_filename); - else { + } else { struct name_entry *name_loop, *name_tmp; LIST_HEAD(name_list); + dbg("parse rules directory '%s'", udev_rules_filename); retval = add_matching_files(&name_list, udev_rules_filename, RULEFILE_SUFFIX); list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { @@ -495,9 +558,11 @@ void udev_rules_close(void) struct udev_rule *rule; struct udev_rule *temp_rule; - list_for_each_entry_safe(rule, temp_rule, &udev_rule_list, node) { - list_del(&rule->node); - free(rule); - } + if (rules_array) + file_unmap(rules_array, rules_array_size); + else + list_for_each_entry_safe(rule, temp_rule, &rules_list, node) { + list_del(&rule->node); + free(rule); + } } - diff --git a/udev_utils.c b/udev_utils.c index 64a7ba9caa..c35b287881 100644 --- a/udev_utils.c +++ b/udev_utils.c @@ -247,7 +247,7 @@ int file_map(const char *filename, char **buf, size_t *bufsize) return 0; } -void file_unmap(char *buf, size_t bufsize) +void file_unmap(void *buf, size_t bufsize) { munmap(buf, bufsize); } diff --git a/udev_utils.h b/udev_utils.h index b3e604fb98..3e372dd948 100644 --- a/udev_utils.h +++ b/udev_utils.h @@ -39,7 +39,7 @@ extern int string_is_true(const char *str); extern int parse_get_pair(char **orig_string, char **left, char **right); extern int unlink_secure(const char *filename); extern int file_map(const char *filename, char **buf, size_t *bufsize); -extern void file_unmap(char *buf, size_t bufsize); +extern void file_unmap(void *buf, size_t bufsize); extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur); extern void remove_trailing_char(char *path, char c); extern void replace_untrusted_chars(char *string); diff --git a/udevrulescompile.c b/udevrulescompile.c new file mode 100644 index 0000000000..ff9dd79fd4 --- /dev/null +++ b/udevrulescompile.c @@ -0,0 +1,127 @@ +/* + * udevrulescompile.c - store already parsed config on disk + * + * Copyright (C) 2005 Kay Sievers + * + * 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 version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "udev_libc_wrapper.h" +#include "udev_sysfs.h" +#include "udev.h" +#include "udev_version.h" +#include "logging.h" +#include "udev_rules.h" +#include "udev_utils.h" +#include "list.h" + +#ifdef USE_LOG +void log_message(int priority, const char *format, ...) +{ + va_list args; + + if (priority > udev_log_priority) + return; + + va_start(args, format); + vsyslog(priority, format, args); + va_end(args); +} +#endif + +int main(int argc, char *argv[], char *envp[]) +{ + struct udev_rule *rule; + FILE *f; + char comp[PATH_SIZE]; + char comp_tmp[PATH_SIZE]; + int retval = 0; + + logging_init("udevrulescompile"); + udev_init_config(); + dbg("version %s", UDEV_VERSION); + + strlcpy(comp, udev_rules_filename, sizeof(comp)); + strlcat(comp, ".compiled", sizeof(comp)); + strlcpy(comp_tmp, comp, sizeof(comp_tmp)); + strlcat(comp_tmp, ".tmp", sizeof(comp_tmp)); + + /* remove old version, otherwise we would read it + * instead of the real rules */ + unlink(comp); + unlink(comp_tmp); + + udev_rules_init(); + + f = fopen(comp_tmp, "w"); + if (f == NULL) { + err("unable to create db file '%s'", comp_tmp); + unlink(comp_tmp); + retval = 1; + goto exit; + } + dbg("storing compiled rules in '%s'", comp_tmp); + + udev_rules_iter_init(); + while (1) { + char *endptr; + unsigned long id; + + rule = udev_rules_iter_next(); + if (rule == NULL) + break; + + id = strtoul(rule->owner, &endptr, 10); + if (endptr[0] != '\0') { + uid_t uid; + + uid = lookup_user(rule->owner); + dbg("replacing username='%s' by id=%i", rule->owner, uid); + sprintf(rule->owner, "%li", uid); + } + + id = strtoul(rule->group, &endptr, 10); + if (endptr[0] != '\0') { + gid_t gid; + + gid = lookup_group(rule->group); + dbg("replacing groupname='%s' by id=%i", rule->group, gid); + sprintf(rule->group, "%li", gid); + } + + dbg("kernel='%s' name='%s'", rule->kernel, rule->name); + fwrite(rule, sizeof(struct udev_rule), 1, f); + } + + fclose(f); + dbg("activating compiled rules in '%s'", comp); + if (rename(comp_tmp, comp) != 0) { + err("unable to write file"); + unlink(comp); + unlink(comp_tmp); + retval = 2; + } + +exit: + logging_close(); + return retval; +}