From: Bartosz Golaszewski Date: Sat, 14 Apr 2018 20:20:53 +0000 (+0200) Subject: core: events: correctly handle POLLNVAL in gpiod_line_event_wait() X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=d3ea4f41484693320153bae226c27c507afef00c;p=qemu-gpiodev%2Flibgpiod.git core: events: correctly handle POLLNVAL in gpiod_line_event_wait() 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 --- diff --git a/src/lib/core.c b/src/lib/core.c index 8b89517..ed80273 100644 --- a/src/lib/core.c +++ b/src/lib/core.c @@ -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; } } diff --git a/tests/tests-event.c b/tests/tests-event.c index 9dbaa65..f91d80f 100644 --- a/tests/tests-event.c +++ b/tests/tests-event.c @@ -9,6 +9,7 @@ #include "gpiod-test.h" +#include #include 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 });