#include "dbus-socket.h"
#include "dbus-unit.h"
#include "def.h"
+#include "errno-list.h"
#include "exit-status.h"
#include "fd-util.h"
#include "format-util.h"
return 0;
}
-static int socket_instantiate_service(Socket *s, int cfd) {
- Unit *service;
- int r;
-
- assert(s);
- assert(cfd >= 0);
-
- /* This fills in s->service if it isn't filled in yet. For Accept=yes sockets we create the next
- * connection service here. For Accept=no this is mostly a NOP since the service is figured out at
- * load time anyway. */
-
- r = socket_load_service_unit(s, cfd, &service);
- if (r < 0)
- return r;
-
- unit_ref_set(&s->service, UNIT(s), service);
-
- return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service,
- false, UNIT_DEPENDENCY_IMPLICIT);
-}
-
static bool have_non_accept_socket(Socket *s) {
SocketPort *p;
assert(fd >= 0);
assert(s);
- r = getpeername(fd, &sa.peer.sa, &salen);
- if (r < 0)
+ if (getpeername(fd, &sa.peer.sa, &salen) < 0)
return log_unit_error_errno(UNIT(s), errno, "getpeername failed: %m");
if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
(void) unlink(*i);
}
-static void socket_apply_socket_options(Socket *s, int fd) {
+static void socket_apply_socket_options(Socket *s, SocketPort *p, int fd) {
int r;
assert(s);
+ assert(p);
assert(fd >= 0);
if (s->keep_alive) {
}
if (s->pass_pktinfo) {
- r = socket_pass_pktinfo(fd, true);
+ r = socket_set_recvpktinfo(fd, socket_address_family(&p->address), true);
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m");
}
}
if (s->receive_buffer > 0) {
- /* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
- if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, s->receive_buffer) < 0) {
- r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, s->receive_buffer);
- if (r < 0)
- log_unit_warning_errno(UNIT(s), r, "SO_RCVBUF failed: %m");
- }
+ r = fd_set_rcvbuf(fd, s->receive_buffer, false);
+ if (r < 0)
+ log_unit_full_errno(UNIT(s), ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
+ "SO_RCVBUF/SO_RCVBUFFORCE failed: %m");
}
if (s->send_buffer > 0) {
- if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, s->send_buffer) < 0) {
- r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, s->send_buffer);
- if (r < 0)
- log_unit_warning_errno(UNIT(s), r, "SO_SNDBUF failed: %m");
- }
+ r = fd_set_sndbuf(fd, s->send_buffer, false);
+ if (r < 0)
+ log_unit_full_errno(UNIT(s), ERRNO_IS_PRIVILEGE(r) ? LOG_DEBUG : LOG_WARNING, r,
+ "SO_SNDBUF/SO_SNDBUFFORCE failed: %m");
}
if (s->mark >= 0) {
}
if (s->ip_ttl >= 0) {
- int x;
-
- r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, s->ip_ttl);
-
- if (socket_ipv6_is_supported())
- x = setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, s->ip_ttl);
- else
- x = -EAFNOSUPPORT;
-
- if (r < 0 && x < 0)
+ r = socket_set_ttl(fd, socket_address_family(&p->address), s->ip_ttl);
+ if (r < 0)
log_unit_warning_errno(UNIT(s), r, "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
}
if (cfd >= 0) {
r = instance_from_socket(cfd, s->n_accepted, &instance);
- if (r == -ENOTCONN)
- /* ENOTCONN is legitimate if TCP RST was received.
- * This connection is over, but the socket unit lives on. */
+ if (ERRNO_IS_DISCONNECT(r))
+ /* ENOTCONN is legitimate if TCP RST was received. Other socket families might return
+ * different errors. This connection is over, but the socket unit lives on. */
return log_unit_debug_errno(UNIT(s), r,
- "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
+ "Got %s on incoming socket, assuming aborted connection attempt, ignoring.",
+ errno_to_name(r));
if (r < 0)
return r;
}
if (p->fd < 0)
return p->fd;
- socket_apply_socket_options(s, p->fd);
+ socket_apply_socket_options(s, p, p->fd);
socket_symlink(s);
break;
}
}
-static void socket_enter_running(Socket *s, int cfd) {
+static void socket_enter_running(Socket *s, int cfd_in) {
+ /* Note that this call takes possession of the connection fd passed. It either has to assign it
+ * somewhere or close it. */
+ _cleanup_close_ int cfd = cfd_in;
+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
- /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or
- * close it. */
-
assert(s);
/* We don't take connections anymore if we are supposed to shut down anyway */
if (cfd >= 0)
goto refuse;
- else
- flush_ports(s);
+ flush_ports(s);
return;
}
if (cfd < 0) {
bool pending = false;
Unit *other;
- Iterator i;
void *v;
/* If there's already a start pending don't bother to
* do anything */
- HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
+ HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS])
if (unit_active_or_pending(other)) {
pending = true;
break;
if (!pending) {
if (!UNIT_ISSET(s->service)) {
- log_unit_error(UNIT(s), "Service to activate vanished, refusing activation.");
- r = -ENOENT;
+ r = log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOENT),
+ "Service to activate vanished, refusing activation.");
goto fail;
}
socket_set_state(s, SOCKET_RUNNING);
} else {
_cleanup_(socket_peer_unrefp) SocketPeer *p = NULL;
- Service *service;
+ Unit *service;
if (s->n_connections >= s->max_connections) {
log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.",
if (s->max_connections_per_source > 0) {
r = socket_acquire_peer(s, cfd, &p);
- if (r < 0)
- goto refuse;
+ if (ERRNO_IS_DISCONNECT(r))
+ return;
+ if (r < 0) /* We didn't have enough resources to acquire peer information, let's fail. */
+ goto fail;
if (r > 0 && p->n_ref > s->max_connections_per_source) {
_cleanup_free_ char *t = NULL;
}
}
- r = socket_instantiate_service(s, cfd);
+ r = socket_load_service_unit(s, cfd, &service);
+ if (ERRNO_IS_DISCONNECT(r))
+ return;
if (r < 0)
goto fail;
- service = SERVICE(UNIT_DEREF(s->service));
- unit_ref_unset(&s->service);
+ r = unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service,
+ false, UNIT_DEPENDENCY_IMPLICIT);
+ if (r < 0)
+ goto fail;
s->n_accepted++;
- r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
+ r = service_set_socket_fd(SERVICE(service), cfd, s, s->selinux_context_from_net);
+ if (ERRNO_IS_DISCONNECT(r))
+ return;
if (r < 0)
goto fail;
TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */
s->n_connections++;
- service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */
+ SERVICE(service)->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_START, service, JOB_REPLACE, NULL, &error, NULL);
if (r < 0) {
/* We failed to activate the new service, but it still exists. Let's make sure the
* service closes and forgets the connection fd again, immediately. */
- service_close_socket_fd(service);
+ service_close_socket_fd(SERVICE(service));
goto fail;
}
unit_add_to_dbus_queue(UNIT(s));
}
+ TAKE_FD(cfd);
return;
refuse:
s->n_refused++;
- safe_close(cfd);
return;
fail:
- log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
- cfd >= 0 ? "template" : "non-template",
- bus_error_message(&error, r));
+ if (ERRNO_IS_RESOURCE(r))
+ log_unit_warning(UNIT(s), "Failed to queue service startup job: %s",
+ bus_error_message(&error, r));
+ else
+ log_unit_warning(UNIT(s), "Failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
+ cfd >= 0 ? "template" : "non-template",
+ bus_error_message(&error, r));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- safe_close(cfd);
}
static void socket_run_next(Socket *s) {
assert(u);
LIST_FOREACH(port, p, s->ports) {
- Iterator i;
int fd;
if (p->type != SOCKET_SOCKET)
if (p->fd >= 0)
continue;
- FDSET_FOREACH(fd, fds, i) {
+ FDSET_FOREACH(fd, fds) {
if (socket_address_matches_fd(&p->address, fd)) {
p->fd = fdset_remove(fds, fd);
s->deserialized_state = SOCKET_LISTENING;
if (cfd < 0)
goto fail;
- socket_apply_socket_options(p->socket, cfd);
+ socket_apply_socket_options(p->socket, p, cfd);
}
socket_enter_running(p->socket, cfd);
assert(other);
/* Filter out invocations with bogus state */
- if (!IN_SET(other->load_state,
- UNIT_LOADED,
- UNIT_NOT_FOUND,
- UNIT_BAD_SETTING,
- UNIT_ERROR,
- UNIT_MASKED) || other->type != UNIT_SERVICE)
- return;
+ assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
+ assert(other->type == UNIT_SERVICE);
/* Don't propagate state changes from the service if we are already down */
if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))