diff --git a/NEWS b/NEWS index b27d38be77..9d691c0640 100644 --- a/NEWS +++ b/NEWS @@ -100,6 +100,11 @@ CHANGES WITH 246 in spe: can now be suspended or resumed either using new systemctl verbs, freeze and thaw respectively, or via D-Bus. + * systemd-udevd gained new configuration option timeout_signal= as well + as coresponding kernel command line option udev.timeout_signal. + The option can be used to configure the UNIX signal that the main + daemon sends to the worker processes on timeout. + * A new sd-path.h API has been added to libsystemd. It provides a simple API for retrieving various search paths and primary directories for various resources. diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 0f2972a0b6..7e59a1aaed 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -271,6 +271,9 @@ rd.udev.exec_delay= udev.event_timeout= rd.udev.event_timeout= + udev.timeout_signal= + rd.udev.timeout_signal= + net.ifnames= net.naming-scheme= diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index c267bb2b11..55edc17353 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -105,6 +105,21 @@ + + + + + Set the signal which systemd-udevd will send to + forked off processes after reaching event timeout. The setting can be overriden + at boot time with the kernel command line option + udev.timeout_signal=. Setting to SIGABRT + may be helpful in order to debug worker timeouts. Defaults to + SIGKILL. Note that setting the option on the command line + overrides the setting from the configuration file. + + + + @@ -160,6 +175,15 @@ terminated due to kernel drivers taking too long to initialize. + + udev.timeout_signal= + rd.udev.timeout_signal= + + Specifies a signal that systemd-udevd will send to + workers on timeout. Note that kernel command line option overrides both the + setting in the configuration file and the one on the program command line. + + net.ifnames= diff --git a/man/udev.conf.xml b/man/udev.conf.xml index 04301c81ab..0b5669b442 100644 --- a/man/udev.conf.xml +++ b/man/udev.conf.xml @@ -97,6 +97,16 @@ This is the same as the option. + + + timeout_signal= + + + Specifies a signal that systemd-udevd will send on worker + timeouts. Note that both workers and spawned processes will be killed using this + signal. Defaults to . + + diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index ea96f5b49d..419c73318f 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -7,6 +7,7 @@ #include "env-file.h" #include "log.h" #include "parse-util.h" +#include "signal-util.h" #include "string-table.h" #include "string-util.h" #include "udev-util.h" @@ -23,9 +24,10 @@ int udev_parse_config_full( unsigned *ret_children_max, usec_t *ret_exec_delay_usec, usec_t *ret_event_timeout_usec, - ResolveNameTiming *ret_resolve_name_timing) { + ResolveNameTiming *ret_resolve_name_timing, + int *ret_timeout_signal) { - _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL; + _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL, *timeout_signal = NULL; int r; r = parse_env_file(NULL, "/etc/udev/udev.conf", @@ -33,7 +35,8 @@ int udev_parse_config_full( "children_max", &children_max, "exec_delay", &exec_delay, "event_timeout", &event_timeout, - "resolve_names", &resolve_names); + "resolve_names", &resolve_names, + "timeout_signal", &timeout_signal); if (r == -ENOENT) return 0; if (r < 0) @@ -65,21 +68,21 @@ int udev_parse_config_full( r = safe_atou(children_max, ret_children_max); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse children_max=%s, ignoring: %m", children_max); + "failed to parse children_max=%s, ignoring: %m", children_max); } if (ret_exec_delay_usec && exec_delay) { r = parse_sec(exec_delay, ret_exec_delay_usec); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse exec_delay=%s, ignoring: %m", exec_delay); + "failed to parse exec_delay=%s, ignoring: %m", exec_delay); } if (ret_event_timeout_usec && event_timeout) { r = parse_sec(event_timeout, ret_event_timeout_usec); if (r < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse event_timeout=%s, ignoring: %m", event_timeout); + "failed to parse event_timeout=%s, ignoring: %m", event_timeout); } if (ret_resolve_name_timing && resolve_names) { @@ -88,11 +91,20 @@ int udev_parse_config_full( t = resolve_name_timing_from_string(resolve_names); if (t < 0) log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, - "failed to set parse resolve_names=%s, ignoring.", resolve_names); + "failed to parse resolve_names=%s, ignoring.", resolve_names); else *ret_resolve_name_timing = t; } + if (ret_timeout_signal && timeout_signal) { + r = signal_from_string(timeout_signal); + if (r < 0) + log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r, + "failed to parse timeout_signal=%s, ignoring: %m", timeout_signal); + else + *ret_timeout_signal = r; + } + return 0; } diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index f62c4c56e3..c35d29344f 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -21,10 +21,11 @@ int udev_parse_config_full( unsigned *ret_children_max, usec_t *ret_exec_delay_usec, usec_t *ret_event_timeout_usec, - ResolveNameTiming *ret_resolve_name_timing); + ResolveNameTiming *ret_resolve_name_timing, + int *ret_timeout_signal); static inline int udev_parse_config(void) { - return udev_parse_config_full(NULL, NULL, NULL, NULL); + return udev_parse_config_full(NULL, NULL, NULL, NULL, NULL); } int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret); diff --git a/src/test/test-udev.c b/src/test/test-udev.c index e87a8ec034..2c32f898c6 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -122,8 +122,8 @@ static int run(int argc, char *argv[]) { } } - udev_event_execute_rules(event, 3 * USEC_PER_SEC, NULL, rules); - udev_event_execute_run(event, 3 * USEC_PER_SEC); + udev_event_execute_rules(event, 3 * USEC_PER_SEC, SIGKILL, NULL, rules); + udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL); return 0; } diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index eb51139e51..e1c3e430ea 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -41,6 +41,7 @@ typedef struct Spawn { pid_t pid; usec_t timeout_warn_usec; usec_t timeout_usec; + int timeout_signal; usec_t event_birth_usec; bool accept_failure; int fd_stdout; @@ -596,7 +597,7 @@ static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(spawn); - kill_and_sigcont(spawn->pid, SIGKILL); + kill_and_sigcont(spawn->pid, spawn->timeout_signal); log_device_error(spawn->device, "Spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, @@ -717,6 +718,7 @@ static int spawn_wait(Spawn *spawn) { int udev_event_spawn(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, bool accept_failure, const char *cmd, char *result, size_t ressize) { @@ -793,6 +795,7 @@ int udev_event_spawn(UdevEvent *event, .accept_failure = accept_failure, .timeout_warn_usec = udev_warn_timeout(timeout_usec), .timeout_usec = timeout_usec, + .timeout_signal = timeout_signal, .event_birth_usec = event->birth_usec, .fd_stdout = outpipe[READ_END], .fd_stderr = errpipe[READ_END], @@ -896,6 +899,7 @@ static int update_devnode(UdevEvent *event) { static void event_execute_rules_on_remove( UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules) { @@ -917,7 +921,7 @@ static void event_execute_rules_on_remove( if (sd_device_get_devnum(dev, NULL) >= 0) (void) udev_watch_end(dev); - (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list); + (void) udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list); if (sd_device_get_devnum(dev, NULL) >= 0) (void) udev_node_remove(dev); @@ -944,6 +948,7 @@ static int udev_event_on_move(UdevEvent *event) { int udev_event_execute_rules(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules) { const char *subsystem; @@ -965,7 +970,7 @@ int udev_event_execute_rules(UdevEvent *event, return log_device_error_errno(dev, r, "Failed to get ACTION: %m"); if (action == DEVICE_ACTION_REMOVE) { - event_execute_rules_on_remove(event, timeout_usec, properties_list, rules); + event_execute_rules_on_remove(event, timeout_usec, timeout_signal, properties_list, rules); return 0; } @@ -983,7 +988,7 @@ int udev_event_execute_rules(UdevEvent *event, return r; } - r = udev_rules_apply_to_event(rules, event, timeout_usec, properties_list); + r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list); if (r < 0) return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m"); @@ -1016,7 +1021,7 @@ int udev_event_execute_rules(UdevEvent *event, return 0; } -void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) { +void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) { const char *command; void *val; Iterator i; @@ -1040,7 +1045,8 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) { } log_device_debug(event->dev, "Running command \"%s\"", command); - r = udev_event_spawn(event, timeout_usec, false, command, NULL, 0); + + r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0); if (r < 0) log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command); else if (r > 0) /* returned value is positive when program fails */ diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h index a05e125259..a78cea440d 100644 --- a/src/udev/udev-event.h +++ b/src/udev/udev-event.h @@ -54,13 +54,15 @@ ssize_t udev_event_apply_format(UdevEvent *event, int udev_check_format(const char *value, size_t *offset, const char **hint); int udev_event_spawn(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, bool accept_failure, const char *cmd, char *result, size_t ressize); int udev_event_execute_rules(UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRules *rules); -void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec); +void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal); static inline usec_t udev_warn_timeout(usec_t timeout_usec) { return DIV_ROUND_UP(timeout_usec, 3); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index e9975030aa..1b501da39f 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -1520,6 +1520,7 @@ static int udev_rule_apply_token_to_event( sd_device *dev, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list) { UdevRuleToken *token; @@ -1662,7 +1663,7 @@ static int udev_rule_apply_token_to_event( (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf); - r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof(result)); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result)); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); @@ -1732,7 +1733,7 @@ static int udev_rule_apply_token_to_event( (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf); - r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof result); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); @@ -2162,7 +2163,8 @@ static bool token_is_for_parents(UdevRuleToken *token) { static int udev_rule_apply_parent_token_to_event( UdevRules *rules, - UdevEvent *event) { + UdevEvent *event, + int timeout_signal) { UdevRuleLine *line; UdevRuleToken *head; @@ -2175,7 +2177,7 @@ static int udev_rule_apply_parent_token_to_event( LIST_FOREACH(tokens, line->current_token, head) { if (!token_is_for_parents(line->current_token)) return true; /* All parent tokens match. */ - r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, NULL); + r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, timeout_signal, NULL); if (r < 0) return r; if (r == 0) @@ -2196,6 +2198,7 @@ static int udev_rule_apply_line_to_event( UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list, UdevRuleLine **next_line) { @@ -2229,7 +2232,7 @@ static int udev_rule_apply_line_to_event( if (parents_done) continue; - r = udev_rule_apply_parent_token_to_event(rules, event); + r = udev_rule_apply_parent_token_to_event(rules, event, timeout_signal); if (r <= 0) return r; @@ -2237,7 +2240,7 @@ static int udev_rule_apply_line_to_event( continue; } - r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, properties_list); + r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, timeout_signal, properties_list); if (r <= 0) return r; } @@ -2252,6 +2255,7 @@ int udev_rules_apply_to_event( UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list) { UdevRuleFile *file; @@ -2264,7 +2268,7 @@ int udev_rules_apply_to_event( LIST_FOREACH(rule_files, file, rules->rule_files) { rules->current_file = file; LIST_FOREACH_SAFE(rule_lines, file->current_line, next_line, file->rule_lines) { - r = udev_rule_apply_line_to_event(rules, event, timeout_usec, properties_list, &next_line); + r = udev_rule_apply_line_to_event(rules, event, timeout_usec, timeout_signal, properties_list, &next_line); if (r < 0) return r; } diff --git a/src/udev/udev-rules.h b/src/udev/udev-rules.h index 9fff5da7b5..6d94a468c3 100644 --- a/src/udev/udev-rules.h +++ b/src/udev/udev-rules.h @@ -23,5 +23,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free); bool udev_rules_check_timestamp(UdevRules *rules); int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event, usec_t timeout_usec, + int timeout_signal, Hashmap *properties_list); int udev_rules_apply_static_dev_perms(UdevRules *rules); diff --git a/src/udev/udev.conf b/src/udev/udev.conf index 7deb7715be..07d7f0cd1a 100644 --- a/src/udev/udev.conf +++ b/src/udev/udev.conf @@ -7,4 +7,5 @@ #children_max= #exec_delay= #event_timeout=180 +#timeout_signal=SIGKILL #resolve_names=early diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 7ad9993f09..8b34809899 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -143,7 +143,7 @@ int test_main(int argc, char *argv[], void *userdata) { assert_se(sigfillset(&mask) >= 0); assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0); - udev_event_execute_rules(event, 60 * USEC_PER_SEC, NULL, rules); + udev_event_execute_rules(event, 60 * USEC_PER_SEC, SIGKILL, NULL, rules); FOREACH_DEVICE_PROPERTY(dev, key, value) printf("%s=%s\n", key, value); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 5f5c1e0ae4..a2b8c6162c 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -75,6 +75,7 @@ static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY; static unsigned arg_children_max = 0; static usec_t arg_exec_delay_usec = 0; static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; +static int arg_timeout_signal = SIGKILL; typedef struct Manager { sd_event *event; @@ -228,7 +229,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(event); assert(event->worker); - kill_and_sigcont(event->worker->pid, SIGKILL); + kill_and_sigcont(event->worker->pid, arg_timeout_signal); event->worker->state = WORKER_KILLED; log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum); @@ -412,11 +413,11 @@ static int worker_process_device(Manager *manager, sd_device *dev) { return r; /* apply rules, create node, symlinks */ - r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, manager->properties, manager->rules); + r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_timeout_signal, manager->properties, manager->rules); if (r < 0) return r; - udev_event_execute_run(udev_event, arg_event_timeout_usec); + udev_event_execute_run(udev_event, arg_event_timeout_usec, arg_timeout_signal); if (!manager->rtnl) /* in case rtnl was initialized */ @@ -1455,6 +1456,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = parse_sec(value, &arg_exec_delay_usec); + } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = signal_from_string(value); + if (r > 0) + arg_timeout_signal = r; } else if (startswith(key, "udev.")) log_warning("Unknown udev kernel command line option \"%s\", ignoring", key); @@ -1492,15 +1500,20 @@ static int help(void) { } static int parse_argv(int argc, char *argv[]) { + enum { + ARG_TIMEOUT_SIGNAL, + }; + static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "event-timeout", required_argument, NULL, 't' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, + { "daemon", no_argument, NULL, 'd' }, + { "debug", no_argument, NULL, 'D' }, + { "children-max", required_argument, NULL, 'c' }, + { "exec-delay", required_argument, NULL, 'e' }, + { "event-timeout", required_argument, NULL, 't' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL }, {} }; @@ -1525,6 +1538,14 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg); break; + case ARG_TIMEOUT_SIGNAL: + r = signal_from_string(optarg); + if (r <= 0) + log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg); + else + arg_timeout_signal = r; + + break; case 't': r = parse_sec(optarg, &arg_event_timeout_usec); if (r < 0) @@ -1722,7 +1743,7 @@ int run_udevd(int argc, char *argv[]) { log_set_target(LOG_TARGET_AUTO); log_open(); - udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing); + udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing, &arg_timeout_signal); log_parse_environment(); log_open(); /* Done again to update after reading configuration. */