socket.c revision d38f6e34a618e2d100b06888e0810f776eb83510
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers This file is part of systemd.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers Copyright 2010 Lennart Poettering
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers systemd is free software; you can redistribute it and/or modify it
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers under the terms of the GNU Lesser General Public License as published by
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers the Free Software Foundation; either version 2.1 of the License, or
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers (at your option) any later version.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers systemd is distributed in the hope that it will be useful, but
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers WITHOUT ANY WARRANTY; without even the implied warranty of
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers Lesser General Public License for more details.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers You should have received a copy of the GNU Lesser General Public License
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers along with systemd; If not, see <http://www.gnu.org/licenses/>.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sieversstatic const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sieversstatic int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sieversstatic int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->timeout_usec = u->manager->default_timeout_start_usec;
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->exec_context.std_output = u->manager->default_std_output;
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->exec_context.std_error = u->manager->default_std_error;
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sieversstatic void socket_unwatch_control_pid(Socket *s) {
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers while ((p = s->ports)) {
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->exec_runtime = exec_runtime_unref(s->exec_runtime);
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
6c7980093c4e39d07bf06484f96f489e236c7c29Kay Sievers s->timer_event_source = sd_event_source_unref(s->timer_event_source);
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers if (s->timeout_usec <= 0) {
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers s->timer_event_source = sd_event_source_unref(s->timer_event_source);
if (s->timer_event_source) {
return sd_event_add_time(
&s->timer_event_source,
Unit *u;
assert(s);
if (!prefix)
return -ENOMEM;
return -ENOMEM;
#ifdef HAVE_SYSV_COMPAT
return -ENOENT;
u->no_gc = true;
SocketPort *p;
assert(s);
if (!s->accept)
SocketPort *p;
assert(s);
if (!path)
assert(s);
assert(s);
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
assert(s);
for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++)
if (s->exec_command[i])
assert(s);
if (have_non_accept_socket(s)) {
Unit *x;
r = socket_add_mount_links(s);
r = socket_add_device_link(s);
r = unit_patch_contexts(u);
if (socket_has_exec(s)) {
if (u->default_dependencies) {
r = socket_add_default_dependencies(s);
assert(s);
if (!s->ports) {
return -EINVAL;
log_error_unit(UNIT(s)->id, "%s configured for accepting sockets, but sockets are non-accepting. Refusing.",
return -EINVAL;
return -EINVAL;
log_error_unit(UNIT(s)->id, "Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id);
return -EINVAL;
log_error_unit(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
return -EINVAL;
assert(u);
r = unit_load_fragment_and_dropin(u);
r = socket_add_extras(s);
return socket_verify(s);
return NULL;
SocketPort *p;
const char *prefix2;
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,
if (s->reuse_port)
fprintf(f,
if (s->smack)
fprintf(f,
if (s->smack_ip_in)
fprintf(f,
if (s->smack_ip_out)
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,
nr,
return -ENOMEM;
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
const uint8_t
if (asprintf(&r,
nr,
return -ENOMEM;
if (asprintf(&r,
nr,
return -ENOMEM;
case AF_UNIX: {
if (asprintf(&r,
return -ENOMEM;
} else if (k == -ENODATA) {
if (asprintf(&r,
nr) < 0)
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->pass_sec) {
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())
"IP_TTL/IPV6_UNICAST_HOPS failed: %m");
if (s->tcp_congestion)
if (s->reuse_port) {
int b = s->reuse_port;
if (s->smack_ip_in)
if (s->smack_ip_out)
assert(s);
if (s->pipe_size > 0)
if (s->smack)
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:
static int special_address_create(
const char *path,
int *_fd) {
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -EEXIST;
goto fail;
fail:
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:
SocketPort *p;
bool know_label = false;
assert(s);
if (p->fd >= 0)
if (!know_label) {
if ((r = socket_instantiate_service(s)) < 0)
r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
if (r != -EPERM)
know_label = true;
&p->address,
s->backlog,
s->bind_ipv6_only,
s->bind_to_device,
s->free_bind,
s->transparent,
s->directory_mode,
s->socket_mode,
label);
goto rollback;
p->fd = r;
p->path,
&p->fd);
goto rollback;
r = fifo_address_create(
p->path,
s->directory_mode,
s->socket_mode,
&p->fd);
goto rollback;
r = mq_address_create(
p->path,
s->socket_mode,
s->mq_maxmsg,
s->mq_msgsize,
&p->fd);
goto rollback;
socket_close_fds(s);
SocketPort *p;
assert(s);
if (p->fd < 0)
if (p->event_source) {
SocketPort *p;
assert(s);
if (p->fd < 0)
if (p->event_source)
r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p);
goto fail;
fail:
assert(s);
socket_close_fds(s);
assert(s);
if (s->control_pid <= 0)
return -EBADMSG;
r = socket_arm_timer(s);
r = socket_open_fds(s);
r = socket_watch_fds(s);
char **argv;
assert(s);
assert(c);
goto fail;
r = socket_arm_timer(s);
goto fail;
goto fail;
r = exec_spawn(c,
argv,
&s->exec_context,
NULL, 0,
NULL,
s->exec_runtime,
&pid);
goto fail;
goto fail;
fail:
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
goto fail;
fail:
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
s->control_pid,
goto fail;
r = socket_arm_timer(s);
goto fail;
fail:
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
goto fail;
fail:
assert(s);
r = socket_watch_fds(s);
goto fail;
fail:
assert(s);
r = socket_open_fds(s);
goto fail;
goto fail;
fail:
assert(s);
goto fail;
fail:
assert(s);
log_debug_unit(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
if (cfd >= 0)
socket_close_fds(s);
r = socket_open_fds(s);
r = socket_watch_fds(s);
if (cfd < 0) {
Iterator i;
bool pending = false;
pending = true;
if (!pending) {
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
goto fail;
log_warning_unit(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections);
r = socket_instantiate_service(s);
goto fail;
if (r != -ENOTCONN)
goto fail;
if (!prefix) {
r = -ENOMEM;
goto fail;
if (!name) {
r = -ENOMEM;
goto fail;
goto fail;
s->n_accepted ++;
goto fail;
s->n_connections ++;
goto fail;
fail:
log_warning_unit(UNIT(s)->id, "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
assert(s);
goto fail;
fail:
assert(s);
return -EAGAIN;
return -ENOENT;
return -EBUSY;
#ifdef HAVE_SYSV_COMPAT
return -ENOENT;
assert(s);
return -EAGAIN;
SocketPort *p;
assert(u);
assert(f);
if (s->control_pid > 0)
if (s->control_command_id >= 0)
int copy;
if (p->fd < 0)
if (copy < 0)
return copy;
assert(u);
if (state < 0)
SocketResult f;
else if (f != SOCKET_SUCCESS)
s->result = f;
s->n_accepted += k;
if (id < 0)
SocketPort *p;
SocketPort *p;
SocketPort *p;
SocketPort *p;
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
SocketPort *p;
SocketPort *p;
assert(u);
Iterator i;
int fd;
if (p->fd >= 0)
assert(u);
assert(u);
assert(p);
switch (p->type) {
case SOCKET_SOCKET:
case SOCK_STREAM:
case SOCK_DGRAM:
case SOCK_SEQPACKET:
case SOCK_RAW:
return NULL;
case SOCKET_SPECIAL:
case SOCKET_MQUEUE:
case SOCKET_FIFO:
return NULL;
assert(u);
return s->n_connections > 0;
assert(p);
log_error_unit(UNIT(p->socket)->id, "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.",
goto fail;
if (cfd < 0) {
goto fail;
fail:
SocketResult f;
assert(s);
s->control_pid = 0;
f = SOCKET_SUCCESS;
if (s->control_command) {
f = SOCKET_SUCCESS;
u->id,
if (f != SOCKET_SUCCESS)
s->result = f;
if (s->control_command &&
f == SOCKET_SUCCESS) {
socket_run_next(s);
switch (s->state) {
case SOCKET_START_PRE:
if (f == SOCKET_SUCCESS)
case SOCKET_START_POST:
if (f == SOCKET_SUCCESS)
socket_enter_stop_pre(s, f);
case SOCKET_STOP_PRE:
case SOCKET_STOP_PRE_SIGTERM:
case SOCKET_STOP_PRE_SIGKILL:
socket_enter_stop_post(s, f);
case SOCKET_STOP_POST:
case SOCKET_FINAL_SIGTERM:
case SOCKET_FINAL_SIGKILL:
socket_enter_dead(s, f);
assert(s);
switch (s->state) {
case SOCKET_START_PRE:
case SOCKET_START_POST:
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:
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);
log_debug_unit(UNIT(s)->id, "%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent));
if (failed_permanent)
assert(s);
s->n_connections--;
assert(u);
s->accept)
socket_notify_service_dead(s, false);
if (!s->timer_event_source)
.sections =
.finished_start_job = {
.finished_stop_job = {