socket.c revision 271b032a053f9d4a1be271bb052276ae27fe36c6
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync This file is part of systemd.
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync Copyright 2010 Lennart Poettering
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync systemd is free software; you can redistribute it and/or modify it
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync under the terms of the GNU General Public License as published by
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync the Free Software Foundation; either version 2 of the License, or
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync (at your option) any later version.
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync systemd is distributed in the hope that it will be useful, but
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync WITHOUT ANY WARRANTY; without even the implied warranty of
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync General Public License for more details.
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync You should have received a copy of the GNU General Public License
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync along with systemd; If not, see <http://www.gnu.org/licenses/>.
8810985701f1c6beb8f74c235c216e18b67192b0vboxsyncstatic const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync s->exec_context.std_output = u->meta.manager->default_std_output;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync s->exec_context.std_error = u->meta.manager->default_std_error;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync while ((p = s->ports)) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if (p->fd >= 0) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync /* Make sure no service instance refers to us anymore. */
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync LIST_FOREACH(units_by_type, i, u->meta.manager->units_by_type[UNIT_SERVICE]) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync /* This fills in s->service if it isn't filled in yet. For
7f21d92108ecca490de4b14e2a0a747b2a966beavboxsync * Accept=yes sockets we create the next connection service
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync * here. For Accept=no this is mostly a NOP since the service
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync * is figured out at load time anyway. */
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync r = asprintf(&name, "%s@%u.service", prefix, s->n_accepted);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync r = manager_load_unit(s->meta.manager, name, NULL, NULL, &u);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("Using SysV services for socket activation is not supported. Refusing.");
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return true;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return true;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return true;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return false;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("%s lacks Listen setting. Refusing.", s->meta.id);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", s->meta.id);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("%s's MaxConnection setting too small. Refusing.", s->meta.id);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", s->meta.id);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", s->meta.id);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsyncstatic bool socket_needs_mount(Socket *s, const char *prefix) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if (socket_address_needs_mount(&p->address, prefix))
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return true;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return true;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return false;
8810985701f1c6beb8f74c235c216e18b67192b0vboxsyncint socket_add_one_mount_link(Socket *s, Mount *m) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_MOUNT])
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = socket_add_one_mount_link(s, (Mount*) other)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsyncstatic int socket_add_default_dependencies(Socket *s) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if (s->meta.manager->running_as == MANAGER_SYSTEM) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync /* This is a new unit? Then let's add in some extras */
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_load_related_unit(u, ".service", (Unit**) &s->service)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service), true)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = socket_add_mount_links(s)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = socket_add_device_link(s)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync if ((r = unit_add_default_cgroups(u)) < 0)
8810985701f1c6beb8f74c235c216e18b67192b0vboxsyncstatic const char* listen_lookup(int family, int type) {
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return "ListenNetlink";
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return "ListenStream";
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return "ListenDatagram";
8810985701f1c6beb8f74c235c216e18b67192b0vboxsync return "ListenSequentialPacket";
return NULL;
SocketPort *p;
const char *prefix2;
char *p2;
assert(s);
assert(f);
fprintf(f,
if (s->control_pid > 0)
fprintf(f,
if (s->bind_to_device)
fprintf(f,
if (s->accept)
fprintf(f,
if (s->priority >= 0)
fprintf(f,
if (s->receive_buffer > 0)
fprintf(f,
if (s->send_buffer > 0)
fprintf(f,
if (s->ip_tos >= 0)
fprintf(f,
if (s->ip_ttl >= 0)
fprintf(f,
if (s->pipe_size > 0)
fprintf(f,
if (s->mark >= 0)
fprintf(f,
if (s->mq_maxmsg > 0)
fprintf(f,
if (s->mq_msgsize > 0)
fprintf(f,
char *k = NULL;
t = strerror(-r);
fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t);
free(k);
for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
socklen_t l;
l = sizeof(local);
return -errno;
l = sizeof(remote);
return -errno;
case AF_INET: {
if (asprintf(&r,
return -ENOMEM;
case AF_INET6: {
static const char ipv4_prefix[] = {
const uint8_t
if (asprintf(&r,
return -ENOMEM;
if (asprintf(&r,
return -ENOMEM;
case AF_UNIX: {
l = sizeof(ucred);
return -errno;
if (asprintf(&r,
nr,
return -ENOMEM;
*instance = r;
SocketPort *p;
assert(s);
if (p->fd < 0)
assert(s);
if (s->keep_alive) {
int b = s->keep_alive;
if (s->broadcast) {
if (s->pass_cred) {
if (s->priority >= 0)
if (s->receive_buffer > 0) {
if (s->send_buffer > 0) {
if (s->mark >= 0)
if (s->ip_tos >= 0)
if (s->ip_ttl >= 0) {
if (socket_ipv6_is_supported())
if (s->tcp_congestion)
assert(s);
if (s->pipe_size > 0)
static int fifo_address_create(
const char *path,
int *_fd) {
goto fail;
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -EEXIST;
goto fail;
fail:
if (fd >= 0)
static int special_address_create(
const char *path,
int *_fd) {
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -EEXIST;
goto fail;
fail:
if (fd >= 0)
static int mq_address_create(
const char *path,
long maxmsg,
long msgsize,
int *_fd) {
if (fd < 0) {
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -EEXIST;
goto fail;
fail:
if (fd >= 0)
SocketPort *p;
bool know_label = false;
assert(s);
if (p->fd >= 0)
if (!know_label) {
if ((r = socket_instantiate_service(s)) < 0)
if (r != -EPERM)
know_label = true;
if ((r = socket_address_listen(
&p->address,
s->backlog,
s->bind_ipv6_only,
s->bind_to_device,
s->free_bind,
s->transparent,
s->directory_mode,
s->socket_mode,
&p->fd)) < 0)
goto rollback;
if ((r = special_address_create(
p->path,
&p->fd)) < 0)
goto rollback;
if ((r = fifo_address_create(
p->path,
s->directory_mode,
s->socket_mode,
&p->fd)) < 0)
goto rollback;
if ((r = mq_address_create(
p->path,
s->socket_mode,
s->mq_maxmsg,
s->mq_msgsize,
&p->fd)) < 0)
goto rollback;
socket_close_fds(s);
SocketPort *p;
assert(s);
if (p->fd < 0)
SocketPort *p;
assert(s);
if (p->fd < 0)
s->accept &&
goto fail;
fail:
assert(s);
socket_close_fds(s);
assert(s);
if (s->control_pid <= 0)
return -EBADMSG;
if ((r = socket_open_fds(s)) < 0)
if ((r = socket_watch_fds(s)) < 0)
char **argv;
assert(s);
assert(c);
goto fail;
r = -ENOMEM;
goto fail;
r = exec_spawn(c,
argv,
&s->exec_context,
NULL, 0,
&pid);
goto fail;
goto fail;
fail:
assert(s);
if (!success)
s->failure = true;
assert(s);
if (!success)
s->failure = true;
goto fail;
fail:
bool wait_for_exit = false;
assert(s);
if (!success)
s->failure = true;
int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
if (s->control_pid > 0) {
wait_for_exit = true;
r = -ENOMEM;
goto fail;
if (s->control_pid > 0)
goto fail;
wait_for_exit = true;
if (wait_for_exit) {
goto fail;
socket_enter_stop_post(s, true);
socket_enter_dead(s, true);
fail:
socket_enter_stop_post(s, false);
socket_enter_dead(s, false);
if (pid_set)
assert(s);
if (!success)
s->failure = true;
goto fail;
socket_enter_stop_post(s, true);
fail:
socket_enter_stop_post(s, false);
assert(s);
if ((r = socket_watch_fds(s)) < 0) {
goto fail;
fail:
socket_enter_stop_pre(s, false);
assert(s);
if ((r = socket_open_fds(s)) < 0) {
goto fail;
goto fail;
fail:
socket_enter_stop_pre(s, false);
assert(s);
goto fail;
fail:
socket_enter_dead(s, false);
assert(s);
if (cfd >= 0)
socket_close_fds(s);
if ((r = socket_watch_fds(s)) < 0) {
socket_enter_stop_pre(s, false);
if (cfd < 0) {
bool pending = false;
Meta *i;
pending = true;
if (!pending)
if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s->service), JOB_REPLACE, true, &error, NULL)) < 0)
goto fail;
if ((r = socket_instantiate_service(s)) < 0)
goto fail;
goto fail;
r = -ENOMEM;
goto fail;
if (!name) {
r = -ENOMEM;
goto fail;
goto fail;
s->n_accepted ++;
goto fail;
s->n_connections ++;
if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL)) < 0)
goto fail;
fail:
socket_enter_stop_pre(s, false);
if (cfd >= 0)
assert(s);
if (!success)
s->failure = true;
goto fail;
fail:
socket_enter_stop_pre(s, false);
socket_enter_dead(s, false);
assert(s);
return -EAGAIN;
if (s->service) {
return -ENOENT;
return -EBUSY;
#ifdef HAVE_SYSV_COMPAT
return -ENOENT;
s->failure = false;
assert(s);
return -EAGAIN;
socket_enter_stop_pre(s, true);
SocketPort *p;
assert(u);
assert(f);
if (s->control_pid > 0)
if (s->control_command_id >= 0)
int copy;
if (p->fd < 0)
return copy;
free(t);
assert(u);
s->n_accepted += k;
SocketPort *p;
if (p->fd >= 0)
SocketPort *p;
if (p->fd >= 0)
SocketPort *p;
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
if (p->fd >= 0)
SocketPort *p;
if (p->fd >= 0)
assert(u);
assert(u);
assert(u);
return s->n_connections > 0;
assert(s);
log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->meta.id);
goto fail;
if (w->socket_accept) {
goto fail;
fail:
socket_enter_stop_pre(s, false);
bool success;
assert(s);
s->control_pid = 0;
if (s->control_command) {
success = true;
switch (s->state) {
case SOCKET_START_PRE:
if (success)
case SOCKET_START_POST:
if (success)
socket_enter_stop_pre(s, false);
case SOCKET_STOP_PRE:
case SOCKET_STOP_PRE_SIGTERM:
case SOCKET_STOP_PRE_SIGKILL:
case SOCKET_STOP_POST:
case SOCKET_FINAL_SIGTERM:
case SOCKET_FINAL_SIGKILL:
assert(s);
switch (s->state) {
case SOCKET_START_PRE:
case SOCKET_START_POST:
socket_enter_stop_pre(s, false);
case SOCKET_STOP_PRE:
case SOCKET_STOP_PRE_SIGTERM:
socket_enter_stop_post(s, false);
case SOCKET_STOP_PRE_SIGKILL:
socket_enter_stop_post(s, false);
case SOCKET_STOP_POST:
case SOCKET_FINAL_SIGTERM:
socket_enter_dead(s, false);
case SOCKET_FINAL_SIGKILL:
socket_enter_dead(s, false);
int *rfds;
unsigned rn_fds, k;
SocketPort *p;
assert(s);
rn_fds = 0;
if (p->fd >= 0)
rn_fds++;
if (rn_fds <= 0) {
*n_fds = 0;
return -ENOMEM;
if (p->fd >= 0)
assert(s);
assert(s);
s->n_connections--;
assert(s);
s->failure = false;
assert(s);
return -ESRCH;
return -ESRCH;
if (s->control_pid > 0)
r = -errno;
return -ENOMEM;
if (s->control_pid > 0)
goto finish;
if (pid_set)
.sections =