]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: make signal that we use to kill workers on timeout configurable
authorMichal Sekletár <msekleta@redhat.com>
Thu, 9 Apr 2020 09:14:25 +0000 (11:14 +0200)
committerMichal Sekletár <msekleta@redhat.com>
Fri, 5 Jun 2020 09:09:17 +0000 (11:09 +0200)
14 files changed:
NEWS
man/kernel-command-line.xml
man/systemd-udevd.service.xml
man/udev.conf.xml
src/shared/udev-util.c
src/shared/udev-util.h
src/test/test-udev.c
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udev.conf
src/udev/udevadm-test.c
src/udev/udevd.c

diff --git a/NEWS b/NEWS
index b27d38be77052ce98cbdd714d11f1781c0688d2e..9d691c0640b3b00394cb42ae9fec80a44f72ab82 100644 (file)
--- 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.
index 0f2972a0b6840346228a56198f3e8d6b8d85eddc..7e59a1aaede8514e9ab55e20ed7612a17b166593 100644 (file)
         <term><varname>rd.udev.exec_delay=</varname></term>
         <term><varname>udev.event_timeout=</varname></term>
         <term><varname>rd.udev.event_timeout=</varname></term>
+        <term><varname>udev.timeout_signal=</varname></term>
+        <term><varname>rd.udev.timeout_signal=</varname></term>
+
         <term><varname>net.ifnames=</varname></term>
         <term><varname>net.naming-scheme=</varname></term>
 
index c267bb2b114b3da1f5154fbb06bf3bf46b286693..55edc17353f43f9721c545f1f51116650062a687 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-s</option></term>
+        <term><option>--timeout-signal=</option></term>
+        <listitem>
+          <para>Set the signal which <filename>systemd-udevd</filename> will send to
+          forked off processes after reaching event timeout. The setting can be overriden
+          at boot time with the kernel command line option
+          <varname>udev.timeout_signal=</varname>. Setting to <constant>SIGABRT</constant>
+          may be helpful in order to debug worker timeouts. Defaults to
+          <constant>SIGKILL</constant>. Note that setting the option on the command line
+          overrides the setting from the configuration file.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-N=</option></term>
         <term><option>--resolve-names=</option></term>
           terminated due to kernel drivers taking too long to initialize.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>udev.timeout_signal=</varname></term>
+        <term><varname>rd.udev.timeout_signal=</varname></term>
+        <listitem>
+          <para>Specifies a signal that <filename>systemd-udevd</filename> 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.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>net.ifnames=</varname></term>
         <listitem>
index 04301c81ab080595a1605c5a222b4759bc3c80f2..0b5669b4425b8dfcbc0e14c0a320c351151ef058 100644 (file)
           <para>This is the same as the <option>--resolve-names=</option> option.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>timeout_signal=</varname></term>
+
+        <listitem>
+          <para>Specifies a signal that <filename>systemd-udevd</filename> will send on worker
+          timeouts. Note that both workers and spawned processes will be killed using this
+          signal. Defaults to <option>SIGKILL</option>.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
 
     <para>
index ea96f5b49dccc428de07869156fb8e6c78fad35e..419c73318fbbcac35ffca3e483f6c736a94d9af2 100644 (file)
@@ -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;
 }
 
index f62c4c56e394b5007db4f9c0e64a3cb514a12fc8..c35d29344f731b39ecb7fedffaae7d6c4fbb203e 100644 (file)
@@ -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);
index e87a8ec03405aff260103d8865ff571fbcc37fcd..2c32f898c6573e7d36db76539d4b98dbba72bd26 100644 (file)
@@ -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;
 }
index eb51139e519c9cbbeef18850dde01396a94a1125..e1c3e430eaa119bba2d535e39b4e5fe07225be96 100644 (file)
@@ -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 */
index a05e1252595a5fd41893d82d307a3d9e483e64aa..a78cea440da22c205dbd1035f60facdef0482cd6 100644 (file)
@@ -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);
index e9975030aae3cf518ed44a7cb2d54150b432efe1..1b501da39f14abd3f0ce29de7fd6a095e90082f8 100644 (file)
@@ -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;
                 }
index 9fff5da7b5d48e30969d9f5a122440a9c5b9a735..6d94a468c33cfe264c2ab5d7a0773dc5c1696eb7 100644 (file)
@@ -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);
index 7deb7715bed4329c6b02018568de8869713fe666..07d7f0cd1a5da95f4610b7130a011c7ce302eb8e 100644 (file)
@@ -7,4 +7,5 @@
 #children_max=
 #exec_delay=
 #event_timeout=180
+#timeout_signal=SIGKILL
 #resolve_names=early
index 7ad9993f09e979fe84e5db396f5965f22de6d774..8b348098990bd230d029e936deffb080c5604a76 100644 (file)
@@ -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);
index 5f5c1e0ae4b4d6b9909e566aaa7a89a3ae4a8c5c..a2b8c6162c16d17a9d12bab98cc89010e0c60659 100644 (file)
@@ -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. */