core: events: correctly handle POLLNVAL in gpiod_line_event_wait()
authorBartosz Golaszewski <bartekgola@gmail.com>
Sat, 14 Apr 2018 20:20:53 +0000 (22:20 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Sun, 15 Apr 2018 17:15:28 +0000 (19:15 +0200)
The only error that can be indicated by ppoll() in the revents field
of struct pollfd when polling a line events file descriptor is
POLLNVAL. It can happen if the user calls close() on said descriptor
after retrieving it with gpiod_line_event_get_fd().

Currently we would act as if there's a line event to read. Make
gpiod_line_event_wait() and its bulk variant return -1 and set errno
to EINVAL in this case.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
src/lib/core.c
tests/tests-event.c

index 8b89517f166d277ad374053d248d0510f9535ef5..ed8027375a9cd14972470a93901cdbf4bb6ed886 100644 (file)
@@ -698,16 +698,23 @@ int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk,
        else if (rv == 0)
                return 0;
 
-       if (event_bulk) {
+       if (event_bulk)
                gpiod_line_bulk_init(event_bulk);
 
-               for (off = 0; off < num_lines; off++) {
-                       if (fds[off].revents) {
+       for (off = 0; off < num_lines; off++) {
+               if (fds[off].revents) {
+                       if (fds[off].revents & POLLNVAL) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+
+                       if (event_bulk) {
                                line = gpiod_line_bulk_get_line(bulk, off);
                                gpiod_line_bulk_add(event_bulk, line);
-                               if (!--rv)
-                                       break;
                        }
+
+                       if (!--rv)
+                               break;
                }
        }
 
index 9dbaa658ea16075150385d38ac9a53d6862e5e19..f91d80fe6c731c11e71ac50d6470f97fc93cd009 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "gpiod-test.h"
 
+#include <unistd.h>
 #include <errno.h>
 
 static void event_rising_edge_good(void)
@@ -293,3 +294,42 @@ static void event_request_bulk_fail(void)
 TEST_DEFINE(event_request_bulk_fail,
            "events - failed bulk request (test reversed release)",
            0, { 8 });
+
+static void event_invalid_fd(void)
+{
+       TEST_CLEANUP_CHIP struct gpiod_chip *chip = NULL;
+       struct gpiod_line_bulk bulk = GPIOD_LINE_BULK_INITIALIZER;
+       struct gpiod_line_bulk ev_bulk;
+       struct timespec ts = { 1, 0 };
+       struct gpiod_line *line;
+       int rv, fd;
+
+       chip = gpiod_chip_open(test_chip_path(0));
+       TEST_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 5);
+       TEST_ASSERT_NOT_NULL(line);
+
+       rv = gpiod_line_request_both_edges_events(line, TEST_CONSUMER);
+       TEST_ASSERT_RET_OK(rv);
+
+       fd = gpiod_line_event_get_fd(line);
+       close(fd);
+
+       rv = gpiod_line_event_wait(line, &ts);
+       TEST_ASSERT_EQ(rv, -1);
+       TEST_ASSERT_ERRNO_IS(EINVAL);
+
+       /*
+        * The single line variant calls gpiod_line_event_wait_bulk() with the
+        * event_bulk argument set to NULL, so test this use case explicitly
+        * as well.
+        */
+       gpiod_line_bulk_add(&bulk, line);
+       rv = gpiod_line_event_wait_bulk(&bulk, &ts, &ev_bulk);
+       TEST_ASSERT_EQ(rv, -1);
+       TEST_ASSERT_ERRNO_IS(EINVAL);
+}
+TEST_DEFINE(event_invalid_fd,
+           "events - gpiod_line_event_wait() error on closed fd",
+           0, { 8 });