From e5e322bc627a07d29a07e08f7c96bd644a3ae057 Mon Sep 17 00:00:00 2001 From: "kay.sievers@vrfy.org" Date: Sat, 12 Mar 2005 22:36:32 +0100 Subject: [PATCH] rename namedev_dev to udev_rule --- Makefile | 6 +- README | 2 +- logging.h | 10 - namedev.c | 778 ----------------------------------------------------- namedev.h | 94 ------- namedev_parse.c | 369 ------------------------- udev.c | 4 +- udev_add.c | 4 +- udev_config.c | 9 +- udev_remove.c | 1 - udev_rules.c | 778 +++++++++++++++++++++++++++++++++++++++++++++++++++++ udev_rules.h | 94 +++++++ udev_rules_parse.c | 365 +++++++++++++++++++++++++ udevstart.c | 4 +- udevtest.c | 4 +- 15 files changed, 1252 insertions(+), 1270 deletions(-) delete mode 100644 namedev.c delete mode 100644 namedev.h delete mode 100644 namedev_parse.c create mode 100644 udev_rules.c create mode 100644 udev_rules.h create mode 100644 udev_rules_parse.c diff --git a/Makefile b/Makefile index 8e29f24e70..556298a9f1 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,7 @@ CFLAGS := -pipe HEADERS = \ udev.h \ udev_utils.h \ - namedev.h \ + udev_rules.h \ udev_version.h \ udev_db.h \ udev_sysfs.h \ @@ -142,8 +142,8 @@ UDEV_OBJS = \ udev_sysfs.o \ udev_db.o \ udev_multiplex.o \ - namedev.o \ - namedev_parse.o \ + udev_rules.o \ + udev_rules_parse.o \ udev_libc_wrapper.o OBJS = \ diff --git a/README b/README index c0475dc904..54cee659a8 100644 --- a/README +++ b/README @@ -65,7 +65,7 @@ To use: This will put the udev binary in /sbin, create the /udev and /etc/udev directories, and place the udev configuration files in /etc/udev. You - will probably want to edit the namedev.* files to create custom naming + will probably want to edit the *.rules files to create custom naming rules. More info on how the config files are set up are contained in comments in the files, and is located in the documentation. diff --git a/logging.h b/logging.h index 3ff54ec974..e7653ee1b8 100644 --- a/logging.h +++ b/logging.h @@ -26,7 +26,6 @@ #define info(format, arg...) do { } while (0) #define dbg(format, arg...) do { } while (0) -#define dbg_parse(format, arg...) do { } while (0) #define logging_init(foo) do { } while (0) #define logging_close(foo) do { } while (0) @@ -49,15 +48,6 @@ } while (0) #endif -/* Parser needs it's own debugging statement, we usually don't care about this at all */ -#ifdef DEBUG_PARSER -#undef dbg_parse -#define dbg_parse(format, arg...) \ - do { \ - log_message(LOG_DEBUG , "%s: " format , __FUNCTION__ , ## arg); \ - } while (0) -#endif - extern void log_message(int level, const char *format, ...) __attribute__ ((format (printf, 2, 3))); diff --git a/namedev.c b/namedev.c deleted file mode 100644 index fd1cf0458a..0000000000 --- a/namedev.c +++ /dev/null @@ -1,778 +0,0 @@ -/* - * namedev.c - * - * Userspace devfs - * - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-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 -#include -#include -#include - -#include "libsysfs/sysfs/libsysfs.h" -#include "list.h" -#include "udev_libc_wrapper.h" -#include "udev.h" -#include "udev_utils.h" -#include "udev_version.h" -#include "logging.h" -#include "namedev.h" -#include "udev_db.h" - -static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr); - -/* compare string with pattern (supports * ? [0-9] [!A-Z]) */ -static int strcmp_pattern(const char *p, const char *s) -{ - if (s[0] == '\0') { - while (p[0] == '*') - p++; - return (p[0] != '\0'); - } - switch (p[0]) { - case '[': - { - int not = 0; - p++; - if (p[0] == '!') { - not = 1; - p++; - } - while ((p[0] != '\0') && (p[0] != ']')) { - int match = 0; - if (p[1] == '-') { - if ((s[0] >= p[0]) && (s[0] <= p[2])) - match = 1; - p += 3; - } else { - match = (p[0] == s[0]); - p++; - } - if (match ^ not) { - while ((p[0] != '\0') && (p[0] != ']')) - p++; - if (p[0] == ']') - return strcmp_pattern(p+1, s+1); - } - } - } - break; - case '*': - if (strcmp_pattern(p, s+1)) - return strcmp_pattern(p+1, s); - return 0; - case '\0': - if (s[0] == '\0') { - return 0; - } - break; - default: - if ((p[0] == s[0]) || (p[0] == '?')) - return strcmp_pattern(p+1, s+1); - break; - } - return 1; -} - -/* extract possible {attr} and move str behind it */ -static char *get_format_attribute(char **str) -{ - char *pos; - char *attr = NULL; - - if (*str[0] == '{') { - pos = strchr(*str, '}'); - if (pos == NULL) { - dbg("missing closing brace for format"); - return NULL; - } - pos[0] = '\0'; - attr = *str+1; - *str = pos+1; - dbg("attribute='%s', str='%s'", attr, *str); - } - return attr; -} - -/* extract possible format length and move str behind it*/ -static int get_format_len(char **str) -{ - int num; - char *tail; - - if (isdigit(*str[0])) { - num = (int) strtoul(*str, &tail, 10); - if (num > 0) { - *str = tail; - dbg("format length=%i", num); - return num; - } else { - dbg("format parsing error '%s'", *str); - } - } - return -1; -} - -/** Finds the lowest positive N such that N isn't present in - * $(udevroot) either as a file or a symlink. - * - * @param name Name to check for - * @return 0 if didn't exist and N otherwise. - */ -static int find_free_number(struct udevice *udev, const char *name) -{ - char devpath[PATH_SIZE]; - char filename[PATH_SIZE]; - int num = 0; - - strlcpy(filename, name, sizeof(filename)); - while (1) { - dbg("look for existing node '%s'", filename); - if (udev_db_search_name(devpath, sizeof(devpath), filename) != 0) { - dbg("free num=%d", num); - return num; - } - - num++; - if (num > 1000) { - info("find_free_number gone crazy (num=%d), aborted", num); - return -1; - } - snprintf(filename, sizeof(filename), "%s%d", name, num); - filename[sizeof(filename)-1] = '\0'; - } -} - -static void apply_format(struct udevice *udev, char *string, size_t maxsize, - struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) -{ - char temp[PATH_SIZE]; - char temp2[PATH_SIZE]; - char *tail, *pos, *cpos, *attr, *rest; - int len; - int i; - char c; - struct sysfs_attribute *tmpattr; - unsigned int next_free_number; - struct sysfs_class_device *class_dev_parent; - - pos = string; - while (1) { - pos = strchr(pos, '%'); - if (pos == NULL) - break; - - pos[0] = '\0'; - tail = pos+1; - len = get_format_len(&tail); - c = tail[0]; - strlcpy(temp, tail+1, sizeof(temp)); - tail = temp; - dbg("format=%c, string='%s', tail='%s'",c , string, tail); - attr = get_format_attribute(&tail); - - switch (c) { - case 'p': - strlcat(string, udev->devpath, maxsize); - dbg("substitute kernel name '%s'", udev->kernel_name); - break; - case 'b': - strlcat(string, udev->bus_id, maxsize); - dbg("substitute bus_id '%s'", udev->bus_id); - break; - case 'k': - strlcat(string, udev->kernel_name, maxsize); - dbg("substitute kernel name '%s'", udev->kernel_name); - break; - case 'n': - strlcat(string, udev->kernel_number, maxsize); - dbg("substitute kernel number '%s'", udev->kernel_number); - break; - case 'm': - sprintf(temp2, "%d", minor(udev->devt)); - strlcat(string, temp2, maxsize); - dbg("substitute minor number '%s'", temp2); - break; - case 'M': - sprintf(temp2, "%d", major(udev->devt)); - strlcat(string, temp2, maxsize); - dbg("substitute major number '%s'", temp2); - break; - case 'c': - if (udev->program_result[0] == '\0') - break; - /* get part part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - dbg("request part #%d of result string", i); - cpos = udev->program_result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - } - if (i > 0) { - dbg("requested part of result string not found"); - break; - } - strlcpy(temp2, cpos, sizeof(temp2)); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(temp2, ' '); - if (cpos) - cpos[0] = '\0'; - } - strlcat(string, temp2, maxsize); - dbg("substitute part of result string '%s'", temp2); - } else { - strlcat(string, udev->program_result, maxsize); - dbg("substitute result string '%s'", udev->program_result); - } - break; - case 's': - if (!class_dev) - break; - if (attr == NULL) { - dbg("missing attribute"); - break; - } - tmpattr = find_sysfs_attribute(class_dev, sysfs_device, attr); - if (tmpattr == NULL) { - dbg("sysfa attribute '%s' not found", attr); - break; - } - /* strip trailing whitespace of matching value */ - if (isspace(tmpattr->value[strlen(tmpattr->value)-1])) { - i = len = strlen(tmpattr->value); - while (i > 0 && isspace(tmpattr->value[i-1])) - i--; - if (i < len) { - tmpattr->value[i] = '\0'; - dbg("remove %i trailing whitespace chars from '%s'", - len - i, tmpattr->value); - } - } - strlcat(string, tmpattr->value, maxsize); - dbg("substitute sysfs value '%s'", tmpattr->value); - break; - case '%': - strlcat(string, "%", maxsize); - pos++; - break; - case 'e': - next_free_number = find_free_number(udev, string); - if (next_free_number > 0) { - sprintf(temp2, "%d", next_free_number); - strlcat(string, temp2, maxsize); - } - break; - case 'P': - if (!class_dev) - break; - class_dev_parent = sysfs_get_classdev_parent(class_dev); - if (class_dev_parent != NULL) { - struct udevice udev_parent; - - dbg("found parent '%s', get the node name", class_dev_parent->path); - udev_init_device(&udev_parent, NULL, NULL); - /* lookup the name in the udev_db with the DEVPATH of the parent */ - if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) { - strlcat(string, udev_parent.name, maxsize); - dbg("substitute parent node name'%s'", udev_parent.name); - } else - dbg("parent not found in database"); - udev_cleanup_device(&udev_parent); - } - break; - case 'N': - if (udev->tmp_node[0] == '\0') { - dbg("create temporary device node for callout"); - snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", - udev_root, major(udev->devt), minor(udev->devt)); - udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; - udev_make_node(udev, udev->tmp_node, udev->devt, 0600, 0, 0); - } - strlcat(string, udev->tmp_node, maxsize); - dbg("substitute temporary device node name '%s'", udev->tmp_node); - break; - case 'r': - strlcat(string, udev_root, maxsize); - dbg("substitute udev_root '%s'", udev_root); - break; - default: - dbg("unknown substitution type '%%%c'", c); - break; - } - /* truncate to specified length */ - if (len > 0) - pos[len] = '\0'; - - strlcat(string, tail, maxsize); - } -} - -static int execute_program(struct udevice *udev, const char *path, char *value, int len) -{ - int retval; - int count; - int status; - int fds[2]; - pid_t pid; - char *pos; - char arg[PATH_SIZE]; - char *argv[(sizeof(arg) / 2) + 1]; - int i; - - strlcpy(arg, path, sizeof(arg)); - i = 0; - if (strchr(path, ' ')) { - pos = arg; - while (pos != NULL) { - if (pos[0] == '\'') { - /* don't separate if in apostrophes */ - pos++; - argv[i] = strsep(&pos, "\'"); - while (pos && pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - } - dbg("arg[%i] '%s'", i, argv[i]); - i++; - } - argv[i] = NULL; - dbg("execute '%s' with parsed arguments", arg); - } else { - argv[0] = arg; - argv[1] = udev->subsystem; - argv[2] = NULL; - dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); - } - - retval = pipe(fds); - if (retval != 0) { - dbg("pipe failed"); - return -1; - } - - pid = fork(); - switch(pid) { - case 0: - /* child */ - /* dup2 write side of pipe to STDOUT */ - dup2(fds[1], STDOUT_FILENO); - retval = execv(arg, argv); - - info(FIELD_PROGRAM " execution of '%s' failed", path); - exit(1); - case -1: - dbg("fork failed"); - return -1; - default: - /* parent reads from fds[0] */ - close(fds[1]); - retval = 0; - i = 0; - while (1) { - count = read(fds[0], value + i, len - i-1); - if (count <= 0) - break; - - i += count; - if (i >= len-1) { - dbg("result len %d too short", len); - retval = -1; - break; - } - } - - if (count < 0) { - dbg("read failed with '%s'", strerror(errno)); - retval = -1; - } - - if (i > 0 && value[i-1] == '\n') - i--; - value[i] = '\0'; - dbg("result is '%s'", value); - - close(fds[0]); - waitpid(pid, &status, 0); - - if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { - dbg("exec program status 0x%x", status); - retval = -1; - } - } - return retval; -} - -static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr) -{ - struct sysfs_attribute *tmpattr = NULL; - char *c; - - dbg("look for device attribute '%s'", attr); - /* try to find the attribute in the class device directory */ - tmpattr = sysfs_get_classdev_attr(class_dev, attr); - if (tmpattr) - goto attr_found; - - /* look in the class device directory if present */ - if (sysfs_device) { - tmpattr = sysfs_get_device_attr(sysfs_device, attr); - if (tmpattr) - goto attr_found; - } - - return NULL; - -attr_found: - c = strchr(tmpattr->value, '\n'); - if (c != NULL) - c[0] = '\0'; - - dbg("found attribute '%s'", tmpattr->path); - return tmpattr; -} - -static int compare_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair) -{ - struct sysfs_attribute *tmpattr; - int i; - int len; - - if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0')) - return -ENODEV; - - tmpattr = find_sysfs_attribute(class_dev, sysfs_device, pair->file); - if (tmpattr == NULL) - return -ENODEV; - - /* strip trailing whitespace of value, if not asked to match for it */ - if (! isspace(pair->value[strlen(pair->value)-1])) { - i = len = strlen(tmpattr->value); - while (i > 0 && isspace(tmpattr->value[i-1])) - i--; - if (i < len) { - tmpattr->value[i] = '\0'; - dbg("remove %i trailing whitespace chars from '%s'", - len - i, tmpattr->value); - } - } - - dbg("compare attribute '%s' value '%s' with '%s'", - pair->file, tmpattr->value, pair->value); - if (strcmp_pattern(pair->value, tmpattr->value) != 0) - return -ENODEV; - - dbg("found matching attribute '%s' with value '%s'", - pair->file, pair->value); - return 0; -} - -static int match_sysfs_pairs(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) -{ - struct sysfs_pair *pair; - int i; - - for (i = 0; i < MAX_SYSFS_PAIRS; ++i) { - pair = &dev->sysfs_pair[i]; - if ((pair->file[0] == '\0') || (pair->value[0] == '\0')) - break; - if (compare_sysfs_attribute(class_dev, sysfs_device, pair) != 0) { - dbg("sysfs attribute doesn't match"); - return -ENODEV; - } - } - - return 0; -} - -static int match_id(struct config_device *dev, struct sysfs_device *sysfs_device) -{ - char path[PATH_SIZE]; - char *temp; - - strlcpy(path, sysfs_device->path, sizeof(path)); - temp = strrchr(path, '/'); - temp++; - dbg("search '%s' in '%s', path='%s'", dev->id, temp, path); - if (strcmp_pattern(dev->id, temp) != 0) - return -ENODEV; - - return 0; -} - -static int match_rule(struct udevice *udev, struct config_device *dev, - struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) -{ - if (dev->kernel[0] != '\0') { - dbg("check for " FIELD_KERNEL " dev->kernel='%s' class_dev->name='%s'", - dev->kernel, class_dev->name); - if (strcmp_pattern(dev->kernel, class_dev->name) != 0) { - dbg(FIELD_KERNEL " is not matching"); - goto exit; - } - dbg(FIELD_KERNEL " matches"); - } - - if (dev->subsystem[0] != '\0') { - dbg("check for " FIELD_SUBSYSTEM " dev->subsystem='%s' class_dev->name='%s'", - dev->subsystem, class_dev->name); - if (strcmp_pattern(dev->subsystem, udev->subsystem) != 0) { - dbg(FIELD_SUBSYSTEM " is not matching"); - goto exit; - } - dbg(FIELD_SUBSYSTEM " matches"); - } - - /* walk up the chain of physical devices and find a match */ - while (1) { - /* check for matching driver */ - if (dev->driver[0] != '\0') { - if (sysfs_device == NULL) { - dbg("device has no sysfs_device"); - goto try_parent; - } - dbg("check for " FIELD_DRIVER " dev->driver='%s' sysfs_device->driver_name='%s'", - dev->driver, sysfs_device->driver_name); - if (strcmp_pattern(dev->driver, sysfs_device->driver_name) != 0) { - dbg(FIELD_DRIVER " is not matching"); - goto try_parent; - } - dbg(FIELD_DRIVER " matches"); - } - - /* check for matching bus value */ - if (dev->bus[0] != '\0') { - if (sysfs_device == NULL) { - dbg("device has no sysfs_device"); - goto try_parent; - } - dbg("check for " FIELD_BUS " dev->bus='%s' sysfs_device->bus='%s'", - dev->bus, sysfs_device->bus); - if (strcmp_pattern(dev->bus, sysfs_device->bus) != 0) { - dbg(FIELD_BUS " is not matching"); - goto try_parent; - } - dbg(FIELD_BUS " matches"); - } - - /* check for matching bus id */ - if (dev->id[0] != '\0') { - if (sysfs_device == NULL) { - dbg("device has no sysfs_device"); - goto try_parent; - } - dbg("check " FIELD_ID); - if (match_id(dev, sysfs_device) != 0) { - dbg(FIELD_ID " is not matching"); - goto try_parent; - } - dbg(FIELD_ID " matches"); - } - - /* check for matching sysfs pairs */ - if (dev->sysfs_pair[0].file[0] != '\0') { - dbg("check " FIELD_SYSFS " pairs"); - if (match_sysfs_pairs(dev, class_dev, sysfs_device) != 0) { - dbg(FIELD_SYSFS " is not matching"); - goto try_parent; - } - dbg(FIELD_SYSFS " matches"); - } - - /* found matching physical device */ - break; -try_parent: - dbg("try parent sysfs device"); - sysfs_device = sysfs_get_device_parent(sysfs_device); - if (sysfs_device == NULL) - goto exit; - dbg("sysfs_device->path='%s'", sysfs_device->path); - dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); - } - - /* execute external program */ - if (dev->program[0] != '\0') { - char program[PATH_SIZE]; - - dbg("check " FIELD_PROGRAM); - strlcpy(program, dev->program, sizeof(program)); - apply_format(udev, program, sizeof(program), class_dev, sysfs_device); - if (execute_program(udev, program, udev->program_result, sizeof(udev->program_result)) != 0) { - dbg(FIELD_PROGRAM " returned nonzero"); - goto try_parent; - } - dbg(FIELD_PROGRAM " returned successful"); - } - - /* check for matching result of external program */ - if (dev->result[0] != '\0') { - dbg("check for " FIELD_RESULT " dev->result='%s', udev->program_result='%s'", - dev->result, udev->program_result); - if (strcmp_pattern(dev->result, udev->program_result) != 0) { - dbg(FIELD_RESULT " is not matching"); - goto try_parent; - } - dbg(FIELD_RESULT " matches"); - } - - /* rule matches */ - return 0; - -exit: - return -1; -} - -int namedev_name_device(struct udevice *udev, struct sysfs_class_device *class_dev) -{ - struct sysfs_class_device *class_dev_parent; - struct sysfs_device *sysfs_device = NULL; - struct config_device *dev; - - dbg("class_dev->name='%s'", class_dev->name); - - /* Figure out where the "device"-symlink is at. For char devices this will - * always be in the class_dev->path. On block devices, only the main block - * device will have the device symlink in it's path. All partition devices - * need to look at the symlink in its parent directory. - */ - class_dev_parent = sysfs_get_classdev_parent(class_dev); - if (class_dev_parent != NULL) { - dbg("given class device has a parent, use this instead"); - sysfs_device = sysfs_get_classdev_device(class_dev_parent); - } else { - sysfs_device = sysfs_get_classdev_device(class_dev); - } - - if (sysfs_device) { - dbg("found devices device: path='%s', bus_id='%s', bus='%s'", - sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus); - strlcpy(udev->bus_id, sysfs_device->bus_id, sizeof(udev->bus_id)); - } - - dbg("udev->kernel_name='%s'", udev->kernel_name); - - /* look for a matching rule to apply */ - list_for_each_entry(dev, &config_device_list, node) { - dbg("process rule"); - if (match_rule(udev, dev, class_dev, sysfs_device) == 0) { - - /* apply options */ - if (dev->ignore_device) { - info("configured rule in '%s[%i]' applied, '%s' is ignored", - dev->config_file, dev->config_line, udev->kernel_name); - return -1; - } - if (dev->ignore_remove) { - udev->ignore_remove = 1; - dbg_parse("remove event should be ignored"); - } - /* apply all_partitions option only at a main block device */ - if (dev->partitions && udev->type == DEV_BLOCK && udev->kernel_number[0] == '\0') { - udev->partitions = dev->partitions; - dbg("creation of partition nodes requested"); - } - - /* apply permissions */ - if (dev->mode != 0000) { - udev->mode = dev->mode; - dbg("applied mode=%#o to '%s'", udev->mode, udev->kernel_name); - } - if (dev->owner[0] != '\0') { - strlcpy(udev->owner, dev->owner, sizeof(udev->owner)); - apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device); - dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name); - } - if (dev->group[0] != '\0') { - strlcpy(udev->group, dev->group, sizeof(udev->group)); - apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device); - dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name); - } - - /* collect symlinks */ - if (dev->symlink[0] != '\0') { - char temp[PATH_SIZE]; - char *pos, *next; - - info("configured rule in '%s[%i]' applied, added symlink '%s'", - dev->config_file, dev->config_line, dev->symlink); - strlcpy(temp, dev->symlink, sizeof(temp)); - apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); - - /* add multiple symlinks separated by spaces */ - pos = temp; - next = strchr(temp, ' '); - while (next) { - next[0] = '\0'; - dbg("add symlink '%s'", pos); - name_list_add(&udev->symlink_list, pos, 0); - pos = &next[1]; - next = strchr(pos, ' '); - } - dbg("add symlink '%s'", pos); - name_list_add(&udev->symlink_list, pos, 0); - } - - /* rule matches */ - if (dev->name[0] != '\0') { - info("configured rule in '%s[%i]' applied, '%s' becomes '%s'", - dev->config_file, dev->config_line, udev->kernel_name, dev->name); - - strlcpy(udev->name, dev->name, sizeof(udev->name)); - apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); - strlcpy(udev->config_file, dev->config_file, sizeof(udev->config_file)); - udev->config_line = dev->config_line; - - if (udev->type != DEV_NET) - dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i", - udev->name, udev->owner, udev->group, udev->mode, udev->partitions); - - break; - } - } - } - - if (udev->name[0] == '\0') { - /* no rule matched, so we use the kernel name */ - strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); - dbg("no rule found, use kernel name '%s'", udev->name); - } - - if (udev->tmp_node[0] != '\0') { - dbg("removing temporary device node"); - unlink_secure(udev->tmp_node); - udev->tmp_node[0] = '\0'; - } - - return 0; -} diff --git a/namedev.h b/namedev.h deleted file mode 100644 index d0235996bc..0000000000 --- a/namedev.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * namedev.h - * - * Userspace devfs - * - * Copyright (C) 2003,2004 Greg Kroah-Hartman - * - * 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. - * - */ - -#ifndef NAMEDEV_H -#define NAMEDEV_H - -#include "udev.h" -#include "list.h" - -struct sysfs_class_device; - -#define FIELD_KERNEL "KERNEL" -#define FIELD_SUBSYSTEM "SUBSYSTEM" -#define FIELD_BUS "BUS" -#define FIELD_SYSFS "SYSFS" -#define FIELD_ID "ID" -#define FIELD_PROGRAM "PROGRAM" -#define FIELD_RESULT "RESULT" -#define FIELD_DRIVER "DRIVER" -#define FIELD_NAME "NAME" -#define FIELD_SYMLINK "SYMLINK" -#define FIELD_OWNER "OWNER" -#define FIELD_GROUP "GROUP" -#define FIELD_MODE "MODE" -#define FIELD_OPTIONS "OPTIONS" - -#define OPTION_IGNORE_DEVICE "ignore_device" -#define OPTION_IGNORE_REMOVE "ignore_remove" -#define OPTION_PARTITIONS "all_partitions" - -#define MAX_SYSFS_PAIRS 5 - -#define RULEFILE_SUFFIX ".rules" - -struct sysfs_pair { - char file[PATH_SIZE]; - char value[VALUE_SIZE]; -}; - -struct config_device { - struct list_head node; - - char kernel[NAME_SIZE]; - char subsystem[NAME_SIZE]; - char bus[NAME_SIZE]; - char id[NAME_SIZE]; - struct sysfs_pair sysfs_pair[MAX_SYSFS_PAIRS]; - char program[PATH_SIZE]; - char result[PATH_SIZE]; - char driver[NAME_SIZE]; - char name[PATH_SIZE]; - char symlink[PATH_SIZE]; - - char owner[USER_SIZE]; - char group[USER_SIZE]; - mode_t mode; - - int partitions; - int ignore_device; - int ignore_remove; - - char config_file[PATH_SIZE]; - int config_line; -}; - -extern struct list_head config_device_list; - -extern int namedev_init(void); -extern int namedev_name_device(struct udevice *udev, struct sysfs_class_device *class_dev); -extern void namedev_close(void); - -extern void dump_config_dev(struct config_device *dev); -extern void dump_config_dev_list(void); - -#endif diff --git a/namedev_parse.c b/namedev_parse.c deleted file mode 100644 index 3be408932b..0000000000 --- a/namedev_parse.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * namedev_parse.c - * - * Userspace devfs - * - * Copyright (C) 2003,2004 Greg Kroah-Hartman - * Copyright (C) 2003-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. - * - */ - -#ifdef DEBUG -/* define this to enable parsing debugging also */ -/* #define DEBUG_PARSER */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev_libc_wrapper.h" -#include "udev.h" -#include "udev_utils.h" -#include "logging.h" -#include "namedev.h" - -LIST_HEAD(config_device_list); - -static int add_config_dev(struct config_device *new_dev) -{ - struct config_device *tmp_dev; - - tmp_dev = malloc(sizeof(*tmp_dev)); - if (tmp_dev == NULL) - return -ENOMEM; - memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); - list_add_tail(&tmp_dev->node, &config_device_list); - //dump_config_dev(tmp_dev); - return 0; -} - -void dump_config_dev(struct config_device *dev) -{ - dbg_parse("name='%s', symlink='%s', bus='%s', id='%s', " - "sysfs_file[0]='%s', sysfs_value[0]='%s', " - "kernel='%s', program='%s', result='%s'" - "owner='%s', group='%s', mode=%#o", - dev->name, dev->symlink, dev->bus, dev->id, - dev->sysfs_pair[0].file, dev->sysfs_pair[0].value, - dev->kernel, dev->program, dev->result, - dev->owner, dev->group, dev->mode); -} - -void dump_config_dev_list(void) -{ - struct config_device *dev; - - list_for_each_entry(dev, &config_device_list, node) - dump_config_dev(dev); -} - -/* extract possible KEY{attr} */ -static char *get_key_attribute(char *str) -{ - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - dbg("missing closing brace for format"); - return NULL; - } - pos[0] = '\0'; - dbg("attribute='%s'", attr); - return attr; - } - - return NULL; -} - -static int namedev_parse(struct udevice *udev, const char *filename) -{ - char line[LINE_SIZE]; - char *bufline; - int lineno; - char *temp; - char *temp2; - char *temp3; - char *attr; - char *buf; - size_t bufsize; - size_t cur; - size_t count; - int program_given = 0; - int valid; - int retval = 0; - struct config_device dev; - - if (file_map(filename, &buf, &bufsize) == 0) { - dbg("reading '%s' as rules file", filename); - } else { - dbg("can't open '%s' as rules file", filename); - return -1; - } - - /* loop through the whole file */ - cur = 0; - lineno = 0; - while (cur < bufsize) { - unsigned int i, j; - - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - if (count >= sizeof(line)) { - info("line too long, rule skipped %s, line %d", filename, lineno); - continue; - } - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == COMMENT_CHARACTER) - continue; - - /* skip backslash and newline from multi line rules */ - for (i = j = 0; i < count; i++) { - if (bufline[i] == '\\' && bufline[i+1] == '\n') - continue; - - line[j++] = bufline[i]; - } - line[j] = '\0'; - dbg_parse("read '%s'", line); - - /* get all known keys */ - memset(&dev, 0x00, sizeof(struct config_device)); - temp = line; - valid = 0; - - while (1) { - retval = parse_get_pair(&temp, &temp2, &temp3); - if (retval) - break; - - if (strcasecmp(temp2, FIELD_KERNEL) == 0) { - strlcpy(dev.kernel, temp3, sizeof(dev.kernel)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_SUBSYSTEM) == 0) { - strlcpy(dev.subsystem, temp3, sizeof(dev.subsystem)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_BUS) == 0) { - strlcpy(dev.bus, temp3, sizeof(dev.bus)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_ID) == 0) { - strlcpy(dev.id, temp3, sizeof(dev.id)); - valid = 1; - continue; - } - - if (strncasecmp(temp2, FIELD_SYSFS, sizeof(FIELD_SYSFS)-1) == 0) { - struct sysfs_pair *pair = &dev.sysfs_pair[0]; - int sysfs_pair_num = 0; - - /* find first unused pair */ - while (pair->file[0] != '\0') { - ++sysfs_pair_num; - if (sysfs_pair_num >= MAX_SYSFS_PAIRS) { - pair = NULL; - break; - } - ++pair; - } - if (pair) { - attr = get_key_attribute(temp2 + sizeof(FIELD_SYSFS)-1); - if (attr == NULL) { - dbg("error parsing " FIELD_SYSFS " attribute"); - continue; - } - strlcpy(pair->file, attr, sizeof(pair->file)); - strlcpy(pair->value, temp3, sizeof(pair->value)); - valid = 1; - } - continue; - } - - if (strcasecmp(temp2, FIELD_DRIVER) == 0) { - strlcpy(dev.driver, temp3, sizeof(dev.driver)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_PROGRAM) == 0) { - program_given = 1; - strlcpy(dev.program, temp3, sizeof(dev.program)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_RESULT) == 0) { - strlcpy(dev.result, temp3, sizeof(dev.result)); - valid = 1; - continue; - } - - if (strncasecmp(temp2, FIELD_NAME, sizeof(FIELD_NAME)-1) == 0) { - attr = get_key_attribute(temp2 + sizeof(FIELD_NAME)-1); - /* FIXME: remove old style options and make OPTIONS= mandatory */ - if (attr != NULL) { - if (strstr(attr, OPTION_PARTITIONS) != NULL) { - dbg_parse("creation of partition nodes requested"); - dev.partitions = DEFAULT_PARTITIONS_COUNT; - } - if (strstr(attr, OPTION_IGNORE_REMOVE) != NULL) { - dbg_parse("remove event should be ignored"); - dev.ignore_remove = 1; - } - } - if (temp3[0] != '\0') - strlcpy(dev.name, temp3, sizeof(dev.name)); - else - dev.ignore_device = 1; - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_SYMLINK) == 0) { - strlcpy(dev.symlink, temp3, sizeof(dev.symlink)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_OWNER) == 0) { - strlcpy(dev.owner, temp3, sizeof(dev.owner)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_GROUP) == 0) { - strlcpy(dev.group, temp3, sizeof(dev.group)); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_MODE) == 0) { - dev.mode = strtol(temp3, NULL, 8); - valid = 1; - continue; - } - - if (strcasecmp(temp2, FIELD_OPTIONS) == 0) { - if (strstr(temp3, OPTION_IGNORE_DEVICE) != NULL) { - dbg_parse("device should be ignored"); - dev.ignore_device = 1; - } - if (strstr(temp3, OPTION_IGNORE_REMOVE) != NULL) { - dbg_parse("remove event should be ignored"); - dev.ignore_remove = 1; - } - if (strstr(temp3, OPTION_PARTITIONS) != NULL) { - dbg_parse("creation of partition nodes requested"); - dev.partitions = DEFAULT_PARTITIONS_COUNT; - } - valid = 1; - continue; - } - - dbg("unknown type of field '%s'", temp2); - goto error; - } - - /* skip line if not any valid key was found */ - if (!valid) - goto error; - - /* simple plausibility checks for given keys */ - if ((dev.sysfs_pair[0].file[0] == '\0') ^ - (dev.sysfs_pair[0].value[0] == '\0')) { - info("inconsistency in " FIELD_SYSFS " key"); - goto error; - } - - if ((dev.result[0] != '\0') && (program_given == 0)) { - info(FIELD_RESULT " is only useful when " - FIELD_PROGRAM " is called in any rule before"); - goto error; - } - - dev.config_line = lineno; - strlcpy(dev.config_file, filename, sizeof(dev.config_file)); - retval = add_config_dev(&dev); - if (retval) { - dbg("add_config_dev returned with error %d", retval); - continue; -error: - info("parse error %s, line %d:%d, rule skipped", - filename, lineno, (int) (temp - line)); - } - } - - file_unmap(buf, bufsize); - return retval; -} - -int namedev_init(void) -{ - struct stat stats; - int retval; - - if (stat(udev_rules_filename, &stats) != 0) - return -1; - - if ((stats.st_mode & S_IFMT) != S_IFDIR) - retval = namedev_parse(NULL, udev_rules_filename); - else - retval = call_foreach_file(namedev_parse, NULL, udev_rules_filename, RULEFILE_SUFFIX); - - return retval; -} - -void namedev_close(void) -{ - struct config_device *dev; - struct config_device *temp_dev; - - list_for_each_entry_safe(dev, temp_dev, &config_device_list, node) { - list_del(&dev->node); - free(dev); - } -} - diff --git a/udev.c b/udev.c index 8ce813fbf5..069cecf973 100644 --- a/udev.c +++ b/udev.c @@ -37,7 +37,7 @@ #include "udev_utils.h" #include "udev_sysfs.h" #include "udev_version.h" -#include "namedev.h" +#include "udev_rules.h" #include "logging.h" @@ -177,7 +177,7 @@ int main(int argc, char *argv[], char *envp[]) wait_for_class_device(class_dev, &error); /* init rules */ - namedev_init(); + udev_rules_init(); /* name, create node, store in db */ retval = udev_add_device(&udev, class_dev); diff --git a/udev_add.c b/udev_add.c index bf5feb1b2d..8443f65e7c 100644 --- a/udev_add.c +++ b/udev_add.c @@ -42,7 +42,7 @@ #include "udev_sysfs.h" #include "udev_version.h" #include "logging.h" -#include "namedev.h" +#include "udev_rules.h" #include "udev_db.h" #include "udev_selinux.h" @@ -276,7 +276,7 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev) } } - if (namedev_name_device(udev, class_dev) != 0) + if (udev_rules_get_name(udev, class_dev) != 0) return 0; dbg("adding name='%s'", udev->name); diff --git a/udev_config.c b/udev_config.c index 26ecf6291c..77ac38a791 100644 --- a/udev_config.c +++ b/udev_config.c @@ -21,9 +21,6 @@ * */ -/* define this to enable parsing debugging */ -/* #define DEBUG_PARSER */ - #include #include #include @@ -38,7 +35,7 @@ #include "udev_utils.h" #include "udev_version.h" #include "logging.h" -#include "namedev.h" +#include "udev_rules.h" /* global variables */ char sysfs_path[PATH_SIZE]; @@ -134,14 +131,14 @@ static int parse_config_file(void) strlcpy(line, bufline, count); temp = line; - dbg_parse("read '%s'", temp); + dbg("read '%s'", temp); retval = parse_get_pair(&temp, &variable, &value); if (retval != 0) info("%s:%d:%Zd: error parsing '%s'", udev_config_filename, lineno, temp-line, temp); - dbg_parse("variable='%s', value='%s'", variable, value); + dbg("variable='%s', value='%s'", variable, value); if (strcasecmp(variable, "udev_root") == 0) { strlcpy(udev_root, value, sizeof(udev_root)); diff --git a/udev_remove.c b/udev_remove.c index a81d20c98c..0a9996dc6a 100644 --- a/udev_remove.c +++ b/udev_remove.c @@ -34,7 +34,6 @@ #include "udev.h" #include "udev_utils.h" #include "udev_version.h" -#include "namedev.h" #include "udev_db.h" #include "logging.h" diff --git a/udev_rules.c b/udev_rules.c new file mode 100644 index 0000000000..cbdf7de317 --- /dev/null +++ b/udev_rules.c @@ -0,0 +1,778 @@ +/* + * udev_rules.c + * + * Userspace devfs + * + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-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 +#include +#include +#include + +#include "libsysfs/sysfs/libsysfs.h" +#include "list.h" +#include "udev_libc_wrapper.h" +#include "udev.h" +#include "udev_utils.h" +#include "udev_version.h" +#include "logging.h" +#include "udev_rules.h" +#include "udev_db.h" + +static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr); + +/* compare string with pattern (supports * ? [0-9] [!A-Z]) */ +static int strcmp_pattern(const char *p, const char *s) +{ + if (s[0] == '\0') { + while (p[0] == '*') + p++; + return (p[0] != '\0'); + } + switch (p[0]) { + case '[': + { + int not = 0; + p++; + if (p[0] == '!') { + not = 1; + p++; + } + while ((p[0] != '\0') && (p[0] != ']')) { + int match = 0; + if (p[1] == '-') { + if ((s[0] >= p[0]) && (s[0] <= p[2])) + match = 1; + p += 3; + } else { + match = (p[0] == s[0]); + p++; + } + if (match ^ not) { + while ((p[0] != '\0') && (p[0] != ']')) + p++; + if (p[0] == ']') + return strcmp_pattern(p+1, s+1); + } + } + } + break; + case '*': + if (strcmp_pattern(p, s+1)) + return strcmp_pattern(p+1, s); + return 0; + case '\0': + if (s[0] == '\0') { + return 0; + } + break; + default: + if ((p[0] == s[0]) || (p[0] == '?')) + return strcmp_pattern(p+1, s+1); + break; + } + return 1; +} + +/* extract possible {attr} and move str behind it */ +static char *get_format_attribute(char **str) +{ + char *pos; + char *attr = NULL; + + if (*str[0] == '{') { + pos = strchr(*str, '}'); + if (pos == NULL) { + dbg("missing closing brace for format"); + return NULL; + } + pos[0] = '\0'; + attr = *str+1; + *str = pos+1; + dbg("attribute='%s', str='%s'", attr, *str); + } + return attr; +} + +/* extract possible format length and move str behind it*/ +static int get_format_len(char **str) +{ + int num; + char *tail; + + if (isdigit(*str[0])) { + num = (int) strtoul(*str, &tail, 10); + if (num > 0) { + *str = tail; + dbg("format length=%i", num); + return num; + } else { + dbg("format parsing error '%s'", *str); + } + } + return -1; +} + +/** Finds the lowest positive N such that N isn't present in + * $(udevroot) either as a file or a symlink. + * + * @param name Name to check for + * @return 0 if didn't exist and N otherwise. + */ +static int find_free_number(struct udevice *udev, const char *name) +{ + char devpath[PATH_SIZE]; + char filename[PATH_SIZE]; + int num = 0; + + strlcpy(filename, name, sizeof(filename)); + while (1) { + dbg("look for existing node '%s'", filename); + if (udev_db_search_name(devpath, sizeof(devpath), filename) != 0) { + dbg("free num=%d", num); + return num; + } + + num++; + if (num > 1000) { + info("find_free_number gone crazy (num=%d), aborted", num); + return -1; + } + snprintf(filename, sizeof(filename), "%s%d", name, num); + filename[sizeof(filename)-1] = '\0'; + } +} + +static void apply_format(struct udevice *udev, char *string, size_t maxsize, + struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) +{ + char temp[PATH_SIZE]; + char temp2[PATH_SIZE]; + char *tail, *pos, *cpos, *attr, *rest; + int len; + int i; + char c; + struct sysfs_attribute *tmpattr; + unsigned int next_free_number; + struct sysfs_class_device *class_dev_parent; + + pos = string; + while (1) { + pos = strchr(pos, '%'); + if (pos == NULL) + break; + + pos[0] = '\0'; + tail = pos+1; + len = get_format_len(&tail); + c = tail[0]; + strlcpy(temp, tail+1, sizeof(temp)); + tail = temp; + dbg("format=%c, string='%s', tail='%s'",c , string, tail); + attr = get_format_attribute(&tail); + + switch (c) { + case 'p': + strlcat(string, udev->devpath, maxsize); + dbg("substitute kernel name '%s'", udev->kernel_name); + break; + case 'b': + strlcat(string, udev->bus_id, maxsize); + dbg("substitute bus_id '%s'", udev->bus_id); + break; + case 'k': + strlcat(string, udev->kernel_name, maxsize); + dbg("substitute kernel name '%s'", udev->kernel_name); + break; + case 'n': + strlcat(string, udev->kernel_number, maxsize); + dbg("substitute kernel number '%s'", udev->kernel_number); + break; + case 'm': + sprintf(temp2, "%d", minor(udev->devt)); + strlcat(string, temp2, maxsize); + dbg("substitute minor number '%s'", temp2); + break; + case 'M': + sprintf(temp2, "%d", major(udev->devt)); + strlcat(string, temp2, maxsize); + dbg("substitute major number '%s'", temp2); + break; + case 'c': + if (udev->program_result[0] == '\0') + break; + /* get part part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + dbg("request part #%d of result string", i); + cpos = udev->program_result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + } + if (i > 0) { + dbg("requested part of result string not found"); + break; + } + strlcpy(temp2, cpos, sizeof(temp2)); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(temp2, ' '); + if (cpos) + cpos[0] = '\0'; + } + strlcat(string, temp2, maxsize); + dbg("substitute part of result string '%s'", temp2); + } else { + strlcat(string, udev->program_result, maxsize); + dbg("substitute result string '%s'", udev->program_result); + } + break; + case 's': + if (!class_dev) + break; + if (attr == NULL) { + dbg("missing attribute"); + break; + } + tmpattr = find_sysfs_attribute(class_dev, sysfs_device, attr); + if (tmpattr == NULL) { + dbg("sysfa attribute '%s' not found", attr); + break; + } + /* strip trailing whitespace of matching value */ + if (isspace(tmpattr->value[strlen(tmpattr->value)-1])) { + i = len = strlen(tmpattr->value); + while (i > 0 && isspace(tmpattr->value[i-1])) + i--; + if (i < len) { + tmpattr->value[i] = '\0'; + dbg("remove %i trailing whitespace chars from '%s'", + len - i, tmpattr->value); + } + } + strlcat(string, tmpattr->value, maxsize); + dbg("substitute sysfs value '%s'", tmpattr->value); + break; + case '%': + strlcat(string, "%", maxsize); + pos++; + break; + case 'e': + next_free_number = find_free_number(udev, string); + if (next_free_number > 0) { + sprintf(temp2, "%d", next_free_number); + strlcat(string, temp2, maxsize); + } + break; + case 'P': + if (!class_dev) + break; + class_dev_parent = sysfs_get_classdev_parent(class_dev); + if (class_dev_parent != NULL) { + struct udevice udev_parent; + + dbg("found parent '%s', get the node name", class_dev_parent->path); + udev_init_device(&udev_parent, NULL, NULL); + /* lookup the name in the udev_db with the DEVPATH of the parent */ + if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) { + strlcat(string, udev_parent.name, maxsize); + dbg("substitute parent node name'%s'", udev_parent.name); + } else + dbg("parent not found in database"); + udev_cleanup_device(&udev_parent); + } + break; + case 'N': + if (udev->tmp_node[0] == '\0') { + dbg("create temporary device node for callout"); + snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", + udev_root, major(udev->devt), minor(udev->devt)); + udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; + udev_make_node(udev, udev->tmp_node, udev->devt, 0600, 0, 0); + } + strlcat(string, udev->tmp_node, maxsize); + dbg("substitute temporary device node name '%s'", udev->tmp_node); + break; + case 'r': + strlcat(string, udev_root, maxsize); + dbg("substitute udev_root '%s'", udev_root); + break; + default: + dbg("unknown substitution type '%%%c'", c); + break; + } + /* truncate to specified length */ + if (len > 0) + pos[len] = '\0'; + + strlcat(string, tail, maxsize); + } +} + +static int execute_program(struct udevice *udev, const char *path, char *value, int len) +{ + int retval; + int count; + int status; + int fds[2]; + pid_t pid; + char *pos; + char arg[PATH_SIZE]; + char *argv[(sizeof(arg) / 2) + 1]; + int i; + + strlcpy(arg, path, sizeof(arg)); + i = 0; + if (strchr(path, ' ')) { + pos = arg; + while (pos != NULL) { + if (pos[0] == '\'') { + /* don't separate if in apostrophes */ + pos++; + argv[i] = strsep(&pos, "\'"); + while (pos && pos[0] == ' ') + pos++; + } else { + argv[i] = strsep(&pos, " "); + } + dbg("arg[%i] '%s'", i, argv[i]); + i++; + } + argv[i] = NULL; + dbg("execute '%s' with parsed arguments", arg); + } else { + argv[0] = arg; + argv[1] = udev->subsystem; + argv[2] = NULL; + dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); + } + + retval = pipe(fds); + if (retval != 0) { + dbg("pipe failed"); + return -1; + } + + pid = fork(); + switch(pid) { + case 0: + /* child */ + /* dup2 write side of pipe to STDOUT */ + dup2(fds[1], STDOUT_FILENO); + retval = execv(arg, argv); + + info(FIELD_PROGRAM " execution of '%s' failed", path); + exit(1); + case -1: + dbg("fork failed"); + return -1; + default: + /* parent reads from fds[0] */ + close(fds[1]); + retval = 0; + i = 0; + while (1) { + count = read(fds[0], value + i, len - i-1); + if (count <= 0) + break; + + i += count; + if (i >= len-1) { + dbg("result len %d too short", len); + retval = -1; + break; + } + } + + if (count < 0) { + dbg("read failed with '%s'", strerror(errno)); + retval = -1; + } + + if (i > 0 && value[i-1] == '\n') + i--; + value[i] = '\0'; + dbg("result is '%s'", value); + + close(fds[0]); + waitpid(pid, &status, 0); + + if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { + dbg("exec program status 0x%x", status); + retval = -1; + } + } + return retval; +} + +static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr) +{ + struct sysfs_attribute *tmpattr = NULL; + char *c; + + dbg("look for device attribute '%s'", attr); + /* try to find the attribute in the class device directory */ + tmpattr = sysfs_get_classdev_attr(class_dev, attr); + if (tmpattr) + goto attr_found; + + /* look in the class device directory if present */ + if (sysfs_device) { + tmpattr = sysfs_get_device_attr(sysfs_device, attr); + if (tmpattr) + goto attr_found; + } + + return NULL; + +attr_found: + c = strchr(tmpattr->value, '\n'); + if (c != NULL) + c[0] = '\0'; + + dbg("found attribute '%s'", tmpattr->path); + return tmpattr; +} + +static int compare_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair) +{ + struct sysfs_attribute *tmpattr; + int i; + int len; + + if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0')) + return -ENODEV; + + tmpattr = find_sysfs_attribute(class_dev, sysfs_device, pair->file); + if (tmpattr == NULL) + return -ENODEV; + + /* strip trailing whitespace of value, if not asked to match for it */ + if (! isspace(pair->value[strlen(pair->value)-1])) { + i = len = strlen(tmpattr->value); + while (i > 0 && isspace(tmpattr->value[i-1])) + i--; + if (i < len) { + tmpattr->value[i] = '\0'; + dbg("remove %i trailing whitespace chars from '%s'", + len - i, tmpattr->value); + } + } + + dbg("compare attribute '%s' value '%s' with '%s'", + pair->file, tmpattr->value, pair->value); + if (strcmp_pattern(pair->value, tmpattr->value) != 0) + return -ENODEV; + + dbg("found matching attribute '%s' with value '%s'", + pair->file, pair->value); + return 0; +} + +static int match_sysfs_pairs(struct udev_rule *rule, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) +{ + struct sysfs_pair *pair; + int i; + + for (i = 0; i < MAX_SYSFS_PAIRS; ++i) { + pair = &rule->sysfs_pair[i]; + if ((pair->file[0] == '\0') || (pair->value[0] == '\0')) + break; + if (compare_sysfs_attribute(class_dev, sysfs_device, pair) != 0) { + dbg("sysfs attribute doesn't match"); + return -ENODEV; + } + } + + return 0; +} + +static int match_id(struct udev_rule *rule, struct sysfs_device *sysfs_device) +{ + char path[PATH_SIZE]; + char *temp; + + strlcpy(path, sysfs_device->path, sizeof(path)); + temp = strrchr(path, '/'); + temp++; + dbg("search '%s' in '%s', path='%s'", rule->id, temp, path); + if (strcmp_pattern(rule->id, temp) != 0) + return -ENODEV; + + return 0; +} + +static int match_rule(struct udevice *udev, struct udev_rule *rule, + struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) +{ + if (rule->kernel[0] != '\0') { + dbg("check for " FIELD_KERNEL " rule->kernel='%s' class_dev->name='%s'", + rule->kernel, class_dev->name); + if (strcmp_pattern(rule->kernel, class_dev->name) != 0) { + dbg(FIELD_KERNEL " is not matching"); + goto exit; + } + dbg(FIELD_KERNEL " matches"); + } + + if (rule->subsystem[0] != '\0') { + dbg("check for " FIELD_SUBSYSTEM " rule->subsystem='%s' class_dev->name='%s'", + rule->subsystem, class_dev->name); + if (strcmp_pattern(rule->subsystem, udev->subsystem) != 0) { + dbg(FIELD_SUBSYSTEM " is not matching"); + goto exit; + } + dbg(FIELD_SUBSYSTEM " matches"); + } + + /* walk up the chain of physical devices and find a match */ + while (1) { + /* check for matching driver */ + if (rule->driver[0] != '\0') { + if (sysfs_device == NULL) { + dbg("device has no sysfs_device"); + goto try_parent; + } + dbg("check for " FIELD_DRIVER " rule->driver='%s' sysfs_device->driver_name='%s'", + rule->driver, sysfs_device->driver_name); + if (strcmp_pattern(rule->driver, sysfs_device->driver_name) != 0) { + dbg(FIELD_DRIVER " is not matching"); + goto try_parent; + } + dbg(FIELD_DRIVER " matches"); + } + + /* check for matching bus value */ + if (rule->bus[0] != '\0') { + if (sysfs_device == NULL) { + dbg("device has no sysfs_device"); + goto try_parent; + } + dbg("check for " FIELD_BUS " rule->bus='%s' sysfs_device->bus='%s'", + rule->bus, sysfs_device->bus); + if (strcmp_pattern(rule->bus, sysfs_device->bus) != 0) { + dbg(FIELD_BUS " is not matching"); + goto try_parent; + } + dbg(FIELD_BUS " matches"); + } + + /* check for matching bus id */ + if (rule->id[0] != '\0') { + if (sysfs_device == NULL) { + dbg("device has no sysfs_device"); + goto try_parent; + } + dbg("check " FIELD_ID); + if (match_id(rule, sysfs_device) != 0) { + dbg(FIELD_ID " is not matching"); + goto try_parent; + } + dbg(FIELD_ID " matches"); + } + + /* check for matching sysfs pairs */ + if (rule->sysfs_pair[0].file[0] != '\0') { + dbg("check " FIELD_SYSFS " pairs"); + if (match_sysfs_pairs(rule, class_dev, sysfs_device) != 0) { + dbg(FIELD_SYSFS " is not matching"); + goto try_parent; + } + dbg(FIELD_SYSFS " matches"); + } + + /* found matching physical device */ + break; +try_parent: + dbg("try parent sysfs device"); + sysfs_device = sysfs_get_device_parent(sysfs_device); + if (sysfs_device == NULL) + goto exit; + dbg("sysfs_device->path='%s'", sysfs_device->path); + dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); + } + + /* execute external program */ + if (rule->program[0] != '\0') { + char program[PATH_SIZE]; + + dbg("check " FIELD_PROGRAM); + strlcpy(program, rule->program, sizeof(program)); + apply_format(udev, program, sizeof(program), class_dev, sysfs_device); + if (execute_program(udev, program, udev->program_result, sizeof(udev->program_result)) != 0) { + dbg(FIELD_PROGRAM " returned nonzero"); + goto try_parent; + } + dbg(FIELD_PROGRAM " returned successful"); + } + + /* check for matching result of external program */ + if (rule->result[0] != '\0') { + dbg("check for " FIELD_RESULT "rule->result='%s', udev->program_result='%s'", + rule->result, udev->program_result); + if (strcmp_pattern(rule->result, udev->program_result) != 0) { + dbg(FIELD_RESULT " is not matching"); + goto try_parent; + } + dbg(FIELD_RESULT " matches"); + } + + /* rule matches */ + return 0; + +exit: + return -1; +} + +int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev) +{ + struct sysfs_class_device *class_dev_parent; + struct sysfs_device *sysfs_device = NULL; + struct udev_rule *rule; + + dbg("class_dev->name='%s'", class_dev->name); + + /* Figure out where the "device"-symlink is at. For char devices this will + * always be in the class_dev->path. On block devices, only the main block + * device will have the device symlink in it's path. All partition devices + * need to look at the symlink in its parent directory. + */ + class_dev_parent = sysfs_get_classdev_parent(class_dev); + if (class_dev_parent != NULL) { + dbg("given class device has a parent, use this instead"); + sysfs_device = sysfs_get_classdev_device(class_dev_parent); + } else { + sysfs_device = sysfs_get_classdev_device(class_dev); + } + + if (sysfs_device) { + dbg("found devices device: path='%s', bus_id='%s', bus='%s'", + sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus); + strlcpy(udev->bus_id, sysfs_device->bus_id, sizeof(udev->bus_id)); + } + + dbg("udev->kernel_name='%s'", udev->kernel_name); + + /* look for a matching rule to apply */ + list_for_each_entry(rule, &udev_rule_list, node) { + dbg("process rule"); + if (match_rule(udev, rule, class_dev, sysfs_device) == 0) { + + /* apply options */ + if (rule->ignore_device) { + info("configured rule in '%s[%i]' applied, '%s' is ignored", + rule->config_file, rule->config_line, udev->kernel_name); + return -1; + } + if (rule->ignore_remove) { + udev->ignore_remove = 1; + dbg("remove event should be ignored"); + } + /* apply all_partitions option only at a main block device */ + if (rule->partitions && udev->type == DEV_BLOCK && udev->kernel_number[0] == '\0') { + udev->partitions = rule->partitions; + dbg("creation of partition nodes requested"); + } + + /* apply permissions */ + if (rule->mode != 0000) { + udev->mode = rule->mode; + dbg("applied mode=%#o to '%s'", udev->mode, udev->kernel_name); + } + if (rule->owner[0] != '\0') { + strlcpy(udev->owner, rule->owner, sizeof(udev->owner)); + apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device); + dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name); + } + if (rule->group[0] != '\0') { + strlcpy(udev->group, rule->group, sizeof(udev->group)); + apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device); + dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name); + } + + /* collect symlinks */ + if (rule->symlink[0] != '\0') { + char temp[PATH_SIZE]; + char *pos, *next; + + info("configured rule in '%s[%i]' applied, added symlink '%s'", + rule->config_file, rule->config_line, rule->symlink); + strlcpy(temp, rule->symlink, sizeof(temp)); + apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); + + /* add multiple symlinks separated by spaces */ + pos = temp; + next = strchr(temp, ' '); + while (next) { + next[0] = '\0'; + dbg("add symlink '%s'", pos); + name_list_add(&udev->symlink_list, pos, 0); + pos = &next[1]; + next = strchr(pos, ' '); + } + dbg("add symlink '%s'", pos); + name_list_add(&udev->symlink_list, pos, 0); + } + + /* rule matches */ + if (rule->name[0] != '\0') { + info("configured rule in '%s[%i]' applied, '%s' becomes '%s'", + rule->config_file, rule->config_line, udev->kernel_name, rule->name); + + strlcpy(udev->name, rule->name, sizeof(udev->name)); + apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); + strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file)); + udev->config_line = rule->config_line; + + if (udev->type != DEV_NET) + dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i", + udev->name, udev->owner, udev->group, udev->mode, udev->partitions); + + break; + } + } + } + + if (udev->name[0] == '\0') { + /* no rule matched, so we use the kernel name */ + strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); + dbg("no rule found, use kernel name '%s'", udev->name); + } + + if (udev->tmp_node[0] != '\0') { + dbg("removing temporary device node"); + unlink_secure(udev->tmp_node); + udev->tmp_node[0] = '\0'; + } + + return 0; +} diff --git a/udev_rules.h b/udev_rules.h new file mode 100644 index 0000000000..71c510bce5 --- /dev/null +++ b/udev_rules.h @@ -0,0 +1,94 @@ +/* + * udev_rules.h + * + * Userspace devfs + * + * Copyright (C) 2003,2004 Greg Kroah-Hartman + * + * 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. + * + */ + +#ifndef NAMEDEV_H +#define NAMEDEV_H + +#include "libsysfs/sysfs/libsysfs.h" +#include "udev.h" +#include "list.h" + + +#define FIELD_KERNEL "KERNEL" +#define FIELD_SUBSYSTEM "SUBSYSTEM" +#define FIELD_BUS "BUS" +#define FIELD_SYSFS "SYSFS" +#define FIELD_ID "ID" +#define FIELD_PROGRAM "PROGRAM" +#define FIELD_RESULT "RESULT" +#define FIELD_DRIVER "DRIVER" +#define FIELD_NAME "NAME" +#define FIELD_SYMLINK "SYMLINK" +#define FIELD_OWNER "OWNER" +#define FIELD_GROUP "GROUP" +#define FIELD_MODE "MODE" +#define FIELD_OPTIONS "OPTIONS" + +#define OPTION_IGNORE_DEVICE "ignore_device" +#define OPTION_IGNORE_REMOVE "ignore_remove" +#define OPTION_PARTITIONS "all_partitions" + +#define MAX_SYSFS_PAIRS 5 + +#define RULEFILE_SUFFIX ".rules" + +struct sysfs_pair { + char file[PATH_SIZE]; + char value[VALUE_SIZE]; +}; + +struct udev_rule { + struct list_head node; + + char kernel[NAME_SIZE]; + char subsystem[NAME_SIZE]; + char bus[NAME_SIZE]; + char id[NAME_SIZE]; + struct sysfs_pair sysfs_pair[MAX_SYSFS_PAIRS]; + char program[PATH_SIZE]; + char result[PATH_SIZE]; + char driver[NAME_SIZE]; + char name[PATH_SIZE]; + char symlink[PATH_SIZE]; + + char owner[USER_SIZE]; + char group[USER_SIZE]; + mode_t mode; + + int partitions; + int ignore_device; + int ignore_remove; + + char config_file[PATH_SIZE]; + 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 void udev_rules_close(void); + +extern void udev_rule_dump(struct udev_rule *rule); +extern void udev_rule_list_dump(void); + +#endif diff --git a/udev_rules_parse.c b/udev_rules_parse.c new file mode 100644 index 0000000000..4f431decf5 --- /dev/null +++ b/udev_rules_parse.c @@ -0,0 +1,365 @@ +/* + * udev_rules_parse.c + * + * Userspace devfs + * + * Copyright (C) 2003,2004 Greg Kroah-Hartman + * Copyright (C) 2003-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 +#include + +#include "udev_libc_wrapper.h" +#include "udev.h" +#include "udev_utils.h" +#include "logging.h" +#include "udev_rules.h" + +LIST_HEAD(udev_rule_list); + +static int add_config_dev(struct udev_rule *new_rule) +{ + struct udev_rule *tmp_rule; + + tmp_rule = malloc(sizeof(*tmp_rule)); + if (tmp_rule == NULL) + return -ENOMEM; + memcpy(tmp_rule, new_rule, sizeof(*tmp_rule)); + list_add_tail(&tmp_rule->node, &udev_rule_list); + udev_rule_dump(tmp_rule); + + return 0; +} + +void udev_rule_dump(struct udev_rule *rule) +{ + dbg("name='%s', symlink='%s', bus='%s', id='%s', " + "sysfs_file[0]='%s', sysfs_value[0]='%s', " + "kernel='%s', program='%s', result='%s'" + "owner='%s', group='%s', mode=%#o", + rule->name, rule->symlink, rule->bus, rule->id, + rule->sysfs_pair[0].file, rule->sysfs_pair[0].value, + rule->kernel, rule->program, rule->result, + rule->owner, rule->group, rule->mode); +} + +void udev_rule_list_dump(void) +{ + struct udev_rule *rule; + + list_for_each_entry(rule, &udev_rule_list, node) + udev_rule_dump(rule); +} + +/* extract possible KEY{attr} */ +static char *get_key_attribute(char *str) +{ + char *pos; + char *attr; + + attr = strchr(str, '{'); + if (attr != NULL) { + attr++; + pos = strchr(attr, '}'); + if (pos == NULL) { + dbg("missing closing brace for format"); + return NULL; + } + pos[0] = '\0'; + dbg("attribute='%s'", attr); + return attr; + } + + return NULL; +} + +static int rules_parse(struct udevice *udev, const char *filename) +{ + char line[LINE_SIZE]; + char *bufline; + int lineno; + char *temp; + char *temp2; + char *temp3; + char *attr; + char *buf; + size_t bufsize; + size_t cur; + size_t count; + int program_given = 0; + int valid; + int retval = 0; + struct udev_rule rule; + + if (file_map(filename, &buf, &bufsize) == 0) { + dbg("reading '%s' as rules file", filename); + } else { + dbg("can't open '%s' as rules file", filename); + return -1; + } + + /* loop through the whole file */ + cur = 0; + lineno = 0; + while (cur < bufsize) { + unsigned int i, j; + + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + if (count >= sizeof(line)) { + info("line too long, rule skipped %s, line %d", filename, lineno); + continue; + } + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == COMMENT_CHARACTER) + continue; + + /* skip backslash and newline from multi line rules */ + for (i = j = 0; i < count; i++) { + if (bufline[i] == '\\' && bufline[i+1] == '\n') + continue; + + line[j++] = bufline[i]; + } + line[j] = '\0'; + dbg("read '%s'", line); + + /* get all known keys */ + memset(&rule, 0x00, sizeof(struct udev_rule)); + temp = line; + valid = 0; + + while (1) { + retval = parse_get_pair(&temp, &temp2, &temp3); + if (retval) + break; + + if (strcasecmp(temp2, FIELD_KERNEL) == 0) { + strlcpy(rule.kernel, temp3, sizeof(rule.kernel)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_SUBSYSTEM) == 0) { + strlcpy(rule.subsystem, temp3, sizeof(rule.subsystem)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_BUS) == 0) { + strlcpy(rule.bus, temp3, sizeof(rule.bus)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_ID) == 0) { + strlcpy(rule.id, temp3, sizeof(rule.id)); + valid = 1; + continue; + } + + if (strncasecmp(temp2, FIELD_SYSFS, sizeof(FIELD_SYSFS)-1) == 0) { + struct sysfs_pair *pair = &rule.sysfs_pair[0]; + int sysfs_pair_num = 0; + + /* find first unused pair */ + while (pair->file[0] != '\0') { + ++sysfs_pair_num; + if (sysfs_pair_num >= MAX_SYSFS_PAIRS) { + pair = NULL; + break; + } + ++pair; + } + if (pair) { + attr = get_key_attribute(temp2 + sizeof(FIELD_SYSFS)-1); + if (attr == NULL) { + dbg("error parsing " FIELD_SYSFS " attribute"); + continue; + } + strlcpy(pair->file, attr, sizeof(pair->file)); + strlcpy(pair->value, temp3, sizeof(pair->value)); + valid = 1; + } + continue; + } + + if (strcasecmp(temp2, FIELD_DRIVER) == 0) { + strlcpy(rule.driver, temp3, sizeof(rule.driver)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_PROGRAM) == 0) { + program_given = 1; + strlcpy(rule.program, temp3, sizeof(rule.program)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_RESULT) == 0) { + strlcpy(rule.result, temp3, sizeof(rule.result)); + valid = 1; + continue; + } + + if (strncasecmp(temp2, FIELD_NAME, sizeof(FIELD_NAME)-1) == 0) { + attr = get_key_attribute(temp2 + sizeof(FIELD_NAME)-1); + /* FIXME: remove old style options and make OPTIONS= mandatory */ + if (attr != NULL) { + if (strstr(attr, OPTION_PARTITIONS) != NULL) { + dbg("creation of partition nodes requested"); + rule.partitions = DEFAULT_PARTITIONS_COUNT; + } + if (strstr(attr, OPTION_IGNORE_REMOVE) != NULL) { + dbg("remove event should be ignored"); + rule.ignore_remove = 1; + } + } + if (temp3[0] != '\0') + strlcpy(rule.name, temp3, sizeof(rule.name)); + else + rule.ignore_device = 1; + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_SYMLINK) == 0) { + strlcpy(rule.symlink, temp3, sizeof(rule.symlink)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_OWNER) == 0) { + strlcpy(rule.owner, temp3, sizeof(rule.owner)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_GROUP) == 0) { + strlcpy(rule.group, temp3, sizeof(rule.group)); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_MODE) == 0) { + rule.mode = strtol(temp3, NULL, 8); + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_OPTIONS) == 0) { + if (strstr(temp3, OPTION_IGNORE_DEVICE) != NULL) { + dbg("device should be ignored"); + rule.ignore_device = 1; + } + if (strstr(temp3, OPTION_IGNORE_REMOVE) != NULL) { + dbg("remove event should be ignored"); + rule.ignore_remove = 1; + } + if (strstr(temp3, OPTION_PARTITIONS) != NULL) { + dbg("creation of partition nodes requested"); + rule.partitions = DEFAULT_PARTITIONS_COUNT; + } + valid = 1; + continue; + } + + dbg("unknown type of field '%s'", temp2); + goto error; + } + + /* skip line if not any valid key was found */ + if (!valid) + goto error; + + /* simple plausibility checks for given keys */ + if ((rule.sysfs_pair[0].file[0] == '\0') ^ + (rule.sysfs_pair[0].value[0] == '\0')) { + info("inconsistency in " FIELD_SYSFS " key"); + goto error; + } + + if ((rule.result[0] != '\0') && (program_given == 0)) { + info(FIELD_RESULT " is only useful when " + FIELD_PROGRAM " is called in any rule before"); + goto error; + } + + rule.config_line = lineno; + strlcpy(rule.config_file, filename, sizeof(rule.config_file)); + retval = add_config_dev(&rule); + if (retval) { + dbg("add_config_dev returned with error %d", retval); + continue; +error: + info("parse error %s, line %d:%d, rule skipped", + filename, lineno, (int) (temp - line)); + } + } + + file_unmap(buf, bufsize); + return retval; +} + +int udev_rules_init(void) +{ + struct stat stats; + int retval; + + if (stat(udev_rules_filename, &stats) != 0) + return -1; + + if ((stats.st_mode & S_IFMT) != S_IFDIR) + retval = rules_parse(NULL, udev_rules_filename); + else + retval = call_foreach_file(rules_parse, NULL, udev_rules_filename, RULEFILE_SUFFIX); + + return retval; +} + +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); + } +} + diff --git a/udevstart.c b/udevstart.c index 7564c3de03..7765661fe4 100644 --- a/udevstart.c +++ b/udevstart.c @@ -39,7 +39,7 @@ #include "udev_libc_wrapper.h" #include "udev.h" #include "logging.h" -#include "namedev.h" +#include "udev_rules.h" #include "udev_utils.h" #include "list.h" @@ -317,7 +317,7 @@ int main(int argc, char *argv[], char *envp[]) setenv("ACTION", "add", 1); setenv("UDEV_START", "1", 1); - namedev_init(); + udev_rules_init(); udev_scan_block(); udev_scan_class(); diff --git a/udevtest.c b/udevtest.c index 1e0037978b..006d55510e 100644 --- a/udevtest.c +++ b/udevtest.c @@ -32,7 +32,7 @@ #include "udev_sysfs.h" #include "udev_utils.h" #include "udev_version.h" -#include "namedev.h" +#include "udev_rules.h" #include "logging.h" @@ -83,7 +83,7 @@ int main(int argc, char *argv[], char *envp[]) info("looking at '%s'", devpath); /* initialize the naming deamon */ - namedev_init(); + udev_rules_init(); if (argc == 3) subsystem = argv[2]; -- cgit v1.2.3-54-g00ecf