1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-04 21:47:31 +03:00

[PATCH] add RUN key to be able to run rule based notification

SUBSYSTEM=="block", RUN="/sbin/program"
  will execute the program only for block device events.

ACTION="remove", SUBSYSTEM=="block", RUN"/sbin/program"
  will execute the program, if a block device is removed.
This commit is contained in:
kay.sievers@vrfy.org 2005-04-02 17:45:35 +02:00 committed by Greg KH
parent e03a196a0d
commit 821d0ec803
13 changed files with 266 additions and 13 deletions

View File

@ -51,7 +51,6 @@ VERSION = 056
INSTALL_DIR = /usr/local/bin INSTALL_DIR = /usr/local/bin
RELEASE_NAME = $(ROOT)-$(VERSION) RELEASE_NAME = $(ROOT)-$(VERSION)
LOCAL_CFG_DIR = etc/udev LOCAL_CFG_DIR = etc/udev
HOTPLUG_EXEC = $(ROOT)
DESTDIR = DESTDIR =
KERNEL_DIR = /lib/modules/${shell uname -r}/build KERNEL_DIR = /lib/modules/${shell uname -r}/build

View File

@ -1243,6 +1243,53 @@ KERNEL=="sda", SYSFS{nothing}!="", NAME="not-1-ok"
KERNEL=="sda", SYSFS{nothing}=="", NAME="not-2-ok" KERNEL=="sda", SYSFS{nothing}=="", NAME="not-2-ok"
KERNEL=="sda", SYSFS{vendor}!="", NAME="ok" KERNEL=="sda", SYSFS{vendor}!="", NAME="ok"
KERNEL=="sda", SYSFS{vendor}=="", NAME="not-3-ok" KERNEL=="sda", SYSFS{vendor}=="", NAME="not-3-ok"
EOF
},
{
desc => "check ACTION value",
subsys => "block",
devpath => "/block/sda",
exp_name => "ok",
rules => <<EOF
ACTION=="unknown", KERNEL=="sda", NAME="unknown-not-ok"
ACTION=="add", KERNEL=="sda", NAME="ok"
EOF
},
{
desc => "apply NAME only once",
subsys => "block",
devpath => "/block/sda",
exp_name => "link",
exp_target => "ok",
rules => <<EOF
KERNEL=="sda", NAME="ok"
KERNEL=="sda", NAME="not-ok"
KERNEL=="sda", SYMLINK+="link"
EOF
},
{
desc => "test RUN key",
subsys => "block",
devpath => "/block/sda",
exp_name => "testsymlink",
exp_target => "ok",
exp_rem_error => "yes",
option => "clean",
rules => <<EOF
KERNEL=="sda", NAME="ok", RUN+="/bin/ln -s ok %r/testsymlink"
KERNEL=="sda", NAME="not-ok"
EOF
},
{
desc => "test RUN key remove",
subsys => "block",
devpath => "/block/sda",
exp_name => "testsymlink2",
exp_target => "ok2",
rules => <<EOF
KERNEL=="sda", NAME="ok2", RUN+="/bin/ln -s ok2 %r/testsymlink2"
KERNEL=="sda", ACTION=="remove", RUN+="/bin/rm -f %r/testsymlink2"
KERNEL=="sda", NAME="not-ok2"
EOF EOF
}, },
); );

View File

@ -111,6 +111,9 @@ Match the kernel device name.
.B SUBSYSTEM .B SUBSYSTEM
Match the kernel subsystem name. Match the kernel subsystem name.
.TP .TP
.B ACTION
Match the kernel action name.
.TP
.B DRIVER .B DRIVER
Match the kernel driver name. Match the kernel driver name.
.TP .TP
@ -170,6 +173,9 @@ distribution provided rules file.
The permissions for the device node. Every specified value overwrites the The permissions for the device node. Every specified value overwrites the
compiled-in default value. compiled-in default value.
.TP .TP
.B RUN
Add a program to the list of programs to be executed for a specific device.
.TP
.B OPTIONS .B OPTIONS
.B last_rule .B last_rule
will be the last rule applied. No later rules will have any effect. will be the last rule applied. No later rules will have any effect.
@ -234,6 +240,9 @@ The node name of the parent device.
.BI %s{ filename } .BI %s{ filename }
The content of a sysfs attribute. The content of a sysfs attribute.
.TP .TP
.B %r
The udev_root value.
.TP
.B %e .B %e
If a device node already exists with the name, the smallest positive If a device node already exists with the name, the smallest positive
decimal integer N is substituted such that the resulting name doesn't decimal integer N is substituted such that the resulting name doesn't
@ -344,6 +353,9 @@ config file.
.B UDEV_LOG .B UDEV_LOG
Overrides the log priority specified in the config file. Overrides the log priority specified in the config file.
.TP .TP
.B UDEV_RUN
If set to "0", it disables the execution of programs added by rules.
.TP
.B UDEV_NO_DEVD .B UDEV_NO_DEVD
The default behavior of The default behavior of
.B udev .B udev
@ -356,7 +368,6 @@ will skip this step.
.nf .nf
/sbin/udev udev program /sbin/udev udev program
/etc/udev/* udev config files /etc/udev/* udev config files
/etc/hotplug.d/default/udev.hotplug hotplug symlink to udev program
/etc/dev.d/* programs invoked by udev /etc/dev.d/* programs invoked by udev
.fi .fi
.SH "SEE ALSO" .SH "SEE ALSO"

19
udev.c
View File

@ -157,6 +157,8 @@ int main(int argc, char *argv[], char *envp[])
} }
if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) { if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) {
udev_rules_init();
if (strcmp(action, "add") == 0) { if (strcmp(action, "add") == 0) {
/* wait for sysfs and possibly add node */ /* wait for sysfs and possibly add node */
dbg("udev add"); dbg("udev add");
@ -178,9 +180,6 @@ int main(int argc, char *argv[], char *envp[])
wait_for_class_device(class_dev, &error); wait_for_class_device(class_dev, &error);
/* init rules */
udev_rules_init();
/* name, create node, store in db */ /* name, create node, store in db */
retval = udev_add_device(&udev, class_dev); retval = udev_add_device(&udev, class_dev);
@ -195,10 +194,24 @@ int main(int argc, char *argv[], char *envp[])
goto hotplug; goto hotplug;
} }
udev_rules_get_run(&udev);
if (udev.ignore_device) {
dbg("device event will be ignored");
goto hotplug;
}
/* get node from db, remove db-entry, delete created node */ /* get node from db, remove db-entry, delete created node */
retval = udev_remove_device(&udev); retval = udev_remove_device(&udev);
} }
if (udev_run && !list_empty(&udev.run_list)) {
struct name_entry *name_loop;
dbg("executing run list");
list_for_each_entry(name_loop, &udev.run_list, node)
execute_command(name_loop->name, udev.subsystem);
}
/* run dev.d/ scripts if we created/deleted a node or changed a netif name */ /* run dev.d/ scripts if we created/deleted a node or changed a netif name */
if (udev.devname[0] != '\0') { if (udev.devname[0] != '\0') {
setenv("DEVNAME", udev.devname, 1); setenv("DEVNAME", udev.devname, 1);

4
udev.h
View File

@ -58,6 +58,7 @@ enum device_type {
struct udevice { struct udevice {
char devpath[PATH_SIZE]; char devpath[PATH_SIZE];
char subsystem[NAME_SIZE]; char subsystem[NAME_SIZE];
char action[NAME_SIZE];
enum device_type type; enum device_type type;
char name[PATH_SIZE]; char name[PATH_SIZE];
@ -67,9 +68,11 @@ struct udevice {
char group[USER_SIZE]; char group[USER_SIZE];
mode_t mode; mode_t mode;
dev_t devt; dev_t devt;
struct list_head run_list;
char tmp_node[PATH_SIZE]; char tmp_node[PATH_SIZE];
int partitions; int partitions;
int ignore_device;
int ignore_remove; int ignore_remove;
int config_line; int config_line;
char config_file[PATH_SIZE]; char config_file[PATH_SIZE];
@ -93,6 +96,7 @@ extern char udev_db_path[PATH_SIZE];
extern char udev_config_filename[PATH_SIZE]; extern char udev_config_filename[PATH_SIZE];
extern char udev_rules_filename[PATH_SIZE]; extern char udev_rules_filename[PATH_SIZE];
extern int udev_log_priority; extern int udev_log_priority;
extern int udev_run;
extern int udev_dev_d; extern int udev_dev_d;
extern int udev_hotplug_d; extern int udev_hotplug_d;

View File

@ -276,8 +276,11 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
} }
} }
if (udev_rules_get_name(udev, class_dev) != 0) udev_rules_get_name(udev, class_dev);
if (udev->ignore_device) {
dbg("device event will be ignored");
return 0; return 0;
}
dbg("adding name='%s'", udev->name); dbg("adding name='%s'", udev->name);

View File

@ -44,6 +44,7 @@ char udev_db_path[PATH_SIZE];
char udev_config_filename[PATH_SIZE]; char udev_config_filename[PATH_SIZE];
char udev_rules_filename[PATH_SIZE]; char udev_rules_filename[PATH_SIZE];
int udev_log_priority; int udev_log_priority;
int udev_run;
int udev_dev_d; int udev_dev_d;
int udev_hotplug_d; int udev_hotplug_d;
@ -217,10 +218,16 @@ void udev_init_config(void)
strcpy(udev_config_filename, UDEV_CONFIG_FILE); strcpy(udev_config_filename, UDEV_CONFIG_FILE);
strcpy(udev_rules_filename, UDEV_RULES_FILE); strcpy(udev_rules_filename, UDEV_RULES_FILE);
udev_log_priority = LOG_ERR; udev_log_priority = LOG_ERR;
udev_run = 1;
udev_dev_d = 1; udev_dev_d = 1;
udev_hotplug_d = 1; udev_hotplug_d = 1;
sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path)); sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path));
/* disable RUN key execution */
env = getenv("UDEV_RUN");
if (env && !string_is_true(env))
udev_run = 0;
env = getenv("UDEV_NO_DEVD"); env = getenv("UDEV_NO_DEVD");
if (env && string_is_true(env)) if (env && string_is_true(env))
udev_dev_d = 0; udev_dev_d = 0;

View File

@ -474,6 +474,21 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,
{ {
struct sysfs_device *parent_device = sysfs_device; struct sysfs_device *parent_device = sysfs_device;
if (rule->action_operation != KEY_OP_UNSET) {
dbg("check for " KEY_ACTION " rule->action='%s' udev->action='%s'",
rule->action, udev->action);
if (strcmp_pattern(rule->action, udev->action) != 0) {
dbg(KEY_ACTION " is not matching");
if (rule->action_operation != KEY_OP_NOMATCH)
goto exit;
} else {
dbg(KEY_ACTION " matches");
if (rule->action_operation == KEY_OP_NOMATCH)
goto exit;
}
dbg(KEY_ACTION " key is true");
}
if (rule->kernel_operation != KEY_OP_UNSET) { if (rule->kernel_operation != KEY_OP_UNSET) {
dbg("check for " KEY_KERNEL " rule->kernel='%s' udev_kernel_name='%s'", dbg("check for " KEY_KERNEL " rule->kernel='%s' udev_kernel_name='%s'",
rule->kernel, udev->kernel_name); rule->kernel, udev->kernel_name);
@ -716,12 +731,17 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
list_for_each_entry(rule, &udev_rule_list, node) { list_for_each_entry(rule, &udev_rule_list, node) {
dbg("process rule"); dbg("process rule");
if (match_rule(udev, rule, class_dev, sysfs_device) == 0) { if (match_rule(udev, rule, class_dev, sysfs_device) == 0) {
if (udev->name[0] != '\0' && rule->name[0] != '\0') {
dbg("node name already set, rule ignored");
continue;
}
/* apply options */ /* apply options */
if (rule->ignore_device) { if (rule->ignore_device) {
info("configured rule in '%s[%i]' applied, '%s' is ignored", info("configured rule in '%s[%i]' applied, '%s' is ignored",
rule->config_file, rule->config_line, udev->kernel_name); rule->config_file, rule->config_line, udev->kernel_name);
return -1; udev->ignore_device = 1;
return 0;
} }
if (rule->ignore_remove) { if (rule->ignore_remove) {
udev->ignore_remove = 1; udev->ignore_remove = 1;
@ -773,7 +793,7 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
name_list_add(&udev->symlink_list, pos, 0); name_list_add(&udev->symlink_list, pos, 0);
} }
/* rule matches */ /* set name, later rules with name set will be ignored */
if (rule->name[0] != '\0') { if (rule->name[0] != '\0') {
info("configured rule in '%s[%i]' applied, '%s' becomes '%s'", info("configured rule in '%s[%i]' applied, '%s' becomes '%s'",
rule->config_file, rule->config_line, udev->kernel_name, rule->name); rule->config_file, rule->config_line, udev->kernel_name, rule->name);
@ -786,20 +806,25 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
if (udev->type != DEV_NET) if (udev->type != DEV_NET)
dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i", 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); udev->name, udev->owner, udev->group, udev->mode, udev->partitions);
}
break; if (rule->run[0] != '\0') {
char program[PATH_SIZE];
strlcpy(program, rule->run, sizeof(program));
apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
dbg("add run '%s'", program);
name_list_add(&udev->run_list, program, 0);
} }
if (rule->last_rule) { if (rule->last_rule) {
dbg("last rule to be applied"); dbg("last rule to be applied");
break; break;
} }
} }
} }
if (udev->name[0] == '\0') { if (udev->name[0] == '\0') {
/* no rule matched, so we use the kernel name */
strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); strlcpy(udev->name, udev->kernel_name, sizeof(udev->name));
info("no rule found, use kernel name '%s'", udev->name); info("no rule found, use kernel name '%s'", udev->name);
} }
@ -812,3 +837,116 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
return 0; return 0;
} }
int udev_rules_get_run(struct udevice *udev)
{
struct udev_rule *rule;
char program[PATH_SIZE];
/* look for a matching rule to apply */
list_for_each_entry(rule, &udev_rule_list, node) {
dbg("process rule");
if (rule->run[0] == '\0')
continue;
if (rule->name[0] != '\0' || rule->symlink[0] != '\0' ||
rule->mode != 0000 || rule->owner[0] != '\0' || rule->group[0] != '\0') {
dbg("skip rule that names a device");
continue;
}
if (rule->action_operation != KEY_OP_UNSET) {
dbg("check for " KEY_ACTION " rule->action='%s' udev->action='%s'",
rule->action, udev->action);
if (strcmp_pattern(rule->action, udev->action) != 0) {
dbg(KEY_ACTION " is not matching");
if (rule->action_operation != KEY_OP_NOMATCH)
continue;
} else {
dbg(KEY_ACTION " matches");
if (rule->action_operation == KEY_OP_NOMATCH)
continue;
}
dbg(KEY_ACTION " key is true");
}
if (rule->kernel_operation != KEY_OP_UNSET) {
dbg("check for " KEY_KERNEL " rule->kernel='%s' udev->kernel_name='%s'",
rule->kernel, udev->kernel_name);
if (strcmp_pattern(rule->kernel, udev->kernel_name) != 0) {
dbg(KEY_KERNEL " is not matching");
if (rule->kernel_operation != KEY_OP_NOMATCH)
continue;
} else {
dbg(KEY_KERNEL " matches");
if (rule->kernel_operation == KEY_OP_NOMATCH)
continue;
}
dbg(KEY_KERNEL " key is true");
}
if (rule->subsystem_operation != KEY_OP_UNSET) {
dbg("check for " KEY_SUBSYSTEM " rule->subsystem='%s' udev->subsystem='%s'",
rule->subsystem, udev->subsystem);
if (strcmp_pattern(rule->subsystem, udev->subsystem) != 0) {
dbg(KEY_SUBSYSTEM " is not matching");
if (rule->subsystem_operation != KEY_OP_NOMATCH)
continue;
} else {
dbg(KEY_SUBSYSTEM " matches");
if (rule->subsystem_operation == KEY_OP_NOMATCH)
continue;
}
dbg(KEY_SUBSYSTEM " key is true");
}
if (rule->env_pair_count) {
int i;
dbg("check for " KEY_ENV " pairs");
for (i = 0; i < rule->env_pair_count; i++) {
struct key_pair *pair;
const char *value;
pair = &rule->env_pair[i];
value = getenv(pair->name);
if (!value) {
dbg(KEY_ENV "{'%s'} is not found", pair->name);
continue;
}
if (strcmp_pattern(pair->value, value) != 0) {
dbg(KEY_ENV "{'%s'} is not matching", pair->name);
if (pair->operation != KEY_OP_NOMATCH)
continue;
} else {
dbg(KEY_ENV "{'%s'} matches", pair->name);
if (pair->operation == KEY_OP_NOMATCH)
continue;
}
}
dbg(KEY_ENV " key is true");
}
/* rule matches */
if (rule->ignore_device) {
info("configured rule in '%s[%i]' applied, '%s' is ignored",
rule->config_file, rule->config_line, udev->kernel_name);
udev->ignore_device = 1;
return 0;
}
strlcpy(program, rule->run, sizeof(program));
apply_format(udev, program, sizeof(program), NULL, NULL);
dbg("add run '%s'", program);
name_list_add(&udev->run_list, program, 0);
if (rule->last_rule) {
dbg("last rule to be applied");
break;
}
}
return 0;
}

View File

@ -30,6 +30,7 @@
#define KEY_KERNEL "KERNEL" #define KEY_KERNEL "KERNEL"
#define KEY_SUBSYSTEM "SUBSYSTEM" #define KEY_SUBSYSTEM "SUBSYSTEM"
#define KEY_ACTION "ACTION"
#define KEY_BUS "BUS" #define KEY_BUS "BUS"
#define KEY_ID "ID" #define KEY_ID "ID"
#define KEY_PROGRAM "PROGRAM" #define KEY_PROGRAM "PROGRAM"
@ -42,6 +43,7 @@
#define KEY_OWNER "OWNER" #define KEY_OWNER "OWNER"
#define KEY_GROUP "GROUP" #define KEY_GROUP "GROUP"
#define KEY_MODE "MODE" #define KEY_MODE "MODE"
#define KEY_RUN "RUN"
#define KEY_OPTIONS "OPTIONS" #define KEY_OPTIONS "OPTIONS"
#define OPTION_LAST_RULE "last_rule" #define OPTION_LAST_RULE "last_rule"
@ -75,6 +77,8 @@ struct udev_rule {
enum key_operation kernel_operation; enum key_operation kernel_operation;
char subsystem[NAME_SIZE]; char subsystem[NAME_SIZE];
enum key_operation subsystem_operation; enum key_operation subsystem_operation;
char action[NAME_SIZE];
enum key_operation action_operation;
char bus[NAME_SIZE]; char bus[NAME_SIZE];
enum key_operation bus_operation; enum key_operation bus_operation;
char id[NAME_SIZE]; char id[NAME_SIZE];
@ -95,6 +99,7 @@ struct udev_rule {
char owner[USER_SIZE]; char owner[USER_SIZE];
char group[USER_SIZE]; char group[USER_SIZE];
mode_t mode; mode_t mode;
char run[PATH_SIZE];
int last_rule; int last_rule;
int ignore_device; int ignore_device;
@ -109,6 +114,7 @@ extern struct list_head udev_rule_list;
extern int udev_rules_init(void); extern int udev_rules_init(void);
extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev); extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev);
extern int udev_rules_get_run(struct udevice *udev);
extern void udev_rules_close(void); extern void udev_rules_close(void);
#endif #endif

View File

@ -256,6 +256,13 @@ static int rules_parse(const char *filename)
continue; continue;
} }
if (strcasecmp(key, KEY_ACTION) == 0) {
strlcpy(rule.action, value, sizeof(rule.action));
rule.action_operation = operation;
valid = 1;
continue;
}
if (strcasecmp(key, KEY_BUS) == 0) { if (strcasecmp(key, KEY_BUS) == 0) {
strlcpy(rule.bus, value, sizeof(rule.bus)); strlcpy(rule.bus, value, sizeof(rule.bus));
rule.bus_operation = operation; rule.bus_operation = operation;
@ -379,6 +386,12 @@ static int rules_parse(const char *filename)
continue; continue;
} }
if (strcasecmp(key, KEY_RUN) == 0) {
strlcpy(rule.run, value, sizeof(rule.run));
valid = 1;
continue;
}
if (strcasecmp(key, KEY_OPTIONS) == 0) { if (strcasecmp(key, KEY_OPTIONS) == 0) {
if (strstr(value, OPTION_LAST_RULE) != NULL) { if (strstr(value, OPTION_LAST_RULE) != NULL) {
dbg("last rule to be applied"); dbg("last rule to be applied");

View File

@ -45,10 +45,14 @@ int udev_init_device(struct udevice *udev, const char* devpath, const char *subs
memset(udev, 0x00, sizeof(struct udevice)); memset(udev, 0x00, sizeof(struct udevice));
INIT_LIST_HEAD(&udev->symlink_list); INIT_LIST_HEAD(&udev->symlink_list);
INIT_LIST_HEAD(&udev->run_list);
if (subsystem) if (subsystem)
strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem)); strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem));
if (action)
strlcpy(udev->action, action, sizeof(udev->action));
if (devpath) { if (devpath) {
strlcpy(udev->devpath, devpath, sizeof(udev->devpath)); strlcpy(udev->devpath, devpath, sizeof(udev->devpath));
remove_trailing_char(udev->devpath, '/'); remove_trailing_char(udev->devpath, '/');

View File

@ -145,7 +145,7 @@ static void msg_queue_insert(struct hotplug_msg *msg)
} }
/* forks event and removes event from run queue when finished */ /* forks event and removes event from run queue when finished */
static void udev_run(struct hotplug_msg *msg) static void execute_udev(struct hotplug_msg *msg)
{ {
char *const argv[] = { "udev", msg->subsystem, NULL }; char *const argv[] = { "udev", msg->subsystem, NULL };
pid_t pid; pid_t pid;
@ -349,7 +349,7 @@ static void exec_queue_manager(void)
if (!msg) { if (!msg) {
/* move event to run list */ /* move event to run list */
list_move_tail(&loop_msg->node, &running_list); list_move_tail(&loop_msg->node, &running_list);
udev_run(loop_msg); execute_udev(loop_msg);
running++; running++;
dbg("moved seq %llu to running list", loop_msg->seqnum); dbg("moved seq %llu to running list", loop_msg->seqnum);
} else { } else {

View File

@ -126,6 +126,14 @@ static int add_device(const char *path, const char *subsystem)
udev_init_device(&udev, devpath, subsystem, "add"); udev_init_device(&udev, devpath, subsystem, "add");
udev_add_device(&udev, class_dev); udev_add_device(&udev, class_dev);
if (udev_run && !list_empty(&udev.run_list)) {
struct name_entry *name_loop;
dbg("executing run list");
list_for_each_entry(name_loop, &udev.run_list, node)
execute_command(name_loop->name, udev.subsystem);
}
/* run dev.d/ scripts if we created a node or changed a netif name */ /* run dev.d/ scripts if we created a node or changed a netif name */
if (udev_dev_d && udev.devname[0] != '\0') { if (udev_dev_d && udev.devname[0] != '\0') {
setenv("DEVNAME", udev.devname, 1); setenv("DEVNAME", udev.devname, 1);