) or (
f.getQualifiedName() = "strerror" and
message = "Call to strerror() is not thread-safe. Use strerror_r() or printf()'s %m format string instead."
+ ) or (
+ f.getQualifiedName() = "accept" and
+ message = "Call to accept() is not O_CLOEXEC-safe. Use accept4() instead."
)
}
_cleanup_close_ int fd_accepted = -1;
fd_accepted = accept4(fd, NULL, NULL, 0);
- if (fd_accepted < 0)
+ if (fd_accepted < 0) {
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
+ return 0;
+
return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
+ }
- getsockname_pretty(fd_accepted, &local);
- getpeername_pretty(fd_accepted, true, &peer);
+ (void) getsockname_pretty(fd_accepted, &local);
+ (void) getpeername_pretty(fd_accepted, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
return fork_and_exec_process(name, argv, envp, fd_accepted);
*
* Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the
* icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
-#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(abs(r), \
- ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \
- ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, \
- ENONET, ESHUTDOWN)
+static inline bool ERRNO_IS_DISCONNECT(int r) {
+ return IN_SET(abs(r),
+ ECONNABORTED,
+ ECONNREFUSED,
+ ECONNRESET,
+ EHOSTDOWN,
+ EHOSTUNREACH,
+ ENETDOWN,
+ ENETRESET,
+ ENETUNREACH,
+ ENONET,
+ ENOPROTOOPT,
+ ENOTCONN,
+ EPIPE,
+ EPROTO,
+ ESHUTDOWN);
+}
+
+/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
+ * the accept(2) man page. */
+static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
+ return ERRNO_IS_DISCONNECT(r) ||
+ IN_SET(abs(r),
+ EAGAIN,
+ EINTR,
+ EOPNOTSUPP);
+}
/* Resource exhaustion, could be our fault or general system trouble */
-#define ERRNO_IS_RESOURCE(r) \
- IN_SET(abs(r), ENOMEM, EMFILE, ENFILE)
+static inline bool ERRNO_IS_RESOURCE(int r) {
+ return IN_SET(abs(r),
+ EMFILE,
+ ENFILE,
+ ENOMEM);
+}
#include <unistd.h>
#include "alloc-util.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
continue;
return -errno;
-
- } else if (r == 0)
+ }
+ if (r == 0)
return 0;
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
- if (errno == EINTR)
- continue;
-
if (errno == EAGAIN)
return 0;
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
+ continue;
+
return -errno;
}
- close(cfd);
+ safe_close(cfd);
}
}
nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (nfd < 0) {
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
+ return 0;
+
log_warning_errno(errno, "Failed to accept private connection, ignoring: %m");
return 0;
}
assert(s);
assert(fd >= 0);
- for (;;) {
- cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK);
- if (cfd < 0) {
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- break;
- }
+ cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (cfd < 0)
+ /* Convert transient network errors into clean and well-defined EAGAIN */
+ return ERRNO_IS_ACCEPT_AGAIN(errno) ? -EAGAIN : -errno;
return cfd;
}
pair[0] = safe_close(pair[0]);
cfd = socket_accept_do(s, fd);
+ if (cfd == -EAGAIN) /* spurious accept() */
+ _exit(EXIT_SUCCESS);
if (cfd < 0) {
log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m");
_exit(EXIT_FAILURE);
return r;
}
+ /* If we received no fd, we got EIO here. If this happens with a process exit code of EXIT_SUCCESS
+ * this is a spurious accept(), let's convert that back to EAGAIN here. */
+ if (cfd == -EIO)
+ return -EAGAIN;
if (cfd < 0)
return log_unit_error_errno(UNIT(s), cfd, "Failed to receive connection socket: %m");
shortcut:
cfd = socket_accept_do(s, fd);
+ if (cfd == -EAGAIN) /* spurious accept(), skip it silently */
+ return -EAGAIN;
if (cfd < 0)
return log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m");
log_unit_debug(UNIT(p->socket), "Incoming traffic");
if (revents != EPOLLIN) {
-
if (revents & EPOLLHUP)
log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.");
else
socket_address_can_accept(&p->address)) {
cfd = socket_accept_in_cgroup(p->socket, p, fd);
+ if (cfd == -EAGAIN) /* Spurious accept() */
+ return 0;
if (cfd < 0)
goto fail;
#include "alloc-util.h"
#include "def.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "journal-file.h"
return journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global);
}
-static int accept_connection(const char* type, int fd,
- SocketAddress *addr, char **hostname) {
- int fd2, r;
+static int accept_connection(
+ const char* type,
+ int fd,
+ SocketAddress *addr,
+ char **hostname) {
+
+ _cleanup_close_ int fd2 = -1;
+ int r;
log_debug("Accepting new %s connection on fd:%d", type, fd);
fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
- if (fd2 < 0)
+ if (fd2 < 0) {
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
+ return -EAGAIN;
+
return log_error_errno(errno, "accept() on fd:%d failed: %m", fd);
+ }
switch(socket_address_family(addr)) {
case AF_INET:
char *b;
r = socket_address_print(addr, &a);
- if (r < 0) {
- log_error_errno(r, "socket_address_print(): %m");
- close(fd2);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "socket_address_print(): %m");
r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
- if (r < 0) {
- log_error_errno(r, "Resolving hostname failed: %m");
- close(fd2);
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "Resolving hostname failed: %m");
log_debug("Accepted %s %s connection from %s",
type,
a);
*hostname = b;
+ return TAKE_FD(fd2);
+ }
- return fd2;
- };
default:
- log_error("Rejected %s connection with unsupported family %d",
- type, socket_address_family(addr));
- close(fd2);
-
- return -EINVAL;
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Rejected %s connection with unsupported family %d",
+ type, socket_address_family(addr));
}
}
-static int dispatch_raw_connection_event(sd_event_source *event,
- int fd,
- uint32_t revents,
- void *userdata) {
+static int dispatch_raw_connection_event(
+ sd_event_source *event,
+ int fd,
+ uint32_t revents,
+ void *userdata) {
+
RemoteServer *s = userdata;
int fd2;
SocketAddress addr = {
char *hostname = NULL;
fd2 = accept_connection("raw", fd, &addr, &hostname);
+ if (fd2 == -EAGAIN)
+ return 0;
if (fd2 < 0)
return fd2;
#include "alloc-util.h"
#include "dirent-util.h"
#include "env-file.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (fd < 0) {
- if (errno == EAGAIN)
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
return log_error_errno(errno, "Failed to accept stdout connection: %m");
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "errno-util.h"
#include "fd-util.h"
#include "missing_network.h"
#include "resolved-dns-stub.h"
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
return -errno;
#include <netinet/in.h>
#include <resolv.h>
+#include "errno-util.h"
#include "fd-util.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
- if (IN_SET(errno, EAGAIN, EINTR))
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
return -errno;
nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (nfd < 0) {
- if (errno != -EAGAIN)
+ if (!ERRNO_IS_ACCEPT_AGAIN(errno))
log_warning_errno(errno, "Failed to accept() socket: %m");
} else {
- getpeername_pretty(nfd, true, &peer);
+ (void) getpeername_pretty(nfd, true, &peer);
log_debug("New connection from %s", strna(peer));
r = add_connection_socket(context, nfd);
#include "sd-event.h"
#include "alloc-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "io-util.h"
sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
if (sock < 0) {
- if (errno != EINTR)
- log_error_errno(errno, "Failed to accept ctrl connection: %m");
- return 0;
+ if (ERRNO_IS_ACCEPT_AGAIN(errno))
+ return 0;
+
+ return log_error_errno(errno, "Failed to accept ctrl connection: %m");
}
/* check peer credential of connection */