mirror of
https://github.com/systemd/systemd.git
synced 2024-11-23 18:23:32 +08:00
socket: enforce limit on number of concurrent connections
This commit is contained in:
parent
a3d4e06dfa
commit
6cf6bbc268
8
fixme
8
fixme
@ -1,7 +1,5 @@
|
||||
* calendar time support in timer
|
||||
|
||||
* enforce max number of concurrent connection limit in sockets.
|
||||
|
||||
* complete dbus exposure
|
||||
|
||||
* make conf parser work more like .desktop parsers
|
||||
@ -51,11 +49,13 @@
|
||||
|
||||
* follow property change dbus spec
|
||||
|
||||
* make systemd bus activatable (?)
|
||||
|
||||
* pam module
|
||||
|
||||
* selinux
|
||||
|
||||
* make systemd bus activatable
|
||||
External:
|
||||
|
||||
* systemd-sysvinit as package
|
||||
|
||||
@ -63,8 +63,6 @@
|
||||
|
||||
if [ $PPID -ne 1 && mountpoint /cgroup/systemd ] ; then echo "You suck!" ; fi
|
||||
|
||||
* remove MANAGER_SYSTEM
|
||||
|
||||
Regularly:
|
||||
|
||||
* look for close() vs. close_nointr() vs. close_nointr_nofail()
|
||||
|
@ -1516,6 +1516,7 @@ static int load_from_path(Unit *u, const char *path) {
|
||||
{ "SocketMode", config_parse_mode, &u->socket.socket_mode, "Socket" },
|
||||
{ "KillMode", config_parse_kill_mode, &u->socket.kill_mode, "Socket" },
|
||||
{ "Accept", config_parse_bool, &u->socket.accept, "Socket" },
|
||||
{ "MaxConnections", config_parse_unsigned, &u->socket.max_connections, "Socket" },
|
||||
EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
|
||||
|
||||
{ "What", config_parse_string, &u->mount.parameters_fragment.what, "Mount" },
|
||||
|
@ -645,7 +645,7 @@ static void mount_enter_unmounting(Mount *m, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run umount exectuable: %s", UNIT(m)->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'umount' task: %s", UNIT(m)->meta.id, strerror(-r));
|
||||
mount_enter_mounted(m, false);
|
||||
}
|
||||
|
||||
@ -688,7 +688,7 @@ static void mount_enter_mounting(Mount *m) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run mount exectuable: %s", UNIT(m)->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'mount' task: %s", UNIT(m)->meta.id, strerror(-r));
|
||||
mount_enter_dead(m, false);
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,16 @@ static void service_close_socket_fd(Service *s) {
|
||||
s->socket_fd = -1;
|
||||
}
|
||||
|
||||
static void service_connection_unref(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (!s->socket)
|
||||
return;
|
||||
|
||||
socket_connection_unref(s->socket);
|
||||
s->socket = NULL;
|
||||
}
|
||||
|
||||
static void service_done(Unit *u) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
@ -190,6 +200,7 @@ static void service_done(Unit *u) {
|
||||
}
|
||||
|
||||
service_close_socket_fd(s);
|
||||
service_connection_unref(s);
|
||||
|
||||
unit_unwatch_timer(u, &s->timer_watch);
|
||||
}
|
||||
@ -800,6 +811,11 @@ static int service_verify(Service *s) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->exec_command[SERVICE_EXEC_START]->command_next) {
|
||||
log_error("%s has more than one ExecStart setting. Refusing.", UNIT(s)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->type == SERVICE_DBUS && !s->bus_name) {
|
||||
log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->meta.id);
|
||||
return -EINVAL;
|
||||
@ -993,6 +1009,9 @@ static int service_get_sockets(Service *s, Set **_set) {
|
||||
assert(s);
|
||||
assert(_set);
|
||||
|
||||
if (s->socket_fd >= 0)
|
||||
return 0;
|
||||
|
||||
/* Collects all Socket objects that belong to this
|
||||
* service. Note that a service might have multiple sockets
|
||||
* via multiple names. */
|
||||
@ -1038,6 +1057,9 @@ static int service_notify_sockets_dead(Service *s) {
|
||||
|
||||
assert(s);
|
||||
|
||||
if (s->socket_fd >= 0)
|
||||
return 0;
|
||||
|
||||
/* Notifies all our sockets when we die */
|
||||
if ((r = service_get_sockets(s, &set)) < 0)
|
||||
return r;
|
||||
@ -1107,8 +1129,19 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
|
||||
if (state != SERVICE_START_PRE &&
|
||||
state != SERVICE_START &&
|
||||
!(state == SERVICE_DEAD && UNIT(s)->meta.job))
|
||||
state != SERVICE_START_POST &&
|
||||
state != SERVICE_RUNNING &&
|
||||
state != SERVICE_RELOAD &&
|
||||
state != SERVICE_STOP &&
|
||||
state != SERVICE_STOP_SIGTERM &&
|
||||
state != SERVICE_STOP_SIGKILL &&
|
||||
state != SERVICE_STOP_POST &&
|
||||
state != SERVICE_FINAL_SIGTERM &&
|
||||
state != SERVICE_FINAL_SIGKILL &&
|
||||
!(state == SERVICE_DEAD && UNIT(s)->meta.job)) {
|
||||
service_close_socket_fd(s);
|
||||
service_connection_unref(s);
|
||||
}
|
||||
|
||||
if (old_state != state)
|
||||
log_debug("%s changed %s -> %s", UNIT(s)->meta.id, service_state_to_string(old_state), service_state_to_string(state));
|
||||
@ -1194,6 +1227,9 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
|
||||
assert(fds);
|
||||
assert(n_fds);
|
||||
|
||||
if (s->socket_fd >= 0)
|
||||
return 0;
|
||||
|
||||
if ((r = service_get_sockets(s, &set)) < 0)
|
||||
return r;
|
||||
|
||||
@ -1255,7 +1291,7 @@ static int service_spawn(
|
||||
|
||||
pid_t pid;
|
||||
int r;
|
||||
int *fds = NULL;
|
||||
int *fds = NULL, *fdsbuf = NULL;
|
||||
unsigned n_fds = 0;
|
||||
char **argv = NULL, **env = NULL;
|
||||
|
||||
@ -1263,12 +1299,20 @@ static int service_spawn(
|
||||
assert(c);
|
||||
assert(_pid);
|
||||
|
||||
if (pass_fds) {
|
||||
if (pass_fds ||
|
||||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
|
||||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
|
||||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
|
||||
|
||||
if (s->socket_fd >= 0) {
|
||||
fds = &s->socket_fd;
|
||||
n_fds = 1;
|
||||
} else if ((r = service_collect_fds(s, &fds, &n_fds)) < 0)
|
||||
goto fail;
|
||||
} else {
|
||||
if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0)
|
||||
goto fail;
|
||||
|
||||
fds = fdsbuf;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout && s->timeout_usec) {
|
||||
@ -1321,12 +1365,8 @@ static int service_spawn(
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (fds) {
|
||||
if (s->socket_fd >= 0)
|
||||
service_close_socket_fd(s);
|
||||
else
|
||||
free(fds);
|
||||
}
|
||||
if (fdsbuf)
|
||||
free(fdsbuf);
|
||||
|
||||
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
@ -1756,7 +1796,7 @@ static void service_run_next(Service *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run spawn next task: %s", UNIT(s)->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run next task: %s", UNIT(s)->meta.id, strerror(-r));
|
||||
|
||||
if (s->state == SERVICE_START_PRE)
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGTERM, false);
|
||||
@ -2524,7 +2564,7 @@ static void service_bus_query_pid_done(
|
||||
service_set_main_pid(s, pid);
|
||||
}
|
||||
|
||||
int service_set_socket_fd(Service *s, int fd) {
|
||||
int service_set_socket_fd(Service *s, int fd, Socket *sock) {
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
@ -2543,6 +2583,8 @@ int service_set_socket_fd(Service *s, int fd) {
|
||||
|
||||
s->socket_fd = fd;
|
||||
s->got_socket_fd = true;
|
||||
s->socket = sock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -137,13 +137,14 @@ struct Service {
|
||||
RateLimit ratelimit;
|
||||
|
||||
int socket_fd;
|
||||
struct Socket *socket;
|
||||
|
||||
Watch timer_watch;
|
||||
};
|
||||
|
||||
extern const UnitVTable service_vtable;
|
||||
|
||||
int service_set_socket_fd(Service *s, int fd);
|
||||
int service_set_socket_fd(Service *s, int fd, struct Socket *socket);
|
||||
|
||||
const char* service_state_to_string(ServiceState i);
|
||||
ServiceState service_state_from_string(const char *s);
|
||||
|
73
src/socket.c
73
src/socket.c
@ -63,6 +63,8 @@ static void socket_init(Unit *u) {
|
||||
s->directory_mode = 0755;
|
||||
s->socket_mode = 0666;
|
||||
|
||||
s->max_connections = 64;
|
||||
|
||||
exec_context_init(&s->exec_context);
|
||||
|
||||
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
|
||||
@ -81,6 +83,7 @@ static void socket_unwatch_control_pid(Socket *s) {
|
||||
static void socket_done(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
SocketPort *p;
|
||||
Meta *i;
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -108,6 +111,14 @@ static void socket_done(Unit *u) {
|
||||
s->bind_to_device = NULL;
|
||||
|
||||
unit_unwatch_timer(u, &s->timer_watch);
|
||||
|
||||
/* Make sure no service instance refers to us anymore. */
|
||||
LIST_FOREACH(units_per_type, i, u->meta.manager->units_per_type[UNIT_SERVICE]) {
|
||||
Service *service = (Service *) i;
|
||||
|
||||
if (service->socket == s)
|
||||
service->socket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool have_non_accept_socket(Socket *s) {
|
||||
@ -141,6 +152,11 @@ static int socket_verify(Socket *s) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->accept && s->max_connections <= 0) {
|
||||
log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->meta.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -307,8 +323,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
|
||||
if (s->accept)
|
||||
fprintf(f,
|
||||
"%sAccepted: %u\n",
|
||||
prefix, s->n_accepted);
|
||||
"%sAccepted: %u\n"
|
||||
"%sNConnections: %u\n"
|
||||
"%sMaxConnections: %u\n",
|
||||
prefix, s->n_accepted,
|
||||
prefix, s->n_connections,
|
||||
prefix, s->max_connections);
|
||||
|
||||
LIST_FOREACH(port, p, s->ports) {
|
||||
|
||||
@ -734,7 +754,7 @@ static void socket_enter_stop_post(Socket *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop-post executable: %s", s->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'stop-post' task: %s", s->meta.id, strerror(-r));
|
||||
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, false);
|
||||
}
|
||||
|
||||
@ -809,7 +829,7 @@ static void socket_enter_stop_pre(Socket *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run stop-pre executable: %s", s->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'stop-pre' task: %s", s->meta.id, strerror(-r));
|
||||
socket_enter_stop_post(s, false);
|
||||
}
|
||||
|
||||
@ -844,7 +864,7 @@ static void socket_enter_start_post(Socket *s) {
|
||||
|
||||
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
|
||||
if ((r = socket_spawn(s, s->control_command, &s->control_pid)) < 0) {
|
||||
log_warning("%s failed to run start-post executable: %s", s->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'start-post' task: %s", s->meta.id, strerror(-r));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -877,7 +897,7 @@ static void socket_enter_start_pre(Socket *s) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run start-pre exectuable: %s", s->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run 'start-pre' task: %s", s->meta.id, strerror(-r));
|
||||
socket_enter_dead(s, false);
|
||||
}
|
||||
|
||||
@ -895,6 +915,12 @@ static void socket_enter_running(Socket *s, int cfd) {
|
||||
Unit *u;
|
||||
char *prefix, *instance, *name;
|
||||
|
||||
if (s->n_connections >= s->max_connections) {
|
||||
log_warning("Too many incoming connections (%u)", s->n_connections);
|
||||
close_nointr_nofail(cfd);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((r = instance_from_socket(cfd, s->n_accepted++, &instance)) < 0)
|
||||
goto fail;
|
||||
|
||||
@ -919,11 +945,13 @@ static void socket_enter_running(Socket *s, int cfd) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if ((r = service_set_socket_fd(SERVICE(u), cfd)) < 0)
|
||||
if ((r = service_set_socket_fd(SERVICE(u), cfd, s)) < 0)
|
||||
goto fail;
|
||||
|
||||
cfd = -1;
|
||||
|
||||
s->n_connections ++;
|
||||
|
||||
if ((r = manager_add_job(u->meta.manager, JOB_START, u, JOB_REPLACE, true, NULL)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
@ -958,7 +986,7 @@ static void socket_run_next(Socket *s, bool success) {
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_warning("%s failed to run spawn next executable: %s", s->meta.id, strerror(-r));
|
||||
log_warning("%s failed to run next task: %s", s->meta.id, strerror(-r));
|
||||
|
||||
if (s->state == SOCKET_START_POST)
|
||||
socket_enter_stop_pre(s, false);
|
||||
@ -1181,6 +1209,14 @@ static const char *socket_sub_state_to_string(Unit *u) {
|
||||
return socket_state_to_string(SOCKET(u)->state);
|
||||
}
|
||||
|
||||
static bool socket_check_gc(Unit *u) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
assert(u);
|
||||
|
||||
return s->n_connections > 0;
|
||||
}
|
||||
|
||||
static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
|
||||
Socket *s = SOCKET(u);
|
||||
int cfd = -1;
|
||||
@ -1375,7 +1411,10 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
|
||||
void socket_notify_service_dead(Socket *s) {
|
||||
assert(s);
|
||||
|
||||
/* The service is dead. Dang. */
|
||||
/* The service is dead. Dang!
|
||||
*
|
||||
* This is strictly for one-instance-for-all-connections
|
||||
* services. */
|
||||
|
||||
if (s->state == SOCKET_RUNNING) {
|
||||
log_debug("%s got notified about service death.", s->meta.id);
|
||||
@ -1383,6 +1422,20 @@ void socket_notify_service_dead(Socket *s) {
|
||||
}
|
||||
}
|
||||
|
||||
void socket_connection_unref(Socket *s) {
|
||||
assert(s);
|
||||
|
||||
/* The service is dead. Yay!
|
||||
*
|
||||
* This is strictly for one-onstance-per-connection
|
||||
* services. */
|
||||
|
||||
assert(s->n_connections > 0);
|
||||
s->n_connections--;
|
||||
|
||||
log_debug("%s: One connection closed, %u left.", s->meta.id, s->n_connections);
|
||||
}
|
||||
|
||||
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
|
||||
[SOCKET_DEAD] = "dead",
|
||||
[SOCKET_START_PRE] = "start-pre",
|
||||
@ -1429,6 +1482,8 @@ const UnitVTable socket_vtable = {
|
||||
.active_state = socket_active_state,
|
||||
.sub_state_to_string = socket_sub_state_to_string,
|
||||
|
||||
.check_gc = socket_check_gc,
|
||||
|
||||
.fd_event = socket_fd_event,
|
||||
.sigchld_event = socket_sigchld_event,
|
||||
.timer_event = socket_timer_event,
|
||||
|
@ -103,6 +103,8 @@ struct Socket {
|
||||
|
||||
bool accept;
|
||||
unsigned n_accepted;
|
||||
unsigned n_connections;
|
||||
unsigned max_connections;
|
||||
|
||||
bool failure;
|
||||
Watch timer_watch;
|
||||
@ -118,6 +120,9 @@ void socket_notify_service_dead(Socket *s);
|
||||
* any of the sockets of this socket */
|
||||
int socket_add_one_mount_link(Socket *s, Mount *m);
|
||||
|
||||
/* Called from the service code when a per-connection service ended */
|
||||
void socket_connection_unref(Socket *s);
|
||||
|
||||
extern const UnitVTable socket_vtable;
|
||||
|
||||
const char* socket_state_to_string(SocketState i);
|
||||
|
Loading…
Reference in New Issue
Block a user