socket.c revision 21b735e798c580e7af8c33ace9f8565860b7f8df
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/***
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2010 Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering***/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <sys/stat.h>
4871690d9e32608bbd9b18505b5326c2079c9690Allin Cottrell#include <unistd.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <errno.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <fcntl.h>
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering#include <sys/epoll.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <signal.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <arpa/inet.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <netinet/tcp.h>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include <mqueue.h>
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "sd-event.h"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering#include "log.h"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering#include "strv.h"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering#include "mkdir.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "path-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "unit-name.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "unit-printf.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "missing.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "special.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "label.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "exit-status.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "def.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "smack-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "bus-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "bus-error.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "selinux-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "dbus-socket.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "unit.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "formats-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "signal-util.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#include "socket.h"
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_DEAD] = UNIT_INACTIVE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_START_PRE] = UNIT_ACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_START_CHOWN] = UNIT_ACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_START_POST] = UNIT_ACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_LISTENING] = UNIT_ACTIVE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_RUNNING] = UNIT_ACTIVE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_STOP_PRE] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_STOP_PRE_SIGTERM] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_STOP_PRE_SIGKILL] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_STOP_POST] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_FINAL_SIGTERM] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_FINAL_SIGKILL] = UNIT_DEACTIVATING,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering [SOCKET_FAILED] = UNIT_FAILED
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering};
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void socket_init(Unit *u) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Socket *s = SOCKET(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(u->load_state == UNIT_STUB);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->backlog = SOMAXCONN;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->timeout_usec = u->manager->default_timeout_start_usec;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering s->directory_mode = 0755;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering s->socket_mode = 0666;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering s->max_connections = 64;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->priority = -1;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->ip_tos = -1;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->ip_ttl = -1;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->mark = -1;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->exec_context.std_output = u->manager->default_std_output;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->exec_context.std_error = u->manager->default_std_error;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void socket_unwatch_control_pid(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering if (s->control_pid <= 0)
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering return;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering unit_unwatch_pid(UNIT(s), s->control_pid);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->control_pid = 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringvoid socket_free_ports(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering SocketPort *p;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering while ((p = s->ports)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LIST_REMOVE(port, s->ports, p);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering sd_event_source_unref(p->event_source);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering safe_close(p->fd);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(p->path);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(p);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void socket_done(Unit *u) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Socket *s = SOCKET(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering socket_free_ports(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->exec_runtime = exec_runtime_unref(s->exec_runtime);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->control_command = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering socket_unwatch_control_pid(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unit_ref_unset(&s->service);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->tcp_congestion);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->tcp_congestion = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->bind_to_device);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->bind_to_device = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->smack);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->smack_ip_in);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->smack_ip_out);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering strv_free(s->symlinks);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->user);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering free(s->group);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->timer_event_source = sd_event_source_unref(s->timer_event_source);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int socket_arm_timer(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering int r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->timeout_usec <= 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering s->timer_event_source = sd_event_source_unref(s->timer_event_source);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->timer_event_source) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = sd_event_add_time(
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering UNIT(s)->manager->event,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering &s->timer_event_source,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering CLOCK_MONOTONIC,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering socket_dispatch_timer, s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (void) sd_event_source_set_description(s->timer_event_source, "socket-timer");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmekint socket_instantiate_service(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering _cleanup_free_ char *prefix = NULL, *name = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering int r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Unit *u;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* This fills in s->service if it isn't filled in yet. For
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * Accept=yes sockets we create the next connection service
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * here. For Accept=no this is mostly a NOP since the service
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * is figured out at load time anyway. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (UNIT_DEREF(s->service))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!s->accept)
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = unit_name_to_prefix(UNIT(s)->id, &prefix);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -ENOMEM;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering u->no_gc = true;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unit_ref_set(&s->service, u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic bool have_non_accept_socket(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering SocketPort *p;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!s->accept)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return true;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LIST_FOREACH(port, p, s->ports) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (p->type != SOCKET_SOCKET)
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek return true;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
e88baee88fad8bc59d33b55a7a2d640ef9e16cd6Zbigniew Jędrzejewski-Szmek if (!socket_address_can_accept(&p->address))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return true;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return false;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering}
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int socket_add_mount_links(Socket *s) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering SocketPort *p;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering int r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering assert(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering LIST_FOREACH(port, p, s->ports) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering const char *path = NULL;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (p->type == SOCKET_SOCKET)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering path = socket_address_get_path(&p->address);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering path = p->path;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (!path)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering continue;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_require_mounts_for(UNIT(s), path);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering }
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return 0;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering}
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int socket_add_device_link(Socket *s) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering char *t;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering assert(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (!s->bind_to_device || streq(s->bind_to_device, "lo"))
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return 0;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return unit_add_node_link(UNIT(s), t, false);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering}
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int socket_add_default_dependencies(Socket *s) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering int r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering assert(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering }
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering}
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering_pure_ static bool socket_has_exec(Socket *s) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering unsigned i;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering assert(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering for (i = 0; i < _SOCKET_EXEC_COMMAND_MAX; i++)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (s->exec_command[i])
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return true;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return false;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering}
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int socket_add_extras(Socket *s) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering Unit *u = UNIT(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering int r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering assert(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (have_non_accept_socket(s)) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (!UNIT_DEREF(s->service)) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering Unit *x;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_load_related_unit(u, ".service", &x);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering unit_ref_set(&s->service, x);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering }
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering }
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = socket_add_mount_links(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = socket_add_device_link(s);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_patch_contexts(u);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (r < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return r;
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (socket_has_exec(s)) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = unit_add_exec_dependencies(u, &s->exec_context);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = unit_add_default_slice(u, &s->cgroup_context);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (u->default_dependencies) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = socket_add_default_dependencies(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *socket_find_symlink_target(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering const char *found = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering SocketPort *p;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering LIST_FOREACH(port, p, s->ports) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering const char *f = NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering switch (p->type) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering case SOCKET_FIFO:
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering f = p->path;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering break;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering case SOCKET_SOCKET:
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (p->address.sockaddr.un.sun_path[0] != 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering f = p->address.sockaddr.un.sun_path;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering break;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering default:
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering break;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (f) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (found)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering found = f;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return found;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int socket_verify(Socket *s) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (UNIT(s)->load_state != UNIT_LOADED)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!s->ports) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "Unit lacks Listen setting. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->accept && have_non_accept_socket(s)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "Unit configured for accepting sockets, but sockets are non-accepting. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->accept && s->max_connections <= 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "MaxConnection= setting too small. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->accept && UNIT_DEREF(s->service)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "Explicit service configuration for accepting socket units not supported. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "Unit has PAM enabled. Kill mode must be set to 'control-group'. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_unit_error(UNIT(s), "Unit has symlinks set but none or more than one node in the file system. Refusing.");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return -EINVAL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return 0;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int socket_load(Unit *u) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Socket *s = SOCKET(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering int r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert(u->load_state == UNIT_STUB);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = unit_load_fragment_and_dropin(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (u->load_state == UNIT_LOADED) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* This is a new unit? Then let's add in some extras */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = socket_add_extras(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (r < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return r;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering }
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return socket_verify(s);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering_const_ static const char* listen_lookup(int family, int type) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (family == AF_NETLINK)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return "ListenNetlink";
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (type == SOCK_STREAM)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return "ListenStream";
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering else if (type == SOCK_DGRAM)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return "ListenDatagram";
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering else if (type == SOCK_SEQPACKET)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return "ListenSequentialPacket";
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering assert_not_reached("Unknown socket type");
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return NULL;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering}
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void socket_dump(Unit *u, FILE *f, const char *prefix) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering char time_string[FORMAT_TIMESPAN_MAX];
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering SocketExecCommand c;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Socket *s = SOCKET(u);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering SocketPort *p;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering const char *prefix2;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering assert(s);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering assert(f);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering prefix = strempty(prefix);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering prefix2 = strjoina(prefix, "\t");
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering fprintf(f,
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sSocket State: %s\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sResult: %s\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sBindIPv6Only: %s\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sBacklog: %u\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sSocketMode: %04o\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sDirectoryMode: %04o\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sKeepAlive: %s\n"
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering "%sNoDelay: %s\n"
"%sFreeBind: %s\n"
"%sTransparent: %s\n"
"%sBroadcast: %s\n"
"%sPassCredentials: %s\n"
"%sPassSecurity: %s\n"
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only),
prefix, s->backlog,
prefix, s->socket_mode,
prefix, s->directory_mode,
prefix, yes_no(s->keep_alive),
prefix, yes_no(s->no_delay),
prefix, yes_no(s->free_bind),
prefix, yes_no(s->transparent),
prefix, yes_no(s->broadcast),
prefix, yes_no(s->pass_cred),
prefix, yes_no(s->pass_sec),
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
prefix, yes_no(s->selinux_context_from_net));
if (s->control_pid > 0)
fprintf(f,
"%sControl PID: "PID_FMT"\n",
prefix, s->control_pid);
if (s->bind_to_device)
fprintf(f,
"%sBindToDevice: %s\n",
prefix, s->bind_to_device);
if (s->accept)
fprintf(f,
"%sAccepted: %u\n"
"%sNConnections: %u\n"
"%sMaxConnections: %u\n",
prefix, s->n_accepted,
prefix, s->n_connections,
prefix, s->max_connections);
if (s->priority >= 0)
fprintf(f,
"%sPriority: %i\n",
prefix, s->priority);
if (s->receive_buffer > 0)
fprintf(f,
"%sReceiveBuffer: %zu\n",
prefix, s->receive_buffer);
if (s->send_buffer > 0)
fprintf(f,
"%sSendBuffer: %zu\n",
prefix, s->send_buffer);
if (s->ip_tos >= 0)
fprintf(f,
"%sIPTOS: %i\n",
prefix, s->ip_tos);
if (s->ip_ttl >= 0)
fprintf(f,
"%sIPTTL: %i\n",
prefix, s->ip_ttl);
if (s->pipe_size > 0)
fprintf(f,
"%sPipeSize: %zu\n",
prefix, s->pipe_size);
if (s->mark >= 0)
fprintf(f,
"%sMark: %i\n",
prefix, s->mark);
if (s->mq_maxmsg > 0)
fprintf(f,
"%sMessageQueueMaxMessages: %li\n",
prefix, s->mq_maxmsg);
if (s->mq_msgsize > 0)
fprintf(f,
"%sMessageQueueMessageSize: %li\n",
prefix, s->mq_msgsize);
if (s->reuse_port)
fprintf(f,
"%sReusePort: %s\n",
prefix, yes_no(s->reuse_port));
if (s->smack)
fprintf(f,
"%sSmackLabel: %s\n",
prefix, s->smack);
if (s->smack_ip_in)
fprintf(f,
"%sSmackLabelIPIn: %s\n",
prefix, s->smack_ip_in);
if (s->smack_ip_out)
fprintf(f,
"%sSmackLabelIPOut: %s\n",
prefix, s->smack_ip_out);
if (!isempty(s->user) || !isempty(s->group))
fprintf(f,
"%sOwnerUser: %s\n"
"%sOwnerGroup: %s\n",
prefix, strna(s->user),
prefix, strna(s->group));
if (s->keep_alive_time > 0)
fprintf(f,
"%sKeepAliveTimeSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_time, USEC_PER_SEC));
if (s->keep_alive_interval)
fprintf(f,
"%sKeepAliveIntervalSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->keep_alive_interval, USEC_PER_SEC));
if (s->keep_alive_cnt)
fprintf(f,
"%sKeepAliveProbes: %u\n",
prefix, s->keep_alive_cnt);
if (s->defer_accept)
fprintf(f,
"%sDeferAcceptSec: %s\n",
prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->defer_accept, USEC_PER_SEC));
LIST_FOREACH(port, p, s->ports) {
if (p->type == SOCKET_SOCKET) {
const char *t;
int r;
char *k = NULL;
if ((r = socket_address_print(&p->address, &k)) < 0)
t = strerror(-r);
else
t = k;
fprintf(f, "%s%s: %s\n", prefix, listen_lookup(socket_address_family(&p->address), p->address.type), t);
free(k);
} else if (p->type == SOCKET_SPECIAL)
fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
else if (p->type == SOCKET_MQUEUE)
fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
else
fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
}
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
for (c = 0; c < _SOCKET_EXEC_COMMAND_MAX; c++) {
if (!s->exec_command[c])
continue;
fprintf(f, "%s-> %s:\n",
prefix, socket_exec_command_to_string(c));
exec_command_dump_list(s->exec_command[c], f, prefix2);
}
}
static int instance_from_socket(int fd, unsigned nr, char **instance) {
socklen_t l;
char *r;
union sockaddr_union local, remote;
assert(fd >= 0);
assert(instance);
l = sizeof(local);
if (getsockname(fd, &local.sa, &l) < 0)
return -errno;
l = sizeof(remote);
if (getpeername(fd, &remote.sa, &l) < 0)
return -errno;
switch (local.sa.sa_family) {
case AF_INET: {
uint32_t
a = ntohl(local.in.sin_addr.s_addr),
b = ntohl(remote.in.sin_addr.s_addr);
if (asprintf(&r,
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
ntohs(local.in.sin_port),
b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
ntohs(remote.in.sin_port)) < 0)
return -ENOMEM;
break;
}
case AF_INET6: {
static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
if (memcmp(&local.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0 &&
memcmp(&remote.in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
const uint8_t
*a = local.in6.sin6_addr.s6_addr+12,
*b = remote.in6.sin6_addr.s6_addr+12;
if (asprintf(&r,
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
a[0], a[1], a[2], a[3],
ntohs(local.in6.sin6_port),
b[0], b[1], b[2], b[3],
ntohs(remote.in6.sin6_port)) < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
if (asprintf(&r,
"%u-%s:%u-%s:%u",
nr,
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
ntohs(local.in6.sin6_port),
inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
ntohs(remote.in6.sin6_port)) < 0)
return -ENOMEM;
}
break;
}
case AF_UNIX: {
struct ucred ucred;
int k;
k = getpeercred(fd, &ucred);
if (k >= 0) {
if (asprintf(&r,
"%u-"PID_FMT"-"UID_FMT,
nr, ucred.pid, ucred.uid) < 0)
return -ENOMEM;
} else if (k == -ENODATA) {
/* This handles the case where somebody is
* connecting from another pid/uid namespace
* (e.g. from outside of our container). */
if (asprintf(&r,
"%u-unknown",
nr) < 0)
return -ENOMEM;
} else
return k;
break;
}
default:
assert_not_reached("Unhandled socket type.");
}
*instance = r;
return 0;
}
static void socket_close_fds(Socket *s) {
SocketPort *p;
char **i;
assert(s);
LIST_FOREACH(port, p, s->ports) {
p->event_source = sd_event_source_unref(p->event_source);
if (p->fd < 0)
continue;
p->fd = safe_close(p->fd);
/* One little note: we should normally not delete any
* sockets in the file system here! After all some
* other process we spawned might still have a
* reference of this fd and wants to continue to use
* it. Therefore we delete sockets in the file system
* before we create a new one, not after we stopped
* using one! */
if (s->remove_on_stop) {
switch (p->type) {
case SOCKET_FIFO:
unlink(p->path);
break;
case SOCKET_MQUEUE:
mq_unlink(p->path);
break;
case SOCKET_SOCKET:
socket_address_unlink(&p->address);
break;
default:
break;
}
}
}
if (s->remove_on_stop)
STRV_FOREACH(i, s->symlinks)
unlink(*i);
}
static void socket_apply_socket_options(Socket *s, int fd) {
int r;
assert(s);
assert(fd >= 0);
if (s->keep_alive) {
int b = s->keep_alive;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_KEEPALIVE failed: %m");
}
if (s->keep_alive_time) {
int value = s->keep_alive_time / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPIDLE failed: %m");
}
if (s->keep_alive_interval) {
int value = s->keep_alive_interval / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPINTVL failed: %m");
}
if (s->keep_alive_cnt) {
int value = s->keep_alive_cnt;
if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
}
if (s->defer_accept) {
int value = s->defer_accept / USEC_PER_SEC;
if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_DEFER_ACCEPT failed: %m");
}
if (s->no_delay) {
int b = s->no_delay;
if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
}
if (s->broadcast) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_BROADCAST failed: %m");
}
if (s->pass_cred) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_PASSCRED failed: %m");
}
if (s->pass_sec) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m");
}
if (s->priority >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m");
if (s->receive_buffer > 0) {
int value = (int) s->receive_buffer;
/* We first try with SO_RCVBUFFORCE, in case we have the perms for that */
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_RCVBUF failed: %m");
}
if (s->send_buffer > 0) {
int value = (int) s->send_buffer;
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_SNDBUF failed: %m");
}
if (s->mark >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_MARK failed: %m");
if (s->ip_tos >= 0)
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
log_unit_warning_errno(UNIT(s), errno, "IP_TOS failed: %m");
if (s->ip_ttl >= 0) {
int x;
r = setsockopt(fd, IPPROTO_IP, IP_TTL, &s->ip_ttl, sizeof(s->ip_ttl));
if (socket_ipv6_is_supported())
x = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &s->ip_ttl, sizeof(s->ip_ttl));
else {
x = -1;
errno = EAFNOSUPPORT;
}
if (r < 0 && x < 0)
log_unit_warning_errno(UNIT(s), errno, "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
}
if (s->tcp_congestion)
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m");
if (s->smack_ip_in) {
r = mac_smack_apply_fd(fd, SMACK_ATTR_IPIN, s->smack_ip_in);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m");
}
if (s->smack_ip_out) {
r = mac_smack_apply_fd(fd, SMACK_ATTR_IPOUT, s->smack_ip_out);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m");
}
}
static void socket_apply_fifo_options(Socket *s, int fd) {
int r;
assert(s);
assert(fd >= 0);
if (s->pipe_size > 0)
if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0)
log_unit_warning_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m");
if (s->smack) {
r = mac_smack_apply_fd(fd, SMACK_ATTR_ACCESS, s->smack);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m");
}
}
static int fifo_address_create(
const char *path,
mode_t directory_mode,
mode_t socket_mode,
int *_fd) {
int fd = -1, r = 0;
struct stat st;
mode_t old_mask;
assert(path);
assert(_fd);
mkdir_parents_label(path, directory_mode);
r = mac_selinux_create_file_prepare(path, S_IFIFO);
if (r < 0)
goto fail;
/* Enforce the right access mode for the fifo */
old_mask = umask(~ socket_mode);
/* Include the original umask in our mask */
umask(~socket_mode | old_mask);
r = mkfifo(path, socket_mode);
umask(old_mask);
if (r < 0 && errno != EEXIST) {
r = -errno;
goto fail;
}
if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
r = -errno;
goto fail;
}
mac_selinux_create_file_clear();
if (fstat(fd, &st) < 0) {
r = -errno;
goto fail;
}
if (!S_ISFIFO(st.st_mode) ||
(st.st_mode & 0777) != (socket_mode & ~old_mask) ||
st.st_uid != getuid() ||
st.st_gid != getgid()) {
r = -EEXIST;
goto fail;
}
*_fd = fd;
return 0;
fail:
mac_selinux_create_file_clear();
safe_close(fd);
return r;
}
static int special_address_create(
const char *path,
int *_fd) {
int fd = -1, r = 0;
struct stat st;
assert(path);
assert(_fd);
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
if (fd < 0) {
r = -errno;
goto fail;
}
if (fstat(fd, &st) < 0) {
r = -errno;
goto fail;
}
/* Check whether this is a /proc, /sys or /dev file or char device */
if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
r = -EEXIST;
goto fail;
}
*_fd = fd;
return 0;
fail:
safe_close(fd);
return r;
}
static int mq_address_create(
const char *path,
mode_t mq_mode,
long maxmsg,
long msgsize,
int *_fd) {
int fd = -1, r = 0;
struct stat st;
mode_t old_mask;
struct mq_attr _attr, *attr = NULL;
assert(path);
assert(_fd);
if (maxmsg > 0 && msgsize > 0) {
zero(_attr);
_attr.mq_flags = O_NONBLOCK;
_attr.mq_maxmsg = maxmsg;
_attr.mq_msgsize = msgsize;
attr = &_attr;
}
/* Enforce the right access mode for the mq */
old_mask = umask(~ mq_mode);
/* Include the original umask in our mask */
umask(~mq_mode | old_mask);
fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
umask(old_mask);
if (fd < 0) {
r = -errno;
goto fail;
}
if (fstat(fd, &st) < 0) {
r = -errno;
goto fail;
}
if ((st.st_mode & 0777) != (mq_mode & ~old_mask) ||
st.st_uid != getuid() ||
st.st_gid != getgid()) {
r = -EEXIST;
goto fail;
}
*_fd = fd;
return 0;
fail:
safe_close(fd);
return r;
}
static int socket_symlink(Socket *s) {
const char *p;
char **i;
assert(s);
p = socket_find_symlink_target(s);
if (!p)
return 0;
STRV_FOREACH(i, s->symlinks)
symlink_label(p, *i);
return 0;
}
static int socket_open_fds(Socket *s) {
SocketPort *p;
int r;
char *label = NULL;
bool know_label = false;
assert(s);
LIST_FOREACH(port, p, s->ports) {
if (p->fd >= 0)
continue;
if (p->type == SOCKET_SOCKET) {
if (!know_label) {
/* Figure out label, if we don't it know
* yet. We do it once, for the first
* socket where we need this and
* remember it for the rest. */
if (s->selinux_context_from_net) {
/* Get it from the network label */
r = mac_selinux_get_our_label(&label);
if (r < 0 && r != -EOPNOTSUPP)
goto rollback;
} else {
/* Get it from the executable we are about to start */
r = socket_instantiate_service(s);
if (r < 0)
goto rollback;
if (UNIT_ISSET(s->service) &&
SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
r = mac_selinux_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
if (r < 0 && r != -EPERM && r != -EOPNOTSUPP)
goto rollback;
}
}
know_label = true;
}
r = socket_address_listen(
&p->address,
SOCK_CLOEXEC|SOCK_NONBLOCK,
s->backlog,
s->bind_ipv6_only,
s->bind_to_device,
s->reuse_port,
s->free_bind,
s->transparent,
s->directory_mode,
s->socket_mode,
label);
if (r < 0)
goto rollback;
p->fd = r;
socket_apply_socket_options(s, p->fd);
socket_symlink(s);
} else if (p->type == SOCKET_SPECIAL) {
r = special_address_create(
p->path,
&p->fd);
if (r < 0)
goto rollback;
} else if (p->type == SOCKET_FIFO) {
r = fifo_address_create(
p->path,
s->directory_mode,
s->socket_mode,
&p->fd);
if (r < 0)
goto rollback;
socket_apply_fifo_options(s, p->fd);
socket_symlink(s);
} else if (p->type == SOCKET_MQUEUE) {
r = mq_address_create(
p->path,
s->socket_mode,
s->mq_maxmsg,
s->mq_msgsize,
&p->fd);
if (r < 0)
goto rollback;
} else
assert_not_reached("Unknown port type");
}
mac_selinux_free(label);
return 0;
rollback:
socket_close_fds(s);
mac_selinux_free(label);
return r;
}
static void socket_unwatch_fds(Socket *s) {
SocketPort *p;
int r;
assert(s);
LIST_FOREACH(port, p, s->ports) {
if (p->fd < 0)
continue;
if (!p->event_source)
continue;
r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF);
if (r < 0)
log_unit_debug_errno(UNIT(s), r, "Failed to disable event source: %m");
}
}
static int socket_watch_fds(Socket *s) {
SocketPort *p;
int r;
assert(s);
LIST_FOREACH(port, p, s->ports) {
if (p->fd < 0)
continue;
if (p->event_source) {
r = sd_event_source_set_enabled(p->event_source, SD_EVENT_ON);
if (r < 0)
goto fail;
} else {
r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(p->event_source, "socket-port-io");
}
}
return 0;
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to watch listening fds: %m");
socket_unwatch_fds(s);
return r;
}
static void socket_set_state(Socket *s, SocketState state) {
SocketState old_state;
assert(s);
old_state = s->state;
s->state = state;
if (!IN_SET(state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL)) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
socket_unwatch_control_pid(s);
s->control_command = NULL;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
}
if (state != SOCKET_LISTENING)
socket_unwatch_fds(s);
if (!IN_SET(state,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL))
socket_close_fds(s);
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
}
static int socket_coldplug(Unit *u) {
Socket *s = SOCKET(u);
int r;
assert(s);
assert(s->state == SOCKET_DEAD);
if (s->deserialized_state == s->state)
return 0;
if (IN_SET(s->deserialized_state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL)) {
if (s->control_pid <= 0)
return -EBADMSG;
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
return r;
r = socket_arm_timer(s);
if (r < 0)
return r;
}
if (IN_SET(s->deserialized_state,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL)) {
r = socket_open_fds(s);
if (r < 0)
return r;
}
if (s->deserialized_state == SOCKET_LISTENING) {
r = socket_watch_fds(s);
if (r < 0)
return r;
}
socket_set_state(s, s->deserialized_state);
return 0;
}
static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
_cleanup_free_ char **argv = NULL;
pid_t pid;
int r;
ExecParameters exec_params = {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
.bus_endpoint_fd = -1,
};
assert(s);
assert(c);
assert(_pid);
(void) unit_realize_cgroup(UNIT(s));
if (s->reset_cpu_usage) {
(void) unit_reset_cpu_usage(UNIT(s));
s->reset_cpu_usage = false;
}
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
r = socket_arm_timer(s);
if (r < 0)
goto fail;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
goto fail;
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
r = exec_spawn(UNIT(s),
c,
&s->exec_context,
&exec_params,
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
goto fail;
*_pid = pid;
return 0;
fail:
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
static int socket_chown(Socket *s, pid_t *_pid) {
pid_t pid;
int r;
r = socket_arm_timer(s);
if (r < 0)
goto fail;
/* We have to resolve the user names out-of-process, hence
* let's fork here. It's messy, but well, what can we do? */
pid = fork();
if (pid < 0)
return -errno;
if (pid == 0) {
SocketPort *p;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int ret;
(void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1);
(void) ignore_signals(SIGPIPE, -1);
log_forget_fds();
if (!isempty(s->user)) {
const char *user = s->user;
r = get_user_creds(&user, &uid, &gid, NULL, NULL);
if (r < 0) {
ret = EXIT_USER;
goto fail_child;
}
}
if (!isempty(s->group)) {
const char *group = s->group;
r = get_group_creds(&group, &gid);
if (r < 0) {
ret = EXIT_GROUP;
goto fail_child;
}
}
LIST_FOREACH(port, p, s->ports) {
const char *path = NULL;
if (p->type == SOCKET_SOCKET)
path = socket_address_get_path(&p->address);
else if (p->type == SOCKET_FIFO)
path = p->path;
if (!path)
continue;
if (chown(path, uid, gid) < 0) {
r = -errno;
ret = EXIT_CHOWN;
goto fail_child;
}
}
_exit(0);
fail_child:
log_open();
log_error_errno(r, "Failed to chown socket at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD));
_exit(ret);
}
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
goto fail;
*_pid = pid;
return 0;
fail:
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
return r;
}
static void socket_enter_dead(Socket *s, SocketResult f) {
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
}
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f);
static void socket_enter_stop_post(Socket *s, SocketResult f) {
int r;
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_STOP_POST;
s->control_command = s->exec_command[SOCKET_EXEC_STOP_POST];
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_POST);
} else
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_SUCCESS);
return;
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-post' task: %m");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
int r;
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
(state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM) ?
KILL_KILL : KILL_TERMINATE,
-1,
s->control_pid,
false);
if (r < 0)
goto fail;
if (r > 0) {
r = socket_arm_timer(s);
if (r < 0)
goto fail;
socket_set_state(s, state);
} else if (state == SOCKET_STOP_PRE_SIGTERM)
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_SUCCESS);
else if (state == SOCKET_STOP_PRE_SIGKILL)
socket_enter_stop_post(s, SOCKET_SUCCESS);
else if (state == SOCKET_FINAL_SIGTERM)
socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_SUCCESS);
else
socket_enter_dead(s, SOCKET_SUCCESS);
return;
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
else
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_stop_pre(Socket *s, SocketResult f) {
int r;
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_STOP_PRE;
s->control_command = s->exec_command[SOCKET_EXEC_STOP_PRE];
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
socket_set_state(s, SOCKET_STOP_PRE);
} else
socket_enter_stop_post(s, SOCKET_SUCCESS);
return;
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run 'stop-pre' task: %m");
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_listening(Socket *s) {
int r;
assert(s);
r = socket_watch_fds(s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
goto fail;
}
socket_set_state(s, SOCKET_LISTENING);
return;
fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_start_post(Socket *s) {
int r;
assert(s);
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_POST;
s->control_command = s->exec_command[SOCKET_EXEC_START_POST];
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-post' task: %m");
goto fail;
}
socket_set_state(s, SOCKET_START_POST);
} else
socket_enter_listening(s);
return;
fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_start_chown(Socket *s) {
int r;
assert(s);
r = socket_open_fds(s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
goto fail;
}
if (!isempty(s->user) || !isempty(s->group)) {
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_CHOWN;
s->control_command = NULL;
r = socket_chown(s, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to fork 'start-chown' task: %m");
goto fail;
}
socket_set_state(s, SOCKET_START_CHOWN);
} else
socket_enter_start_post(s);
return;
fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_start_pre(Socket *s) {
int r;
assert(s);
socket_unwatch_control_pid(s);
s->control_command_id = SOCKET_EXEC_START_PRE;
s->control_command = s->exec_command[SOCKET_EXEC_START_PRE];
if (s->control_command) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to run 'start-pre' task: %m");
goto fail;
}
socket_set_state(s, SOCKET_START_PRE);
} else
socket_enter_start_chown(s);
return;
fail:
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
static void socket_enter_running(Socket *s, int cfd) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(s);
/* We don't take connections anymore if we are supposed to
* shut down anyway */
if (unit_stop_pending(UNIT(s))) {
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
safe_close(cfd);
else {
/* Flush all sockets by closing and reopening them */
socket_close_fds(s);
r = socket_open_fds(s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
return;
}
r = socket_watch_fds(s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
}
return;
}
if (cfd < 0) {
Iterator i;
Unit *other;
bool pending = false;
/* If there's already a start pending don't bother to
* do anything */
SET_FOREACH(other, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
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;
goto fail;
}
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
if (r < 0)
goto fail;
}
socket_set_state(s, SOCKET_RUNNING);
} else {
_cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL;
Service *service;
if (s->n_connections >= s->max_connections) {
log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections);
safe_close(cfd);
return;
}
r = socket_instantiate_service(s);
if (r < 0)
goto fail;
r = instance_from_socket(cfd, s->n_accepted, &instance);
if (r < 0) {
if (r != -ENOTCONN)
goto fail;
/* ENOTCONN is legitimate if TCP RST was received.
* This connection is over, but the socket unit lives on. */
safe_close(cfd);
return;
}
r = unit_name_to_prefix(UNIT(s)->id, &prefix);
if (r < 0)
goto fail;
r = unit_name_build(prefix, instance, ".service", &name);
if (r < 0)
goto fail;
r = unit_add_name(UNIT_DEREF(s->service), name);
if (r < 0)
goto fail;
service = SERVICE(UNIT_DEREF(s->service));
unit_ref_unset(&s->service);
s->n_accepted ++;
UNIT(service)->no_gc = false;
unit_choose_id(UNIT(service), name);
r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
if (r < 0)
goto fail;
cfd = -1;
s->n_connections ++;
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
if (r < 0)
goto fail;
/* Notify clients about changed counters */
unit_add_to_dbus_queue(UNIT(s));
}
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));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
safe_close(cfd);
}
static void socket_run_next(Socket *s) {
int r;
assert(s);
assert(s->control_command);
assert(s->control_command->command_next);
socket_unwatch_control_pid(s);
s->control_command = s->control_command->command_next;
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0)
goto fail;
return;
fail:
log_unit_warning_errno(UNIT(s), r, "Failed to run next task: %m");
if (s->state == SOCKET_START_POST)
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
else if (s->state == SOCKET_STOP_POST)
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
else
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
}
static int socket_start(Unit *u) {
Socket *s = SOCKET(u);
assert(s);
/* We cannot fulfill this request right now, try again later
* please! */
if (IN_SET(s->state,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL))
return -EAGAIN;
/* Already on it! */
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST))
return 0;
/* Cannot run this without the service being around */
if (UNIT_ISSET(s->service)) {
Service *service;
service = SERVICE(UNIT_DEREF(s->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
log_unit_error(u, "Socket service %s not loaded, refusing.", UNIT(service)->id);
return -ENOENT;
}
/* If the service is already active we cannot start the
* socket */
if (service->state != SERVICE_DEAD &&
service->state != SERVICE_FAILED &&
service->state != SERVICE_AUTO_RESTART) {
log_unit_error(u, "Socket service %s already active, refusing.", UNIT(service)->id);
return -EBUSY;
}
}
assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
s->result = SOCKET_SUCCESS;
s->reset_cpu_usage = true;
socket_enter_start_pre(s);
return 1;
}
static int socket_stop(Unit *u) {
Socket *s = SOCKET(u);
assert(s);
/* Already on it */
if (IN_SET(s->state,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_STOP_POST,
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL))
return 0;
/* If there's already something running we go directly into
* kill mode. */
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST)) {
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS);
return -EAGAIN;
}
assert(s->state == SOCKET_LISTENING || s->state == SOCKET_RUNNING);
socket_enter_stop_pre(s, SOCKET_SUCCESS);
return 1;
}
static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
Socket *s = SOCKET(u);
SocketPort *p;
int r;
assert(u);
assert(f);
assert(fds);
unit_serialize_item(u, f, "state", socket_state_to_string(s->state));
unit_serialize_item(u, f, "result", socket_result_to_string(s->result));
unit_serialize_item_format(u, f, "n-accepted", "%u", s->n_accepted);
if (s->control_pid > 0)
unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", socket_exec_command_to_string(s->control_command_id));
LIST_FOREACH(port, p, s->ports) {
int copy;
if (p->fd < 0)
continue;
copy = fdset_put_dup(fds, p->fd);
if (copy < 0)
return copy;
if (p->type == SOCKET_SOCKET) {
_cleanup_free_ char *t = NULL;
r = socket_address_print(&p->address, &t);
if (r < 0)
return r;
if (socket_address_family(&p->address) == AF_NETLINK)
unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t);
else
unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t);
} else if (p->type == SOCKET_SPECIAL)
unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
else if (p->type == SOCKET_MQUEUE)
unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path);
else {
assert(p->type == SOCKET_FIFO);
unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
}
}
return 0;
}
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Socket *s = SOCKET(u);
assert(u);
assert(key);
assert(value);
if (streq(key, "state")) {
SocketState state;
state = socket_state_from_string(value);
if (state < 0)
log_unit_debug(u, "Failed to parse state value: %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
SocketResult f;
f = socket_result_from_string(value);
if (f < 0)
log_unit_debug(u, "Failed to parse result value: %s", value);
else if (f != SOCKET_SUCCESS)
s->result = f;
} else if (streq(key, "n-accepted")) {
unsigned k;
if (safe_atou(value, &k) < 0)
log_unit_debug(u, "Failed to parse n-accepted value: %s", value);
else
s->n_accepted += k;
} else if (streq(key, "control-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
log_unit_debug(u, "Failed to parse control-pid value: %s", value);
else
s->control_pid = pid;
} else if (streq(key, "control-command")) {
SocketExecCommand id;
id = socket_exec_command_from_string(value);
if (id < 0)
log_unit_debug(u, "Failed to parse exec-command value: %s", value);
else {
s->control_command_id = id;
s->control_command = s->exec_command[id];
}
} else if (streq(key, "fifo")) {
int fd, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fifo value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_FIFO &&
path_equal_or_files_same(p->path, value+skip))
break;
if (p) {
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
}
} else if (streq(key, "special")) {
int fd, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse special value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_SPECIAL &&
path_equal_or_files_same(p->path, value+skip))
break;
if (p) {
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
}
} else if (streq(key, "mqueue")) {
int fd, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_MQUEUE &&
streq(p->path, value+skip))
break;
if (p) {
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
}
} else if (streq(key, "socket")) {
int fd, type, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (socket_address_is(&p->address, value+skip, type))
break;
if (p) {
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
}
} else if (streq(key, "netlink")) {
int fd, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
else {
LIST_FOREACH(port, p, s->ports)
if (socket_address_is_netlink(&p->address, value+skip))
break;
if (p) {
safe_close(p->fd);
p->fd = fdset_remove(fds, fd);
}
}
} else
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
return 0;
}
static int socket_distribute_fds(Unit *u, FDSet *fds) {
Socket *s = SOCKET(u);
SocketPort *p;
assert(u);
LIST_FOREACH(port, p, s->ports) {
Iterator i;
int fd;
if (p->type != SOCKET_SOCKET)
continue;
if (p->fd >= 0)
continue;
FDSET_FOREACH(fd, fds, i) {
if (socket_address_matches_fd(&p->address, fd)) {
p->fd = fdset_remove(fds, fd);
s->deserialized_state = SOCKET_LISTENING;
break;
}
}
}
return 0;
}
_pure_ static UnitActiveState socket_active_state(Unit *u) {
assert(u);
return state_translation_table[SOCKET(u)->state];
}
_pure_ static const char *socket_sub_state_to_string(Unit *u) {
assert(u);
return socket_state_to_string(SOCKET(u)->state);
}
const char* socket_port_type_to_string(SocketPort *p) {
assert(p);
switch (p->type) {
case SOCKET_SOCKET:
switch (p->address.type) {
case SOCK_STREAM:
return "Stream";
case SOCK_DGRAM:
return "Datagram";
case SOCK_SEQPACKET:
return "SequentialPacket";
case SOCK_RAW:
if (socket_address_family(&p->address) == AF_NETLINK)
return "Netlink";
default:
return NULL;
}
case SOCKET_SPECIAL:
return "Special";
case SOCKET_MQUEUE:
return "MessageQueue";
case SOCKET_FIFO:
return "FIFO";
default:
return NULL;
}
}
_pure_ static bool socket_check_gc(Unit *u) {
Socket *s = SOCKET(u);
assert(u);
return s->n_connections > 0;
}
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
SocketPort *p = userdata;
int cfd = -1;
assert(p);
assert(fd >= 0);
if (p->socket->state != SOCKET_LISTENING)
return 0;
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
log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents);
goto fail;
}
if (p->socket->accept &&
p->type == SOCKET_SOCKET &&
socket_address_can_accept(&p->address)) {
for (;;) {
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK);
if (cfd < 0) {
if (errno == EINTR)
continue;
log_unit_error_errno(UNIT(p->socket), errno, "Failed to accept socket: %m");
goto fail;
}
break;
}
socket_apply_socket_options(p->socket, cfd);
}
socket_enter_running(p->socket, cfd);
return 0;
fail:
socket_enter_stop_pre(p->socket, SOCKET_FAILURE_RESOURCES);
return 0;
}
static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
Socket *s = SOCKET(u);
SocketResult f;
assert(s);
assert(pid >= 0);
if (pid != s->control_pid)
return;
s->control_pid = 0;
if (is_clean_exit(code, status, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
else if (code == CLD_KILLED)
f = SOCKET_FAILURE_SIGNAL;
else if (code == CLD_DUMPED)
f = SOCKET_FAILURE_CORE_DUMP;
else
assert_not_reached("Unknown sigchld code");
if (s->control_command) {
exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
if (s->control_command->ignore)
f = SOCKET_SUCCESS;
}
log_unit_full(u, f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
if (f != SOCKET_SUCCESS)
s->result = f;
if (s->control_command &&
s->control_command->command_next &&
f == SOCKET_SUCCESS) {
log_unit_debug(u, "Running next command for state %s", socket_state_to_string(s->state));
socket_run_next(s);
} else {
s->control_command = NULL;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
/* No further commands for this step, so let's figure
* out what to do next */
log_unit_debug(u, "Got final SIGCHLD for state %s", socket_state_to_string(s->state));
switch (s->state) {
case SOCKET_START_PRE:
if (f == SOCKET_SUCCESS)
socket_enter_start_chown(s);
else
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f);
break;
case SOCKET_START_CHOWN:
if (f == SOCKET_SUCCESS)
socket_enter_start_post(s);
else
socket_enter_stop_pre(s, f);
break;
case SOCKET_START_POST:
if (f == SOCKET_SUCCESS)
socket_enter_listening(s);
else
socket_enter_stop_pre(s, f);
break;
case SOCKET_STOP_PRE:
case SOCKET_STOP_PRE_SIGTERM:
case SOCKET_STOP_PRE_SIGKILL:
socket_enter_stop_post(s, f);
break;
case SOCKET_STOP_POST:
case SOCKET_FINAL_SIGTERM:
case SOCKET_FINAL_SIGKILL:
socket_enter_dead(s, f);
break;
default:
assert_not_reached("Uh, control process died at wrong time.");
}
}
/* Notify clients about changed exit status */
unit_add_to_dbus_queue(u);
}
static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
Socket *s = SOCKET(userdata);
assert(s);
assert(s->timer_event_source == source);
switch (s->state) {
case SOCKET_START_PRE:
log_unit_warning(UNIT(s), "Starting timed out. Terminating.");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_START_CHOWN:
case SOCKET_START_POST:
log_unit_warning(UNIT(s), "Starting timed out. Stopping.");
socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE:
log_unit_warning(UNIT(s), "Stopping timed out. Terminating.");
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE_SIGTERM:
if (s->kill_context.send_sigkill) {
log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL. Ignoring.");
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_STOP_PRE_SIGKILL:
log_unit_warning(UNIT(s), "Processes still around after SIGKILL. Ignoring.");
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_POST:
log_unit_warning(UNIT(s), "Stopping timed out (2). Terminating.");
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
log_unit_warning(UNIT(s), "Stopping timed out (2). Killing.");
socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
log_unit_warning(UNIT(s), "Stopping timed out (2). Skipping SIGKILL. Ignoring.");
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_FINAL_SIGKILL:
log_unit_warning(UNIT(s), "Still around after SIGKILL (2). Entering failed mode.");
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
break;
default:
assert_not_reached("Timeout at wrong time.");
}
return 0;
}
int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
int *rfds;
unsigned rn_fds, k;
SocketPort *p;
assert(s);
assert(fds);
assert(n_fds);
/* Called from the service code for requesting our fds */
rn_fds = 0;
LIST_FOREACH(port, p, s->ports)
if (p->fd >= 0)
rn_fds++;
if (rn_fds <= 0) {
*fds = NULL;
*n_fds = 0;
return 0;
}
rfds = new(int, rn_fds);
if (!rfds)
return -ENOMEM;
k = 0;
LIST_FOREACH(port, p, s->ports)
if (p->fd >= 0)
rfds[k++] = p->fd;
assert(k == rn_fds);
*fds = rfds;
*n_fds = rn_fds;
return 0;
}
static void socket_reset_failed(Unit *u) {
Socket *s = SOCKET(u);
assert(s);
if (s->state == SOCKET_FAILED)
socket_set_state(s, SOCKET_DEAD);
s->result = SOCKET_SUCCESS;
}
static void socket_notify_service_dead(Socket *s, bool failed_permanent) {
assert(s);
/* The service is dead. Dang!
*
* This is strictly for one-instance-for-all-connections
* services. */
if (s->state == SOCKET_RUNNING) {
log_unit_debug(UNIT(s), "Got notified about service death (failed permanently: %s)", yes_no(failed_permanent));
if (failed_permanent)
socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
else
socket_enter_listening(s);
}
}
void socket_connection_unref(Socket *s) {
assert(s);
/* The service is dead. Yay!
*
* This is strictly for one-instance-per-connection
* services. */
assert(s->n_connections > 0);
s->n_connections--;
log_unit_debug(UNIT(s), "One connection closed, %u left.", s->n_connections);
}
static void socket_trigger_notify(Unit *u, Unit *other) {
Socket *s = SOCKET(u);
Service *se;
assert(u);
assert(other);
/* Don't propagate state changes from the service if we are
already down or accepting connections */
if ((s->state != SOCKET_RUNNING &&
s->state != SOCKET_LISTENING) ||
s->accept)
return;
if (other->load_state != UNIT_LOADED ||
other->type != UNIT_SERVICE)
return;
se = SERVICE(other);
if (se->state == SERVICE_FAILED)
socket_notify_service_dead(s, se->result == SERVICE_FAILURE_START_LIMIT);
if (se->state == SERVICE_DEAD ||
se->state == SERVICE_FINAL_SIGTERM ||
se->state == SERVICE_FINAL_SIGKILL ||
se->state == SERVICE_AUTO_RESTART)
socket_notify_service_dead(s, false);
if (se->state == SERVICE_RUNNING)
socket_set_state(s, SOCKET_RUNNING);
}
static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
}
static int socket_get_timeout(Unit *u, uint64_t *timeout) {
Socket *s = SOCKET(u);
int r;
if (!s->timer_event_source)
return 0;
r = sd_event_source_get_time(s->timer_event_source, timeout);
if (r < 0)
return r;
return 1;
}
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = "dead",
[SOCKET_START_PRE] = "start-pre",
[SOCKET_START_CHOWN] = "start-chown",
[SOCKET_START_POST] = "start-post",
[SOCKET_LISTENING] = "listening",
[SOCKET_RUNNING] = "running",
[SOCKET_STOP_PRE] = "stop-pre",
[SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
[SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
[SOCKET_STOP_POST] = "stop-post",
[SOCKET_FINAL_SIGTERM] = "final-sigterm",
[SOCKET_FINAL_SIGKILL] = "final-sigkill",
[SOCKET_FAILED] = "failed"
};
DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
[SOCKET_EXEC_START_CHOWN] = "StartChown",
[SOCKET_EXEC_START_POST] = "StartPost",
[SOCKET_EXEC_STOP_PRE] = "StopPre",
[SOCKET_EXEC_STOP_POST] = "StopPost"
};
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_SUCCESS] = "success",
[SOCKET_FAILURE_RESOURCES] = "resources",
[SOCKET_FAILURE_TIMEOUT] = "timeout",
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent"
};
DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
const UnitVTable socket_vtable = {
.object_size = sizeof(Socket),
.exec_context_offset = offsetof(Socket, exec_context),
.cgroup_context_offset = offsetof(Socket, cgroup_context),
.kill_context_offset = offsetof(Socket, kill_context),
.exec_runtime_offset = offsetof(Socket, exec_runtime),
.sections =
"Unit\0"
"Socket\0"
"Install\0",
.private_section = "Socket",
.init = socket_init,
.done = socket_done,
.load = socket_load,
.coldplug = socket_coldplug,
.dump = socket_dump,
.start = socket_start,
.stop = socket_stop,
.kill = socket_kill,
.get_timeout = socket_get_timeout,
.serialize = socket_serialize,
.deserialize_item = socket_deserialize_item,
.distribute_fds = socket_distribute_fds,
.active_state = socket_active_state,
.sub_state_to_string = socket_sub_state_to_string,
.check_gc = socket_check_gc,
.sigchld_event = socket_sigchld_event,
.trigger_notify = socket_trigger_notify,
.reset_failed = socket_reset_failed,
.bus_vtable = bus_socket_vtable,
.bus_set_property = bus_socket_set_property,
.bus_commit_properties = bus_socket_commit_properties,
.status_message_formats = {
/*.starting_stopping = {
[0] = "Starting socket %s...",
[1] = "Stopping socket %s...",
},*/
.finished_start_job = {
[JOB_DONE] = "Listening on %s.",
[JOB_FAILED] = "Failed to listen on %s.",
[JOB_TIMEOUT] = "Timed out starting %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Closed %s.",
[JOB_FAILED] = "Failed stopping %s.",
[JOB_TIMEOUT] = "Timed out stopping %s.",
},
},
};