* 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
* follow property change dbus spec
+* make systemd bus activatable (?)
+
* pam module
* selinux
-* make systemd bus activatable
+External:
* systemd-sysvinit as package
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()
{ "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" },
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);
}
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);
}
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);
}
service_close_socket_fd(s);
+ service_connection_unref(s);
unit_unwatch_timer(u, &s->timer_watch);
}
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;
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. */
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;
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));
assert(fds);
assert(n_fds);
+ if (s->socket_fd >= 0)
+ return 0;
+
if ((r = service_get_sockets(s, &set)) < 0)
return r;
pid_t pid;
int r;
- int *fds = NULL;
+ int *fds = NULL, *fdsbuf = NULL;
unsigned n_fds = 0;
char **argv = NULL, **env = NULL;
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) {
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 */
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);
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);
s->socket_fd = fd;
s->got_socket_fd = true;
+ s->socket = sock;
+
return 0;
}
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);
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;
static void socket_done(Unit *u) {
Socket *s = SOCKET(u);
SocketPort *p;
+ Meta *i;
assert(s);
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) {
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;
}
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) {
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);
}
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);
}
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;
}
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);
}
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;
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;
}
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);
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;
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);
}
}
+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",
.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,
bool accept;
unsigned n_accepted;
+ unsigned n_connections;
+ unsigned max_connections;
bool failure;
Watch timer_watch;
* 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);