Merge pull request #21508 from poettering/conn-count-fix
pid1: fix connection counting
This commit is contained in:
+22
-26
@@ -392,36 +392,32 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Accept=</varname></term>
|
||||
<listitem><para>Takes a boolean argument. If yes, a service
|
||||
instance is spawned for each incoming connection and only the
|
||||
connection socket is passed to it. If no, all listening
|
||||
sockets themselves are passed to the started service unit, and
|
||||
only one service unit is spawned for all connections (also see
|
||||
above). This value is ignored for datagram sockets and FIFOs
|
||||
where a single service unit unconditionally handles all
|
||||
incoming traffic. Defaults to <option>no</option>. For
|
||||
performance reasons, it is recommended to write new daemons
|
||||
only in a way that is suitable for
|
||||
<option>Accept=no</option>. A daemon listening on an
|
||||
<constant>AF_UNIX</constant> socket may, but does not need to,
|
||||
call
|
||||
<citerefentry><refentrytitle>close</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
on the received socket before exiting. However, it must not
|
||||
unlink the socket from a file system. It should not invoke
|
||||
<citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
on sockets it got with <varname>Accept=no</varname>, but it
|
||||
may do so for sockets it got with
|
||||
<varname>Accept=yes</varname> set. Setting
|
||||
<varname>Accept=yes</varname> is mostly useful to allow
|
||||
daemons designed for usage with
|
||||
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
to work unmodified with systemd socket
|
||||
activation.</para>
|
||||
<listitem><para>Takes a boolean argument. If yes, a service instance is spawned for each incoming
|
||||
connection and only the connection socket is passed to it. If no, all listening sockets themselves
|
||||
are passed to the started service unit, and only one service unit is spawned for all connections
|
||||
(also see above). This value is ignored for datagram sockets and FIFOs where a single service unit
|
||||
unconditionally handles all incoming traffic. Defaults to <option>no</option>. For performance
|
||||
reasons, it is recommended to write new daemons only in a way that is suitable for
|
||||
<option>Accept=no</option>. A daemon listening on an <constant>AF_UNIX</constant> socket may, but
|
||||
does not need to, call
|
||||
<citerefentry><refentrytitle>close</refentrytitle><manvolnum>2</manvolnum></citerefentry> on the
|
||||
received socket before exiting. However, it must not unlink the socket from a file system. It should
|
||||
not invoke
|
||||
<citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>2</manvolnum></citerefentry> on
|
||||
sockets it got with <varname>Accept=no</varname>, but it may do so for sockets it got with
|
||||
<varname>Accept=yes</varname> set. Setting <varname>Accept=yes</varname> is mostly useful to allow
|
||||
daemons designed for usage with <citerefentry
|
||||
project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry> to work
|
||||
unmodified with systemd socket activation.</para>
|
||||
|
||||
<para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname> environment variable will
|
||||
contain the remote IP address, and <varname>REMOTE_PORT</varname> will contain the remote port. This
|
||||
is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
|
||||
protocol.</para></listitem>
|
||||
protocol.</para>
|
||||
|
||||
<para>It is recommended to set <varname>CollectMode=inactive-or-failed</varname> for service
|
||||
instances activated via <varname>Accept=yes</varname>, to ensure that failed connection services are
|
||||
cleaned up and released from memory, and do not accumulate.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
+18
-9
@@ -190,6 +190,8 @@ void service_close_socket_fd(Service *s) {
|
||||
socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
|
||||
unit_ref_unset(&s->accept_socket);
|
||||
}
|
||||
|
||||
s->socket_peer = socket_peer_unref(s->socket_peer);
|
||||
}
|
||||
|
||||
static void service_stop_watchdog(Service *s) {
|
||||
@@ -388,7 +390,6 @@ static void service_done(Unit *u) {
|
||||
s->usb_function_strings = mfree(s->usb_function_strings);
|
||||
|
||||
service_close_socket_fd(s);
|
||||
s->peer = socket_peer_unref(s->peer);
|
||||
|
||||
unit_ref_unset(&s->accept_socket);
|
||||
|
||||
@@ -1237,8 +1238,8 @@ static int service_coldplug(Unit *u) {
|
||||
|
||||
/* Make a best-effort attempt at bumping the connection count */
|
||||
if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) {
|
||||
socket_peer_unref(s->peer);
|
||||
s->peer = peer;
|
||||
socket_peer_unref(s->socket_peer);
|
||||
s->socket_peer = peer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4285,8 +4286,14 @@ static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
|
||||
}
|
||||
}
|
||||
|
||||
int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context_net) {
|
||||
_cleanup_free_ char *peer = NULL;
|
||||
int service_set_socket_fd(
|
||||
Service *s,
|
||||
int fd,
|
||||
Socket *sock,
|
||||
SocketPeer *peer,
|
||||
bool selinux_context_net) {
|
||||
|
||||
_cleanup_free_ char *peer_text = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@@ -4301,22 +4308,23 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
|
||||
if (s->socket_fd >= 0)
|
||||
return -EBUSY;
|
||||
|
||||
assert(!s->socket_peer);
|
||||
|
||||
if (s->state != SERVICE_DEAD)
|
||||
return -EAGAIN;
|
||||
|
||||
if (getpeername_pretty(fd, true, &peer) >= 0) {
|
||||
if (getpeername_pretty(fd, true, &peer_text) >= 0) {
|
||||
|
||||
if (UNIT(s)->description) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
|
||||
a = strjoin(UNIT(s)->description, " (", peer, ")");
|
||||
a = strjoin(UNIT(s)->description, " (", peer_text, ")");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
r = unit_set_description(UNIT(s), a);
|
||||
} else
|
||||
r = unit_set_description(UNIT(s), peer);
|
||||
|
||||
r = unit_set_description(UNIT(s), peer_text);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -4326,6 +4334,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
|
||||
return r;
|
||||
|
||||
s->socket_fd = fd;
|
||||
s->socket_peer = socket_peer_ref(peer);
|
||||
s->socket_fd_selinux_context_net = selinux_context_net;
|
||||
|
||||
unit_ref_set(&s->accept_socket, UNIT(s), UNIT(sock));
|
||||
|
||||
+5
-4
@@ -157,8 +157,11 @@ struct Service {
|
||||
DynamicCreds dynamic_creds;
|
||||
|
||||
pid_t main_pid, control_pid;
|
||||
|
||||
/* if we are a socket activated service instance, store information of the connection/peer/socket */
|
||||
int socket_fd;
|
||||
SocketPeer *peer;
|
||||
SocketPeer *socket_peer;
|
||||
UnitRef accept_socket;
|
||||
bool socket_fd_selinux_context_net;
|
||||
|
||||
bool permissions_start_only;
|
||||
@@ -186,8 +189,6 @@ struct Service {
|
||||
char *status_text;
|
||||
int status_errno;
|
||||
|
||||
UnitRef accept_socket;
|
||||
|
||||
sd_event_source *timer_event_source;
|
||||
PathSpec *pid_file_pathspec;
|
||||
|
||||
@@ -226,7 +227,7 @@ static inline usec_t service_get_watchdog_usec(Service *s) {
|
||||
|
||||
extern const UnitVTable service_vtable;
|
||||
|
||||
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
|
||||
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, struct SocketPeer *peer, bool selinux_context_net);
|
||||
void service_close_socket_fd(Service *s);
|
||||
|
||||
const char* service_restart_to_string(ServiceRestart i) _const_;
|
||||
|
||||
+14
-15
@@ -463,10 +463,6 @@ static int socket_load(Unit *u) {
|
||||
assert(u);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_load_fragment_and_dropin(u, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -485,12 +481,13 @@ static int socket_load(Unit *u) {
|
||||
static SocketPeer *socket_peer_new(void) {
|
||||
SocketPeer *p;
|
||||
|
||||
p = new0(SocketPeer, 1);
|
||||
p = new(SocketPeer, 1);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
p->n_ref = 1;
|
||||
|
||||
*p = (SocketPeer) {
|
||||
.n_ref = 1,
|
||||
};
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -507,14 +504,15 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(SocketPeer, socket_peer, socket_peer_free);
|
||||
|
||||
int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
|
||||
_cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL;
|
||||
SocketPeer sa = {}, *i;
|
||||
socklen_t salen = sizeof(sa.peer);
|
||||
SocketPeer sa = {
|
||||
.peer_salen = sizeof(union sockaddr_union),
|
||||
}, *i;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(s);
|
||||
|
||||
if (getpeername(fd, &sa.peer.sa, &salen) < 0)
|
||||
if (getpeername(fd, &sa.peer.sa, &sa.peer_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)) {
|
||||
@@ -522,6 +520,10 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
i = set_get(s->peers_by_address, &sa);
|
||||
if (i) {
|
||||
*p = socket_peer_ref(i);
|
||||
@@ -533,7 +535,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
|
||||
return log_oom();
|
||||
|
||||
remote->peer = sa.peer;
|
||||
remote->peer_salen = salen;
|
||||
remote->peer_salen = sa.peer_salen;
|
||||
|
||||
r = set_put(s->peers_by_address, remote);
|
||||
if (r < 0)
|
||||
@@ -542,7 +544,6 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
|
||||
remote->socket = s;
|
||||
|
||||
*p = TAKE_PTR(remote);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2409,7 +2410,7 @@ static void socket_enter_running(Socket *s, int cfd_in) {
|
||||
|
||||
s->n_accepted++;
|
||||
|
||||
r = service_set_socket_fd(SERVICE(service), cfd, s, s->selinux_context_from_net);
|
||||
r = service_set_socket_fd(SERVICE(service), cfd, s, p, s->selinux_context_from_net);
|
||||
if (ERRNO_IS_DISCONNECT(r))
|
||||
return;
|
||||
if (r < 0)
|
||||
@@ -2418,8 +2419,6 @@ static void socket_enter_running(Socket *s, int cfd_in) {
|
||||
TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */
|
||||
s->n_connections++;
|
||||
|
||||
SERVICE(service)->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user