From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kay Sievers Date: Mon, 28 Feb 2005 18:41:41 +0000 Subject: Re: The Next Generation Message-Id: <20050228184141.GA24961@vrfy.org> List-Id: References: <20050217190941.GA1561@vrfy.org> In-Reply-To: <20050217190941.GA1561@vrfy.org> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: linux-hotplug@vger.kernel.org On Fri, Feb 25, 2005 at 03:17:14PM -0800, Greg KH wrote: > On Thu, Feb 17, 2005 at 08:09:41PM +0100, Kay Sievers wrote: > > For that reason, we should get rid of all the just too simple > > brute-force logic in /etc/hotplug/*, /etc/hotplug.d/ and /etc/dev.d/, which > > requires scripts to check if they are called for the right device. > > No, we can't break backward compatiblity like that. We need to always > support the /etc/hotplug.d/ way, as we've already told too many people > they can rely on that always working. ... > And /etc/dev.d/ is also a good thing to have. Ok, ok, here is a more intelligent replacement. :) I've changes the HOTPLUG key to be able to read a whole subdirectory with a wildcard instead of only one file. The following three rules will emulate the dev.d/ directory handling we currently have compiled in: HOTPLUG="/etc/dev.d/%D/*.dev" HOTPLUG="/etc/dev.d/%S/*.dev" HOTPLUG="/etc/dev.d/default/*.dev" The nice thing with this approach is that a program called specified twice will only added once to the list of programs to be executed. We can ship these rules by default which remains the needed backwards compatibility, but the user can remove these rules and udev does not need to search for anything outside the specified rules. How does that sound now? Thanks, Kay === namedev.c 1.189 vs edited ==--- 1.189/namedev.c 2005-02-24 12:13:24 +01:00 +++ edited/namedev.c 2005-02-28 17:54:21 +01:00 @@ -168,8 +168,7 @@ static int find_free_number(struct udevi } static void apply_format(struct udevice *udev, char *string, size_t maxsize, - struct sysfs_class_device *class_dev, - struct sysfs_device *sysfs_device) + struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) { char temp[NAME_SIZE]; char temp2[NAME_SIZE]; @@ -203,26 +202,18 @@ static void apply_format(struct udevice switch (c) { case 'p': - if (strlen(udev->devpath) = 0) - break; strfieldcatmax(string, udev->devpath, maxsize); dbg("substitute kernel name '%s'", udev->kernel_name); break; case 'b': - if (strlen(udev->bus_id) = 0) - break; strfieldcatmax(string, udev->bus_id, maxsize); dbg("substitute bus_id '%s'", udev->bus_id); break; case 'k': - if (strlen(udev->kernel_name) = 0) - break; strfieldcatmax(string, udev->kernel_name, maxsize); dbg("substitute kernel name '%s'", udev->kernel_name); break; case 'n': - if (strlen(udev->kernel_number) = 0) - break; strfieldcatmax(string, udev->kernel_number, maxsize); dbg("substitute kernel number '%s'", udev->kernel_number); break; @@ -235,7 +226,7 @@ static void apply_format(struct udevice dbg("substitute major number '%u'", major(udev->devt)); break; case 'c': - if (strlen(udev->program_result) = 0) + if (udev->program_result[0] = '\0') break; /* get part part of the result string */ i = 0; @@ -326,6 +317,14 @@ static void apply_format(struct udevice strfieldcatmax(string, udev->tmp_node, maxsize); dbg("substitute temporary device node name '%s'", udev->tmp_node); break; + case 'S': + strfieldcatmax(string, udev->subsystem, maxsize); + dbg("substitute subsystem '%s'", udev->subsystem); + break; + case 'D': + strfieldcatmax(string, udev->name, maxsize); + dbg("substitute node name '%s'", udev->name); + break; case 'r': strfieldcatmax(string, udev_root, maxsize); dbg("substitute udev_root '%s'", udev_root); @@ -571,7 +570,7 @@ static int match_rule(struct udevice *ud 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) { + if (strcmp_pattern(dev->kernel, udev->kernel_name) != 0) { dbg(FIELD_KERNEL " is not matching"); goto exit; } @@ -588,6 +587,16 @@ static int match_rule(struct udevice *ud dbg(FIELD_SUBSYSTEM " matches"); } + if (dev->action[0] != '\0') { + dbg("check for " FIELD_ACTION " dev->action='%s' class_dev->name='%s'", + dev->action, class_dev->name); + if (strcmp_pattern(dev->action, udev->action) != 0) { + dbg(FIELD_ACTION " is not matching"); + goto exit; + } + dbg(FIELD_ACTION " matches"); + } + /* walk up the chain of physical devices and find a match */ while (1) { /* check for matching driver */ @@ -689,13 +698,13 @@ exit: return -1; } -int namedev_name_device(struct udevice *udev, struct sysfs_class_device *class_dev) +int 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 config_device *dev; - dbg("class_dev->name='%s'", class_dev->name); + dbg("udev->devpath='%s'", udev->devpath); /* 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 @@ -722,6 +731,10 @@ int namedev_name_device(struct udevice * list_for_each_entry(dev, &config_device_list, node) { dbg("process rule"); if (match_rule(udev, dev, class_dev, sysfs_device) = 0) { + if (dev->name[0] != '\0' && udev->name[0] != '\0') { + dbg("node name already set, rule ignored"); + continue; + } /* apply options */ if (dev->ignore_device) { @@ -730,7 +743,7 @@ int namedev_name_device(struct udevice * return -1; } if (dev->ignore_remove) { - udev->ignore_remove = dev->ignore_remove; + udev->ignore_remove = 1; dbg_parse("remove event should be ignored"); } /* apply all_partitions option only at a main block device */ @@ -738,6 +751,10 @@ int namedev_name_device(struct udevice * udev->partitions = dev->partitions; dbg("creation of partition nodes requested"); } + if (dev->no_hotplug) { + udev->no_hotplug = 1; + dbg_parse("supress hotplug program execution"); + } /* apply permissions */ if (dev->mode != 0000) { @@ -768,7 +785,14 @@ int namedev_name_device(struct udevice * strfieldcat(udev->symlink, temp); } - /* rule matches */ + /* collect hotplug programs */ + if (dev->hotplug[0] != '\0') { + dbg("configured rule in '%s[%i]' added hotplug program '%s'", + dev->config_file, dev->config_line, dev->hotplug); + name_list_add(&udev->hotplug_list, dev->hotplug, 0); + } + + /* set the name of the node */ 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); @@ -781,21 +805,136 @@ int namedev_name_device(struct udevice * if (udev->type != 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); + } - goto exit; + if (dev->last_rule) { + dbg("last rule key found, processing will stop at this rule"); + break; } } } /* no rule matched, so we use the kernel name */ - strfieldcpy(udev->name, udev->kernel_name); - dbg("no rule found, use kernel name '%s'", udev->name); + if (udev->name[0] = '\0') { + strfieldcpy(udev->name, udev->kernel_name); + dbg("no rule found, use kernel name '%s'", udev->name); + } -exit: if (udev->tmp_node[0] != '\0') { dbg("removing temporary device node"); unlink_secure(udev->tmp_node); udev->tmp_node[0] = '\0'; + } + + return 0; +} + +int rules_get_hotplug(struct udevice *udev) +{ + struct config_device *dev; + + /* look for a matching rule to apply */ + list_for_each_entry(dev, &config_device_list, node) { + dbg("process rule"); + + if (dev->hotplug[0] = '\0') + continue; + + /* skip rules that can't match */ + if (dev->bus[0] != '\0' || dev->id[0] != '\0' || + dev->place[0] != '\0' || dev->sysfs_pair[0].file[0] != '\0' || + dev->program[0] != '\0' || dev->result[0] != '\0' || + dev->driver[0] != '\0') + continue; + + if (dev->action[0] != '\0') { + dbg("check for " FIELD_ACTION " dev->action='%s' udev->action='%s'", + dev->action, udev->action); + if (strcmp(dev->action, udev->action) != 0) { + dbg(FIELD_ACTION " is not matching"); + continue; + } + dbg(FIELD_ACTION " matches"); + } + + if (dev->subsystem[0] != '\0') { + dbg("check for " FIELD_SUBSYSTEM " dev->subsystem='%s' udev->subsystem='%s'", + dev->subsystem, udev->subsystem); + if (strcmp_pattern(dev->subsystem, udev->subsystem) != 0) { + dbg(FIELD_SUBSYSTEM " is not matching"); + continue; + } + dbg(FIELD_SUBSYSTEM " matches"); + } + + if (dev->kernel[0] != '\0') { + dbg("check for " FIELD_KERNEL " dev->kernel='%s' class_dev->name='%s'", + dev->kernel, udev->kernel_name); + if (strcmp_pattern(dev->kernel, udev->kernel_name) != 0) { + dbg(FIELD_KERNEL " is not matching"); + continue; + } + dbg(FIELD_KERNEL " matches"); + } + + /* rule matches, see if hotplug programs should run*/ + if (dev->no_hotplug) { + udev->no_hotplug = 1; + dbg_parse("supress hotplug program execution"); + return -1; + } + + name_list_add(&udev->hotplug_list, dev->hotplug, 0); + } + + return 0; +} + +static int hotplug_list_insert(struct udevice *udev, const char *name) +{ + char temp[NAME_SIZE]; + + strfieldcpy(temp, name); + apply_format(udev, temp, NAME_SIZE, NULL, NULL); + dbg("applied format '%s'", temp); + name_list_add(&udev->hotplug_list, temp, 0); + + return 0; +} + +int rules_expand_hotplug_list(struct udevice *udev) +{ + struct name_entry *name_loop; + struct name_entry *name_tmp; + + list_for_each_entry_safe(name_loop, name_tmp, &udev->hotplug_list, node) { + apply_format(udev, name_loop->name, NAME_SIZE, NULL, NULL); + dbg("applied format '%s'", name_loop->name); + + /* allow wildcard to add all files with a specific suffix */ + if (strchr(name_loop->name, '*')) { + char directory[NAME_SIZE]; + char *pos; + + strfieldcpy(directory, name_loop->name); + pos = strrchr(directory, '/'); + if (pos = NULL) + return -1; + pos[0] = '\0'; + pos++; + + if (strchr(pos, '*') = NULL) + continue; + + pos = strrchr(pos, '.'); + if (pos = NULL) + continue; + + dbg("remove '%s' as command, instead search for '%s'-suffix in '%s'", + name_loop->name, pos, directory); + list_del(&name_loop->node); + call_foreach_file(hotplug_list_insert, udev, directory, pos); + } } return 0; === namedev.h 1.40 vs edited ==--- 1.40/namedev.h 2005-02-21 06:48:12 +01:00 +++ edited/namedev.h 2005-02-28 17:32:04 +01:00 @@ -34,10 +34,10 @@ struct sysfs_class_device; #define ID_SIZE 64 #define PLACE_SIZE 64 #define DRIVER_SIZE 64 -#define PROGRAM_SIZE 128 #define FIELD_KERNEL "KERNEL" #define FIELD_SUBSYSTEM "SUBSYSTEM" +#define FIELD_ACTION "ACTION" #define FIELD_BUS "BUS" #define FIELD_SYSFS "SYSFS" #define FIELD_ID "ID" @@ -51,10 +51,13 @@ struct sysfs_class_device; #define FIELD_GROUP "GROUP" #define FIELD_MODE "MODE" #define FIELD_OPTIONS "OPTIONS" +#define FIELD_HOTPLUG "HOTPLUG" -#define ATTR_IGNORE_DEVICE "ignore_device" -#define ATTR_IGNORE_REMOVE "ignore_remove" -#define ATTR_PARTITIONS "all_partitions" +#define OPTION_LAST_RULE "last_rule" +#define OPTION_IGNORE_DEVICE "ignore_device" +#define OPTION_IGNORE_REMOVE "ignore_remove" +#define OPTION_NO_HOTPLUG "no_hotplug" +#define OPTION_ALL_PARTITIONS "all_partitions" #define MAX_SYSFS_PAIRS 5 @@ -70,6 +73,7 @@ struct config_device { char kernel[NAME_SIZE]; char subsystem[SUBSYSTEM_SIZE]; + char action[ACTION_SIZE]; char bus[BUS_SIZE]; char id[ID_SIZE]; char place[PLACE_SIZE]; @@ -87,6 +91,10 @@ struct config_device { int partitions; int ignore_device; int ignore_remove; + int no_hotplug; + int last_rule; + + char hotplug[PROGRAM_SIZE]; char config_file[NAME_SIZE]; int config_line; @@ -94,9 +102,12 @@ struct config_device { 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 int rules_init(void); +extern void rules_close(void); + +extern int rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev); +extern int rules_get_hotplug(struct udevice *udev); +extern int rules_expand_hotplug_list(struct udevice *udev); extern void dump_config_dev(struct config_device *dev); extern void dump_config_dev_list(void); === namedev_parse.c 1.52 vs edited ==--- 1.52/namedev_parse.c 2005-02-25 18:52:03 +01:00 +++ edited/namedev_parse.c 2005-02-28 12:39:46 +01:00 @@ -98,7 +98,7 @@ static char *get_key_attribute(char *str return NULL; } -static int namedev_parse(const char *filename, void *data) +static int rules_parse(struct udevice *udev, const char *filename) { char line[LINE_SIZE]; char *bufline; @@ -183,6 +183,12 @@ static int namedev_parse(const char *fil continue; } + if (strcasecmp(temp2, FIELD_ACTION) = 0) { + strfieldcpy(dev.action, temp3); + valid = 1; + continue; + } + if (strcasecmp(temp2, FIELD_BUS) = 0) { strfieldcpy(dev.bus, temp3); valid = 1; @@ -250,11 +256,11 @@ static int namedev_parse(const char *fil attr = get_key_attribute(temp2 + sizeof(FIELD_NAME)-1); /* FIXME: remove old style options and make OPTIONS= mandatory */ if (attr != NULL) { - if (strstr(attr, ATTR_PARTITIONS) != NULL) { + if (strstr(attr, OPTION_ALL_PARTITIONS) != NULL) { dbg_parse("creation of partition nodes requested"); dev.partitions = DEFAULT_PARTITIONS_COUNT; } - if (strstr(attr, ATTR_IGNORE_REMOVE) != NULL) { + if (strstr(attr, OPTION_IGNORE_REMOVE) != NULL) { dbg_parse("remove event should be ignored"); dev.ignore_remove = 1; } @@ -292,18 +298,32 @@ static int namedev_parse(const char *fil } if (strcasecmp(temp2, FIELD_OPTIONS) = 0) { - if (strstr(temp3, ATTR_IGNORE_DEVICE) != NULL) { + if (strstr(temp3, OPTION_LAST_RULE) != NULL) { + dbg_parse("last rule to be applied for that device"); + dev.last_rule = 1; + } + if (strstr(temp3, OPTION_IGNORE_DEVICE) != NULL) { dbg_parse("device should be ignored"); dev.ignore_device = 1; } - if (strstr(temp3, ATTR_IGNORE_REMOVE) != NULL) { + if (strstr(temp3, OPTION_IGNORE_REMOVE) != NULL) { dbg_parse("remove event should be ignored"); dev.ignore_remove = 1; } - if (strstr(temp3, ATTR_PARTITIONS) != NULL) { + if (strstr(temp3, OPTION_ALL_PARTITIONS) != NULL) { dbg_parse("creation of partition nodes requested"); dev.partitions = DEFAULT_PARTITIONS_COUNT; } + if (strstr(temp3, OPTION_NO_HOTPLUG) != NULL) { + dbg_parse("execution of hotplug programs supressed"); + dev.no_hotplug = 1; + } + valid = 1; + continue; + } + + if (strcasecmp(temp2, FIELD_HOTPLUG) = 0) { + strfieldcpy(dev.hotplug, temp3); valid = 1; continue; } @@ -345,7 +365,7 @@ error: return retval; } -int namedev_init(void) +int rules_init(void) { struct stat stats; int retval; @@ -354,14 +374,14 @@ int namedev_init(void) return -1; if ((stats.st_mode & S_IFMT) != S_IFDIR) - retval = namedev_parse(udev_rules_filename, NULL); + retval = rules_parse(NULL, udev_rules_filename); else - retval = call_foreach_file(namedev_parse, udev_rules_filename, RULEFILE_SUFFIX, NULL); + retval = call_foreach_file(rules_parse, NULL, udev_rules_filename, RULEFILE_SUFFIX); return retval; } -void namedev_close(void) +void rules_close(void) { struct config_device *dev; === udev.8.in 1.78 vs edited ==--- 1.78/udev.8.in 2005-02-13 22:03:06 +01:00 +++ edited/udev.8.in 2005-02-28 05:38:24 +01:00 @@ -98,9 +98,8 @@ Every rule consists of a list of comma s .sp where fields are: .TP -.B BUS -Match the bus type of the device. -(The sysfs device bus must be able to be determined by a "device" symlink.) +.B ACTION +Match the event type. .TP .B KERNEL Match the kernel device name. @@ -108,9 +107,16 @@ Match the kernel device name. .B SUBSYSTEM Match the kernel subsystem name. .TP +.B ACTION +Match the event action string. +.TP .B DRIVER Match the kernel driver name. .TP +.B BUS +Match the bus type of the device. +(The sysfs device bus must be able to be determined by a "device" symlink.) +.TP .B ID Match the device number on the bus, like PCI bus id. .TP @@ -178,6 +184,10 @@ This may be useful for removable media d change. .sp Multiple attributes may be separated by comma. +.TP +.B HOTPLUG +programs to be added to a list to be excuted after node creation or removal. +Identical keys will only be added once. .P .RB "The " NAME ", " SYMLINK ", " PROGRAM ", " OWNER " and " GROUP fields support simple printf-like string substitutions: @@ -219,8 +229,14 @@ all remaining parts of the result string The name of a created temporary device node to provide access to the device from a external program. .TP +.B %S +The name of the subsystem. +.TP +.B %D +The name of the device node. +.TP .B %P -The node name of the parent device. +The name of the parent device node. .TP .BI %s{ filename } The content of a sysfs attribute. @@ -301,15 +317,7 @@ is set if udev is configured to use the want to follow that setting. .B DEVNAME is exported to make the name of the created node, or the name the network -device is renamed to, available to the executed program. The programs in every -directory are sorted in lexical order, while the directories are searched in -the following order: -.sp -.nf -/etc/dev.d/$(DEVNAME)/*.dev -/etc/dev.d/$(SUBSYSTEM)/*.dev -/etc/dev.d/default/*.dev -.fi +device is renamed to, available to the executed program. .SH "ENVIRONMENT" .P The following variables are read from the environment: @@ -330,20 +338,11 @@ Overrides the default location of the .B udev config file. .TP -.B UDEV_NO_DEVD -The default behavior of -.B udev -is to execute programs in the -.I /etc/dev.d/ -directory after device handling. If set, -.B udev -will skip this step. .SH "FILES" .nf /sbin/udev udev program /etc/udev/* udev config files /etc/hotplug.d/default/udev.hotplug hotplug symlink to udev program -/etc/dev.d/* programs invoked by udev .fi .SH "SEE ALSO" .BR udevinfo (8), === udev.c 1.104 vs edited ==--- 1.104/udev.c 2005-02-21 07:01:52 +01:00 +++ edited/udev.c 2005-02-28 17:40:45 +01:00 @@ -136,7 +136,7 @@ int main(int argc, char *argv[], char *e /* disable all logging, as it's much too slow on some facilities */ udev_log = 0; - namedev_init(); + rules_init(); retval = udev_start(); goto exit; } @@ -170,58 +170,74 @@ int main(int argc, char *argv[], char *e if (udev_log) setenv("UDEV_LOG", "1", 1); - udev_init_device(&udev, devpath, subsystem); + udev_init_device(&udev, devpath, subsystem, action); if (udev.type = BLOCK || udev.type = CLASS || udev.type = NET) { - if (strcmp(action, "add") = 0) { - /* wait for sysfs and possibly add node */ - dbg("udev add"); + rules_init(); - /* skip subsystems without "dev", but handle net devices */ - if (udev.type != NET && subsystem_expect_no_dev(udev.subsystem)) { - dbg("don't care about '%s' devices", udev.subsystem); - goto hotplug; - } + if (strcmp(udev.action, "add") = 0) { + dbg("udev add"); + /* wait for sysfs to populate */ snprintf(path, SYSFS_PATH_MAX, "%s%s", sysfs_path, udev.devpath); + path[SYSFS_PATH_MAX-1] = '\0'; class_dev = wait_class_device_open(path); if (class_dev = NULL) { dbg ("open class device failed"); goto hotplug; } dbg("opened class_dev->name='%s'", class_dev->name); - wait_for_class_device(class_dev, &error); - /* init rules */ - namedev_init(); + /* create a node for devices with a "dev" file */ + if (!subsystem_expect_no_dev(udev.subsystem)) { + udev.devt = get_devt(class_dev); + if (!udev.devt) { + dbg("dev file expected, but not found"); + sysfs_close_class_device(class_dev); + goto hotplug; + } + } - /* name, create node, store in db */ - retval = udev_add_device(&udev, class_dev); + /* name device or possibly ignore event */ + if (rules_get_name(&udev, class_dev) != 0) { + sysfs_close_class_device(class_dev); + goto hotplug; + } + udev_add_device(&udev, class_dev); sysfs_close_class_device(class_dev); - } else if (strcmp(action, "remove") = 0) { - /* possibly remove a node */ + + } else if (strcmp(udev.action, "remove") = 0) { dbg("udev remove"); /* skip subsystems without "dev" */ if (subsystem_expect_no_dev(udev.subsystem)) { - dbg("don't care about '%s' devices", udev.subsystem); + dbg("'%s' devices don't have device nodes", udev.subsystem); goto hotplug; } + /* get hotplug scripts or possibly ignore event */ + if (rules_get_hotplug(&udev) != 0) + goto hotplug; + /* get node from db, remove db-entry, delete created node */ retval = udev_remove_device(&udev); } - /* run dev.d/ scripts if we created/deleted a node or changed a netif name */ - if (udev.devname[0] != '\0') { + /* execute hotplug scripts */ + if (udev.devname[0] != '\0' && !udev.no_hotplug) { + struct name_entry *name_loop; + setenv("DEVNAME", udev.devname, 1); - if (udev_dev_d) - udev_multiplex_directory(&udev, DEVD_DIR, DEVD_SUFFIX); + rules_expand_hotplug_list(&udev); + + list_for_each_entry(name_loop, &udev.hotplug_list, node) + execute_command(&udev, name_loop->name); } + } else if (udev.type = PHYSDEV) { - if (strcmp(action, "add") = 0) { + if (strcmp(udev.action, "add") = 0) { /* wait for sysfs */ dbg("devices add"); @@ -236,12 +252,11 @@ int main(int argc, char *argv[], char *e wait_for_devices_device(devices_dev, &error); sysfs_close_device(devices_dev); - } else if (strcmp(action, "remove") = 0) { + } else if (strcmp(action, "remove") = 0) dbg("devices remove"); - } - } else { - dbg("unhandled"); - } + + } else + dbg("don't care"); hotplug: if (udev_hotplug_d && managed_event) === udev.h 1.87 vs edited ==--- 1.87/udev.h 2005-02-21 06:48:12 +01:00 +++ edited/udev.h 2005-02-28 12:38:48 +01:00 @@ -4,6 +4,7 @@ * 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 @@ -31,7 +32,9 @@ #define ALARM_TIMEOUT 120 #define COMMENT_CHARACTER '#' +#define LINE_SIZE 512 #define NAME_SIZE 256 +#define PROGRAM_SIZE 128 #define USER_SIZE 32 #define ACTION_SIZE 32 @@ -39,16 +42,16 @@ #define SUBSYSTEM_SIZE 32 #define SEQNUM_SIZE 32 -#define LINE_SIZE 512 - -#define DEVD_DIR "/etc/dev.d" -#define DEVD_SUFFIX ".dev" - #define HOTPLUGD_DIR "/etc/hotplug.d" #define HOTPLUG_SUFFIX ".hotplug" #define DEFAULT_PARTITIONS_COUNT 15 +struct name_entry { + struct list_head node; + char name[NAME_SIZE]; +}; + enum device_type { UNKNOWN, CLASS, @@ -58,22 +61,28 @@ enum device_type { }; struct udevice { + char action[ACTION_SIZE]; char devpath[DEVPATH_SIZE]; char subsystem[SUBSYSTEM_SIZE]; char name[NAME_SIZE]; + char devname[NAME_SIZE]; char symlink[NAME_SIZE]; char owner[USER_SIZE]; char group[USER_SIZE]; mode_t mode; - char type; + enum device_type type ; dev_t devt; - char devname[NAME_SIZE]; char tmp_node[NAME_SIZE]; int partitions; int ignore_remove; - int config_line; + int no_hotplug; + int last_rule; + + struct list_head hotplug_list; + + unsigned int config_line; char config_file[NAME_SIZE]; char bus_id[SYSFS_NAME_LEN]; char program_result[NAME_SIZE]; @@ -95,7 +104,6 @@ extern char udev_db_path[PATH_MAX+NAME_M extern char udev_config_filename[PATH_MAX+NAME_MAX]; extern char udev_rules_filename[PATH_MAX+NAME_MAX]; extern int udev_log; -extern int udev_dev_d; extern int udev_hotplug_d; #endif === udev_add.c 1.95 vs edited ==--- 1.95/udev_add.c 2005-02-21 05:45:03 +01:00 +++ edited/udev_add.c 2005-02-28 04:17:44 +01:00 @@ -39,7 +39,6 @@ #include "libsysfs/sysfs/libsysfs.h" #include "udev.h" #include "udev_utils.h" -#include "udev_sysfs.h" #include "udev_version.h" #include "logging.h" #include "namedev.h" @@ -272,17 +271,6 @@ int udev_add_device(struct udevice *udev { char *pos; int retval = 0; - - if (udev->type = BLOCK || udev->type = CLASS) { - udev->devt = get_devt(class_dev); - if (!udev->devt) { - dbg("no dev-file found, do nothing"); - return 0; - } - } - - if (namedev_name_device(udev, class_dev) != 0) - return 0; dbg("adding name='%s'", udev->name); === udev_config.c 1.31 vs edited ==--- 1.31/udev_config.c 2005-01-04 21:37:01 +01:00 +++ edited/udev_config.c 2005-02-28 04:17:44 +01:00 @@ -46,7 +46,6 @@ char udev_db_path[PATH_MAX+NAME_MAX]; char udev_rules_filename[PATH_MAX+NAME_MAX]; char udev_config_filename[PATH_MAX+NAME_MAX]; int udev_log; -int udev_dev_d; int udev_hotplug_d; @@ -72,11 +71,6 @@ static void init_variables(void) strcpy(udev_rules_filename, UDEV_RULES_FILE); udev_log = string_is_true(UDEV_LOG_DEFAULT); - - udev_dev_d = 1; - env = getenv("UDEV_NO_DEVD"); - if (env && string_is_true(env)) - udev_dev_d = 0; udev_hotplug_d = 1; env = getenv("UDEV_NO_HOTPLUGD"); === udev_multiplex.c 1.25 vs edited ==--- 1.25/udev_multiplex.c 2004-11-25 01:55:54 +01:00 +++ edited/udev_multiplex.c 2005-02-28 04:27:29 +01:00 @@ -31,11 +31,10 @@ #include "udev_utils.h" #include "logging.h" -static int run_program(const char *filename, void *data) +static int run_program(struct udevice *udev, const char *filename) { pid_t pid; int fd; - struct udevice *udev = data; dbg("running %s", filename); @@ -89,7 +88,7 @@ void udev_multiplex_directory(struct ude if (strcmp(devname, udev->subsystem) != 0) { snprintf(dirname, PATH_MAX, "%s/%s", basedir, devname); dirname[PATH_MAX-1] = '\0'; - call_foreach_file(run_program, dirname, suffix, udev); + call_foreach_file(run_program, udev, dirname, suffix); } temp[0] = '/'; @@ -101,16 +100,16 @@ void udev_multiplex_directory(struct ude if (udev->name[0] != '\0') { snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->name); dirname[PATH_MAX-1] = '\0'; - call_foreach_file(run_program, dirname, suffix, udev); + call_foreach_file(run_program, udev, dirname, suffix); } if (udev->subsystem[0] != '\0') { snprintf(dirname, PATH_MAX, "%s/%s", basedir, udev->subsystem); dirname[PATH_MAX-1] = '\0'; - call_foreach_file(run_program, dirname, suffix, udev); + call_foreach_file(run_program, udev, dirname, suffix); } snprintf(dirname, PATH_MAX, "%s/default", basedir); dirname[PATH_MAX-1] = '\0'; - call_foreach_file(run_program, dirname, suffix, udev); + call_foreach_file(run_program, udev, dirname, suffix); } === udev_start.c 1.29 vs edited ==--- 1.29/udev_start.c 2005-02-10 10:26:09 +01:00 +++ edited/udev_start.c 2005-02-28 04:17:44 +01:00 @@ -35,6 +35,8 @@ #include #include "libsysfs/sysfs/libsysfs.h" +#include "namedev.h" +#include "udev_sysfs.h" #include "logging.h" #include "udev_utils.h" #include "list.h" @@ -96,7 +98,7 @@ static int add_device(const char *path, devpath = &path[strlen(sysfs_path)]; - /* set environment for callouts and dev.d/ */ + /* set environment for callouts and hotplug programs */ setenv("DEVPATH", devpath, 1); setenv("SUBSYSTEM", subsystem, 1); @@ -108,15 +110,32 @@ static int add_device(const char *path, return -ENODEV; } - udev_init_device(&udev, devpath, subsystem); + udev_init_device(&udev, devpath, subsystem, "add"); + + /* create a node for devices with a "dev" file */ + if (!subsystem_expect_no_dev(udev.subsystem)) { + udev.devt = get_devt(class_dev); + if (!udev.devt) { + dbg("dev file expected, but not found"); + goto exit; + } + } + + if (rules_get_name(&udev, class_dev) != 0) + goto exit; + udev_add_device(&udev, class_dev); - /* run dev.d/ scripts if we created a node or changed a netif name */ - if (udev_dev_d && udev.devname[0] != '\0') { + /* execute hotplug scripts */ + if (udev.devname[0] != '\0' && !udev.no_hotplug) { + struct name_entry *name_loop; + setenv("DEVNAME", udev.devname, 1); - udev_multiplex_directory(&udev, DEVD_DIR, DEVD_SUFFIX); + list_for_each_entry(name_loop, &udev.hotplug_list, node) + execute_command(&udev, name_loop->name); } +exit: sysfs_close_class_device(class_dev); return 0; @@ -280,7 +299,7 @@ static void udev_scan_class(void) int udev_start(void) { - /* set environment for callouts and dev.d/ */ + /* set environment for callouts and hotplug programs */ setenv("ACTION", "add", 1); setenv("UDEV_START", "1", 1); === udev_utils.c 1.32 vs edited ==--- 1.32/udev_utils.c 2005-02-21 07:01:52 +01:00 +++ edited/udev_utils.c 2005-02-28 04:40:45 +01:00 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "udev.h" @@ -37,11 +38,12 @@ #include "list.h" -int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem) +int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action) { char *pos; memset(udev, 0x00, sizeof(struct udevice)); + INIT_LIST_HEAD(&udev->hotplug_list); if (devpath) { strfieldcpy(udev->devpath, devpath); @@ -49,6 +51,8 @@ int udev_init_device(struct udevice *ude } if (subsystem) strfieldcpy(udev->subsystem, subsystem); + if (action) + strfieldcpy(udev->action, action); if (strcmp(udev->subsystem, "block") = 0) udev->type = BLOCK; @@ -262,13 +266,7 @@ void no_trailing_slash(char *path) path[--len] = '\0'; } -struct name_entry { - struct list_head node; - char name[NAME_SIZE]; -}; - -/* sort files in lexical order */ -static int name_list_add(struct list_head *name_list, const char *name, int sort) +int name_list_add(struct list_head *name_list, const char *name, int sort) { struct name_entry *loop_name; struct name_entry *new_name; @@ -291,11 +289,75 @@ static int name_list_add(struct list_hea strfieldcpy(new_name->name, name); list_add_tail(&new_name->node, &loop_name->node); + + return 0; +} + +int execute_command(struct udevice *udev, const char *command) +{ + int retval; + pid_t pid; + char arg[PROGRAM_SIZE]; + char *argv[(PROGRAM_SIZE / 2) + 1]; + char *pos; + int fd; + int i; + + strfieldcpy(arg, command); + i = 0; + if (strchr(command, ' ')) { + 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]); + } + + pid = fork(); + switch (pid) { + case 0: + /* child */ + fd = open("/dev/null", O_RDWR); + if ( fd >= 0) { + dup2(fd, STDOUT_FILENO); + dup2(fd, STDIN_FILENO); + dup2(fd, STDERR_FILENO); + } + close(fd); + + retval = execv(arg, argv); + dbg("exec of child failed"); + _exit(1); + case -1: + dbg("fork of child failed"); + break; + return -1; + default: + waitpid(pid, NULL, 0); + } + return 0; } /* calls function for every file found in specified directory */ -int call_foreach_file(file_fnct_t fnct, const char *dirname, const char *suffix, void *data) +int call_foreach_file(int (*handler_function)(struct udevice *udev, const char *string), + struct udevice *udev, const char *dirname, const char *suffix) { struct dirent *ent; DIR *dir; @@ -338,7 +400,7 @@ int call_foreach_file(file_fnct_t fnct, snprintf(filename, NAME_SIZE, "%s/%s", dirname, loop_file->name); filename[NAME_SIZE-1] = '\0'; - fnct(filename, data); + handler_function(udev, filename); list_del(&loop_file->node); free(loop_file); === udev_utils.h 1.23 vs edited ==--- 1.23/udev_utils.h 2005-02-21 06:48:12 +01:00 +++ edited/udev_utils.h 2005-02-28 04:37:18 +01:00 @@ -76,7 +76,7 @@ do { \ # define asmlinkage /* nothing */ #endif -extern int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem); +extern int udev_init_device(struct udevice *udev, const char* devpath, const char *subsystem, const char *action); extern int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel); extern int create_path(const char *path); extern int parse_get_pair(char **orig_string, char **left, char **right); @@ -85,8 +85,10 @@ extern int file_map(const char *filename extern void file_unmap(char *buf, size_t bufsize); extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur); extern void no_trailing_slash(char *path); -typedef int (*file_fnct_t)(const char *filename, void *data); -extern int call_foreach_file(file_fnct_t fnct, const char *dirname, - const char *suffix, void *data); +extern int name_list_add(struct list_head *name_list, const char *name, int sort); + +extern int execute_command(struct udevice *udev, const char *command); +extern int call_foreach_file(int (*handler_function)(struct udevice *udev, const char *string), + struct udevice *udev, const char *dirname, const char *suffix); #endif === udevtest.c 1.24 vs edited ==--- 1.24/udevtest.c 2005-02-21 05:45:04 +01:00 +++ edited/udevtest.c 2005-02-28 04:17:44 +01:00 @@ -85,13 +85,13 @@ int main(int argc, char *argv[], char *e info("looking at '%s'", devpath); /* initialize the naming deamon */ - namedev_init(); + rules_init(); if (argc = 3) subsystem = argv[2]; /* fill in values and test_run flag*/ - udev_init_device(&udev, devpath, subsystem); + udev_init_device(&udev, devpath, subsystem, "add"); /* skip subsystems without "dev", but handle net devices */ if (udev.type != NET && subsystem_expect_no_dev(udev.subsystem)) { ------------------------------------------------------- SF email is sponsored by - The IT Product Guide Read honest & candid reviews on hundreds of IT Products from real users. Discover which products truly live up to the hype. Start reading now. http://ads.osdn.com/?ad_ide95&alloc_id396&op=click _______________________________________________ Linux-hotplug-devel mailing list http://linux-hotplug.sourceforge.net Linux-hotplug-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel