diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml
index 3935141fc0..09a5b11dff 100644
--- a/man/sd_event_add_io.xml
+++ b/man/sd_event_add_io.xml
@@ -216,16 +216,20 @@
source object and returns the non-negative file descriptor
or a negative error number on error (see below).
- sd_event_source_set_io_fd()
- changes the UNIX file descriptor of an I/O event source created
- previously with sd_event_add_io(). It takes
- the event source object and the new file descriptor.
+ sd_event_source_set_io_fd() changes the UNIX file descriptor of an I/O event
+ source created previously with sd_event_add_io(). It takes the event source object
+ and the new file descriptor. If the event source takes the ownership of the previous file descriptor,
+ that is, sd_event_source_set_io_fd_own() was called for the event source with a
+ non-zero value, then the previous file descriptor will be closed and the event source will also take the
+ ownership of the new file descriptor on success.
- sd_event_source_set_io_fd_own() controls whether the file descriptor of the event source
- shall be closed automatically when the event source is freed, i.e. whether it shall be considered 'owned' by the
- event source object. By default it is not closed automatically, and the application has to do this on its own. The
- b parameter is a boolean parameter: if zero, the file descriptor is not closed automatically
- when the event source is freed, otherwise it is closed.
+ sd_event_source_set_io_fd_own() controls whether the file descriptor of the
+ event source shall be closed automatically when the event source is freed (or when the file descriptor
+ assigned to the event source is replaced by sd_event_source_set_io_fd()), i.e.
+ whether it shall be considered 'owned' by the event source object. By default it is not closed
+ automatically, and the application has to do this on its own. The b parameter is a
+ boolean parameter: if zero, the file descriptor is not closed automatically when the event source is
+ freed, otherwise it is closed.
sd_event_source_get_io_fd_own() may be used to query the current setting of the file
descriptor ownership boolean flag as set with sd_event_source_set_io_fd_own(). It returns
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 39c60297f0..bd5bd81ac4 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -2647,7 +2647,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
}
_public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
- int r;
+ int saved_fd, r;
assert_return(s, -EINVAL);
assert_return(fd >= 0, -EBADF);
@@ -2657,16 +2657,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
if (s->io.fd == fd)
return 0;
- if (event_source_is_offline(s)) {
- s->io.fd = fd;
- s->io.registered = false;
- } else {
- int saved_fd;
+ saved_fd = s->io.fd;
+ s->io.fd = fd;
- saved_fd = s->io.fd;
- assert(s->io.registered);
+ assert(event_source_is_offline(s) == !s->io.registered);
- s->io.fd = fd;
+ if (s->io.registered) {
s->io.registered = false;
r = source_io_register(s, s->enabled, s->io.events);
@@ -2679,6 +2675,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
(void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
}
+ if (s->io.owned)
+ safe_close(saved_fd);
+
return 0;
}
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 991aa925a4..c617935992 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -843,6 +843,24 @@ TEST(fork) {
assert_se(r >= 0);
}
+TEST(sd_event_source_set_io_fd) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_close_pair_ int pfd_a[2] = EBADF_PAIR, pfd_b[2] = EBADF_PAIR;
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ assert_se(pipe2(pfd_a, O_CLOEXEC) >= 0);
+ assert_se(pipe2(pfd_b, O_CLOEXEC) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, pfd_a[0], EPOLLIN, NULL, INT_TO_PTR(-ENOANO)) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+ TAKE_FD(pfd_a[0]);
+
+ assert_se(sd_event_source_set_io_fd(s, pfd_b[0]) >= 0);
+ TAKE_FD(pfd_b[0]);
+}
+
static int hup_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
unsigned *c = userdata;