udev-ctrl: split out logic of waiting for reply to udev_ctrl_wait()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 25 Jan 2019 15:33:08 +0000 (00:33 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 19 Feb 2019 21:17:42 +0000 (06:17 +0900)
This makes `udevadm control` can send multiple commands in one
connection.

src/udev/udev-ctrl.c
src/udev/udev-ctrl.h
src/udev/udevadm-control.c
src/udev/udevadm-settle.c
src/udev/udevadm-trigger.c

index 7b0a556..59ce5c1 100644 (file)
@@ -47,6 +47,7 @@ struct udev_ctrl {
         bool bound:1;
         bool cleanup_socket:1;
         bool connected:1;
+        bool maybe_disconnected:1;
         sd_event *event;
         sd_event_source *event_source;
         sd_event_source *event_source_connect;
@@ -239,9 +240,14 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32
                 return 0;
         }
 
+        if (msg_wire.type == _UDEV_CTRL_END_MESSAGES)
+                return 0;
+
         if (uctrl->callback)
                 (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata);
 
+        /* Do not disconnect and wait for next message. */
+        uctrl = udev_ctrl_unref(uctrl);
         return 0;
 }
 
@@ -319,13 +325,16 @@ int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void
         return 0;
 }
 
-static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, usec_t timeout) {
+int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) {
         struct udev_ctrl_msg_wire ctrl_msg_wire = {
                 .version = "udev-" STRINGIFY(PROJECT_VERSION),
                 .magic = UDEV_CTRL_MAGIC,
                 .type = type,
         };
 
+        if (uctrl->maybe_disconnected)
+                return -ENOANO; /* to distinguish this from other errors. */
+
         if (buf)
                 strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), buf);
         else
@@ -336,59 +345,62 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
                         return -errno;
                 uctrl->connected = true;
         }
+
         if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0)
                 return -errno;
 
-        /* wait for peer message handling or disconnect */
-        for (;;) {
-                struct pollfd pfd = {
-                        .fd = uctrl->sock,
-                        .events = POLLIN,
-                };
-                int r;
-
-                r = poll(&pfd, 1, DIV_ROUND_UP(timeout, USEC_PER_MSEC));
-                if (r < 0) {
-                        if (errno == EINTR)
-                                continue;
-                        return -errno;
-                }
-                if (r == 0)
-                        return -ETIMEDOUT;
-                if (pfd.revents & POLLERR)
-                        return -EIO;
-                return 0;
-        }
-}
+        if (type == UDEV_CTRL_EXIT)
+                uctrl->maybe_disconnected = true;
 
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout);
+        return 0;
 }
 
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout);
+static int udev_ctrl_wait_io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        return sd_event_exit(sd_event_source_get_event(s), 0);
 }
 
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout);
-}
+int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) {
+        _cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
+        int r;
 
-int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout);
-}
+        assert(uctrl);
 
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout);
-}
+        if (uctrl->sock < 0)
+                return 0;
+        if (!uctrl->connected)
+                return 0;
 
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout);
-}
+        if (!uctrl->maybe_disconnected) {
+                r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, 0, NULL);
+                if (r < 0)
+                        return r;
+        }
 
-int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout);
-}
+        if (timeout == 0)
+                return 0;
+
+        if (!uctrl->event) {
+                r = udev_ctrl_attach_event(uctrl, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, udev_ctrl_wait_io_handler, NULL);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl-wait-io");
+
+        if (timeout != USEC_INFINITY) {
+                usec_t usec;
+
+                usec = now(clock_boottime_or_monotonic()) + timeout;
+                r = sd_event_add_time(uctrl->event, &source_timeout, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT));
+                if (r < 0)
+                        return r;
+
+                (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-io");
+        }
 
-int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout) {
-        return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout);
+        return sd_event_loop(uctrl->event);
 }
index 4e8c00b..2c84a8b 100644 (file)
@@ -9,7 +9,7 @@
 struct udev_ctrl;
 
 enum udev_ctrl_msg_type {
-        UDEV_CTRL_UNKNOWN,
+        _UDEV_CTRL_END_MESSAGES,
         UDEV_CTRL_SET_LOG_LEVEL,
         UDEV_CTRL_STOP_EXEC_QUEUE,
         UDEV_CTRL_START_EXEC_QUEUE,
@@ -41,13 +41,39 @@ int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event);
 int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
 sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl);
 
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout);
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout);
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout);
-int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout);
-int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout);
-int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout);
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout);
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout);
+int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout);
+
+int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf);
+static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
+}
+
+static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
+}
+
+static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
+}
+
+static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL);
+}
+
+static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
+}
+
+static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
+}
+
+static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL);
+}
+
+static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) {
+        return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL);
+}
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
index 01f6794..f9b3e95 100644 (file)
@@ -93,8 +93,10 @@ int control_main(int argc, char *argv[], void *userdata) {
         while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
                 switch (c) {
                 case 'e':
-                        r = udev_ctrl_send_exit(uctrl, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_exit(uctrl);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --exit after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send exit request: %m");
                         break;
                 case 'l':
@@ -102,31 +104,41 @@ int control_main(int argc, char *argv[], void *userdata) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse log priority '%s': %m", optarg);
 
-                        r = udev_ctrl_send_set_log_level(uctrl, r, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_set_log_level(uctrl, r);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --log-priority after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send request to set log level: %m");
                         break;
                 case 's':
-                        r = udev_ctrl_send_stop_exec_queue(uctrl, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_stop_exec_queue(uctrl);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --stop-exec-queue after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send request to stop exec queue: %m");
                         break;
                 case 'S':
-                        r = udev_ctrl_send_start_exec_queue(uctrl, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_start_exec_queue(uctrl);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --start-exec-queue after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send request to start exec queue: %m");
                         break;
                 case 'R':
-                        r = udev_ctrl_send_reload(uctrl, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_reload(uctrl);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --reload after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send reload request: %m");
                         break;
                 case 'p':
                         if (!strchr(optarg, '='))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "expect <KEY>=<value> instead of '%s'", optarg);
 
-                        r = udev_ctrl_send_set_env(uctrl, optarg, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_set_env(uctrl, optarg);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --property after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send request to update environment: %m");
                         break;
                 case 'm': {
@@ -136,15 +148,19 @@ int control_main(int argc, char *argv[], void *userdata) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse maximum number of events '%s': %m", optarg);
 
-                        r = udev_ctrl_send_set_children_max(uctrl, i, timeout);
-                        if (r < 0)
+                        r = udev_ctrl_send_set_children_max(uctrl, i);
+                        if (r == -ENOANO)
+                                log_warning("Cannot specify --children-max after --exit, ignoring.");
+                        else if (r < 0)
                                 return log_error_errno(r, "Failed to send request to set number of children: %m");
                         break;
                 }
                 case ARG_PING:
-                        r = udev_ctrl_send_ping(uctrl, timeout);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to connect to udev daemon: %m");
+                        r = udev_ctrl_send_ping(uctrl);
+                        if (r == -ENOANO)
+                                log_error("Cannot specify --ping after --exit, ignoring.");
+                        else if (r < 0)
+                                return log_error_errno(r, "Failed to send a ping message: %m");
                         break;
                 case 't':
                         r = parse_sec(optarg, &timeout);
@@ -165,5 +181,9 @@ int control_main(int argc, char *argv[], void *userdata) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Extraneous argument: %s", argv[optind]);
 
+        r = udev_ctrl_wait(uctrl, timeout);
+        if (r < 0)
+                return log_error_errno(r, "Failed to wait for daemon to reply: %m");
+
         return 0;
 }
index b4ea5a3..9cf88b2 100644 (file)
@@ -101,11 +101,15 @@ int settle_main(int argc, char *argv[], void *userdata) {
                 _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
 
                 if (udev_ctrl_new(&uctrl) >= 0) {
-                        r = udev_ctrl_send_ping(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout));
+                        r = udev_ctrl_send_ping(uctrl);
                         if (r < 0) {
                                 log_debug_errno(r, "Failed to connect to udev daemon: %m");
                                 return 0;
                         }
+
+                        r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout));
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to wait for daemon to reply: %m");
                 }
         }
 
index 14c70e4..63ceaaf 100644 (file)
@@ -312,9 +312,13 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to initialize udev control: %m");
 
-                r = udev_ctrl_send_ping(uctrl, ping_timeout_usec);
+                r = udev_ctrl_send_ping(uctrl);
                 if (r < 0)
                         return log_error_errno(r, "Failed to connect to udev daemon: %m");
+
+                r = udev_ctrl_wait(uctrl, ping_timeout_usec);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to wait for daemon to reply: %m");
         }
 
         for (; optind < argc; optind++) {