mirror of
https://github.com/AuxXxilium/eudev.git
synced 2025-01-19 02:28:04 +07: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:
parent
e03a196a0d
commit
821d0ec803
1
Makefile
1
Makefile
@ -51,7 +51,6 @@ VERSION = 056
|
||||
INSTALL_DIR = /usr/local/bin
|
||||
RELEASE_NAME = $(ROOT)-$(VERSION)
|
||||
LOCAL_CFG_DIR = etc/udev
|
||||
HOTPLUG_EXEC = $(ROOT)
|
||||
DESTDIR =
|
||||
KERNEL_DIR = /lib/modules/${shell uname -r}/build
|
||||
|
||||
|
@ -1243,6 +1243,53 @@ KERNEL=="sda", SYSFS{nothing}!="", NAME="not-1-ok"
|
||||
KERNEL=="sda", SYSFS{nothing}=="", NAME="not-2-ok"
|
||||
KERNEL=="sda", SYSFS{vendor}!="", NAME="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
|
||||
},
|
||||
);
|
||||
|
13
udev.8.in
13
udev.8.in
@ -111,6 +111,9 @@ Match the kernel device name.
|
||||
.B SUBSYSTEM
|
||||
Match the kernel subsystem name.
|
||||
.TP
|
||||
.B ACTION
|
||||
Match the kernel action name.
|
||||
.TP
|
||||
.B DRIVER
|
||||
Match the kernel driver name.
|
||||
.TP
|
||||
@ -170,6 +173,9 @@ distribution provided rules file.
|
||||
The permissions for the device node. Every specified value overwrites the
|
||||
compiled-in default value.
|
||||
.TP
|
||||
.B RUN
|
||||
Add a program to the list of programs to be executed for a specific device.
|
||||
.TP
|
||||
.B OPTIONS
|
||||
.B last_rule
|
||||
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 }
|
||||
The content of a sysfs attribute.
|
||||
.TP
|
||||
.B %r
|
||||
The udev_root value.
|
||||
.TP
|
||||
.B %e
|
||||
If a device node already exists with the name, the smallest positive
|
||||
decimal integer N is substituted such that the resulting name doesn't
|
||||
@ -344,6 +353,9 @@ config file.
|
||||
.B UDEV_LOG
|
||||
Overrides the log priority specified in the config file.
|
||||
.TP
|
||||
.B UDEV_RUN
|
||||
If set to "0", it disables the execution of programs added by rules.
|
||||
.TP
|
||||
.B UDEV_NO_DEVD
|
||||
The default behavior of
|
||||
.B udev
|
||||
@ -356,7 +368,6 @@ will skip this step.
|
||||
.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"
|
||||
|
19
udev.c
19
udev.c
@ -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) {
|
||||
udev_rules_init();
|
||||
|
||||
if (strcmp(action, "add") == 0) {
|
||||
/* wait for sysfs and possibly add node */
|
||||
dbg("udev add");
|
||||
@ -178,9 +180,6 @@ int main(int argc, char *argv[], char *envp[])
|
||||
|
||||
wait_for_class_device(class_dev, &error);
|
||||
|
||||
/* init rules */
|
||||
udev_rules_init();
|
||||
|
||||
/* name, create node, store in db */
|
||||
retval = udev_add_device(&udev, class_dev);
|
||||
|
||||
@ -195,10 +194,24 @@ int main(int argc, char *argv[], char *envp[])
|
||||
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 */
|
||||
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 */
|
||||
if (udev.devname[0] != '\0') {
|
||||
setenv("DEVNAME", udev.devname, 1);
|
||||
|
4
udev.h
4
udev.h
@ -58,6 +58,7 @@ enum device_type {
|
||||
struct udevice {
|
||||
char devpath[PATH_SIZE];
|
||||
char subsystem[NAME_SIZE];
|
||||
char action[NAME_SIZE];
|
||||
|
||||
enum device_type type;
|
||||
char name[PATH_SIZE];
|
||||
@ -67,9 +68,11 @@ struct udevice {
|
||||
char group[USER_SIZE];
|
||||
mode_t mode;
|
||||
dev_t devt;
|
||||
struct list_head run_list;
|
||||
|
||||
char tmp_node[PATH_SIZE];
|
||||
int partitions;
|
||||
int ignore_device;
|
||||
int ignore_remove;
|
||||
int config_line;
|
||||
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_rules_filename[PATH_SIZE];
|
||||
extern int udev_log_priority;
|
||||
extern int udev_run;
|
||||
extern int udev_dev_d;
|
||||
extern int udev_hotplug_d;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
dbg("adding name='%s'", udev->name);
|
||||
|
||||
|
@ -44,6 +44,7 @@ char udev_db_path[PATH_SIZE];
|
||||
char udev_config_filename[PATH_SIZE];
|
||||
char udev_rules_filename[PATH_SIZE];
|
||||
int udev_log_priority;
|
||||
int udev_run;
|
||||
int udev_dev_d;
|
||||
int udev_hotplug_d;
|
||||
|
||||
@ -217,10 +218,16 @@ void udev_init_config(void)
|
||||
strcpy(udev_config_filename, UDEV_CONFIG_FILE);
|
||||
strcpy(udev_rules_filename, UDEV_RULES_FILE);
|
||||
udev_log_priority = LOG_ERR;
|
||||
udev_run = 1;
|
||||
udev_dev_d = 1;
|
||||
udev_hotplug_d = 1;
|
||||
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");
|
||||
if (env && string_is_true(env))
|
||||
udev_dev_d = 0;
|
||||
|
148
udev_rules.c
148
udev_rules.c
@ -474,6 +474,21 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,
|
||||
{
|
||||
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) {
|
||||
dbg("check for " KEY_KERNEL " rule->kernel='%s' udev_kernel_name='%s'",
|
||||
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) {
|
||||
dbg("process rule");
|
||||
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 */
|
||||
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;
|
||||
udev->ignore_device = 1;
|
||||
return 0;
|
||||
}
|
||||
if (rule->ignore_remove) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* rule matches */
|
||||
/* set name, later rules with name set will be ignored */
|
||||
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);
|
||||
@ -786,20 +806,25 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
|
||||
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 (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) {
|
||||
dbg("last rule to be applied");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (udev->name[0] == '\0') {
|
||||
/* no rule matched, so we use the kernel name */
|
||||
strlcpy(udev->name, udev->kernel_name, sizeof(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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#define KEY_KERNEL "KERNEL"
|
||||
#define KEY_SUBSYSTEM "SUBSYSTEM"
|
||||
#define KEY_ACTION "ACTION"
|
||||
#define KEY_BUS "BUS"
|
||||
#define KEY_ID "ID"
|
||||
#define KEY_PROGRAM "PROGRAM"
|
||||
@ -42,6 +43,7 @@
|
||||
#define KEY_OWNER "OWNER"
|
||||
#define KEY_GROUP "GROUP"
|
||||
#define KEY_MODE "MODE"
|
||||
#define KEY_RUN "RUN"
|
||||
#define KEY_OPTIONS "OPTIONS"
|
||||
|
||||
#define OPTION_LAST_RULE "last_rule"
|
||||
@ -75,6 +77,8 @@ struct udev_rule {
|
||||
enum key_operation kernel_operation;
|
||||
char subsystem[NAME_SIZE];
|
||||
enum key_operation subsystem_operation;
|
||||
char action[NAME_SIZE];
|
||||
enum key_operation action_operation;
|
||||
char bus[NAME_SIZE];
|
||||
enum key_operation bus_operation;
|
||||
char id[NAME_SIZE];
|
||||
@ -95,6 +99,7 @@ struct udev_rule {
|
||||
char owner[USER_SIZE];
|
||||
char group[USER_SIZE];
|
||||
mode_t mode;
|
||||
char run[PATH_SIZE];
|
||||
|
||||
int last_rule;
|
||||
int ignore_device;
|
||||
@ -109,6 +114,7 @@ extern struct list_head udev_rule_list;
|
||||
|
||||
extern int udev_rules_init(void);
|
||||
extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev);
|
||||
extern int udev_rules_get_run(struct udevice *udev);
|
||||
extern void udev_rules_close(void);
|
||||
|
||||
#endif
|
||||
|
@ -256,6 +256,13 @@ static int rules_parse(const char *filename)
|
||||
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) {
|
||||
strlcpy(rule.bus, value, sizeof(rule.bus));
|
||||
rule.bus_operation = operation;
|
||||
@ -379,6 +386,12 @@ static int rules_parse(const char *filename)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcasecmp(key, KEY_RUN) == 0) {
|
||||
strlcpy(rule.run, value, sizeof(rule.run));
|
||||
valid = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcasecmp(key, KEY_OPTIONS) == 0) {
|
||||
if (strstr(value, OPTION_LAST_RULE) != NULL) {
|
||||
dbg("last rule to be applied");
|
||||
|
@ -45,10 +45,14 @@ int udev_init_device(struct udevice *udev, const char* devpath, const char *subs
|
||||
|
||||
memset(udev, 0x00, sizeof(struct udevice));
|
||||
INIT_LIST_HEAD(&udev->symlink_list);
|
||||
INIT_LIST_HEAD(&udev->run_list);
|
||||
|
||||
if (subsystem)
|
||||
strlcpy(udev->subsystem, subsystem, sizeof(udev->subsystem));
|
||||
|
||||
if (action)
|
||||
strlcpy(udev->action, action, sizeof(udev->action));
|
||||
|
||||
if (devpath) {
|
||||
strlcpy(udev->devpath, devpath, sizeof(udev->devpath));
|
||||
remove_trailing_char(udev->devpath, '/');
|
||||
|
4
udevd.c
4
udevd.c
@ -145,7 +145,7 @@ static void msg_queue_insert(struct hotplug_msg *msg)
|
||||
}
|
||||
|
||||
/* 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 };
|
||||
pid_t pid;
|
||||
@ -349,7 +349,7 @@ static void exec_queue_manager(void)
|
||||
if (!msg) {
|
||||
/* move event to run list */
|
||||
list_move_tail(&loop_msg->node, &running_list);
|
||||
udev_run(loop_msg);
|
||||
execute_udev(loop_msg);
|
||||
running++;
|
||||
dbg("moved seq %llu to running list", loop_msg->seqnum);
|
||||
} else {
|
||||
|
@ -126,6 +126,14 @@ static int add_device(const char *path, const char *subsystem)
|
||||
udev_init_device(&udev, devpath, subsystem, "add");
|
||||
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 */
|
||||
if (udev_dev_d && udev.devname[0] != '\0') {
|
||||
setenv("DEVNAME", udev.devname, 1);
|
||||
|
Loading…
Reference in New Issue
Block a user