Merge pull request #4456 from keszybz/stored-fds
Preserve stored fds over service restart
This commit is contained in:
+19
-22
@@ -205,28 +205,25 @@
|
||||
<varlistentry>
|
||||
<term>FDSTORE=1</term>
|
||||
|
||||
<listitem><para>Stores additional file descriptors in the
|
||||
service manager. File descriptors sent this way will be
|
||||
maintained per-service by the service manager and be passed
|
||||
again using the usual file descriptor passing logic on the
|
||||
next invocation of the service (see
|
||||
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
|
||||
This is useful for implementing service restart schemes where
|
||||
services serialize their state to <filename>/run</filename>,
|
||||
push their file descriptors to the system manager, and are
|
||||
then restarted, retrieving their state again via socket
|
||||
passing and <filename>/run</filename>. Note that the service
|
||||
manager will accept messages for a service only if
|
||||
<varname>FileDescriptorStoreMax=</varname> is set to non-zero
|
||||
for it (defaults to zero). See
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details. Multiple arrays of file descriptors may be sent
|
||||
in separate messages, in which case the arrays are combined.
|
||||
Note that the service manager removes duplicate file
|
||||
descriptors before passing them to the service. Use
|
||||
<function>sd_pid_notify_with_fds()</function> to send messages
|
||||
with <literal>FDSTORE=1</literal>, see
|
||||
below.</para></listitem>
|
||||
<listitem><para>Stores additional file descriptors in the service manager. File
|
||||
descriptors sent this way will be maintained per-service by the service manager
|
||||
and will be passed again using the usual file descriptor passing logic on the next
|
||||
invocation of the service, see
|
||||
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
This is useful for implementing service restart schemes where services serialize
|
||||
their state to <filename>/run</filename>, push their file descriptors to the
|
||||
system manager, and are then restarted, retrieving their state again via socket
|
||||
passing and <filename>/run</filename>. Note that the service manager will accept
|
||||
messages for a service only if <varname>FileDescriptorStoreMax=</varname> is set
|
||||
to non-zero for it (defaults to zero, see
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
|
||||
File descriptors must be pollable, see
|
||||
<citerefentry><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
|
||||
Multiple arrays of file descriptors may be sent in separate messages, in which
|
||||
case the arrays are combined. Note that the service manager removes duplicate
|
||||
file descriptors before passing them to the service. Use
|
||||
<function>sd_pid_notify_with_fds()</function> to send messages with
|
||||
<literal>FDSTORE=1</literal>, see below.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -852,13 +852,13 @@
|
||||
serialized to <filename>/run</filename> and the file
|
||||
descriptors passed to the service manager, to allow restarts
|
||||
without losing state. Defaults to 0, i.e. no file descriptors
|
||||
may be stored in the service manager by default. All file
|
||||
may be stored in the service manager. All file
|
||||
descriptors passed to the service manager from a specific
|
||||
service are passed back to the service's main process on the
|
||||
next service restart. Any file descriptors passed to the
|
||||
service manager are automatically closed when POLLHUP or
|
||||
POLLERR is seen on them, or when the service is fully stopped
|
||||
and no job queued or being executed for it.</para></listitem>
|
||||
and no job is queued or being executed for it.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
+37
-24
@@ -289,7 +289,17 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
|
||||
free(fs);
|
||||
}
|
||||
|
||||
static void service_release_resources(Unit *u) {
|
||||
static void service_release_fd_store(Service *s) {
|
||||
assert(s);
|
||||
|
||||
log_unit_debug(UNIT(s), "Releasing all stored fds");
|
||||
while (s->fd_store)
|
||||
service_fd_store_unlink(s->fd_store);
|
||||
|
||||
assert(s->n_fd_store == 0);
|
||||
}
|
||||
|
||||
static void service_release_resources(Unit *u, bool inactive) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
@@ -297,16 +307,14 @@ static void service_release_resources(Unit *u) {
|
||||
if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
|
||||
return;
|
||||
|
||||
log_unit_debug(u, "Releasing all resources.");
|
||||
log_unit_debug(u, "Releasing resources.");
|
||||
|
||||
s->stdin_fd = safe_close(s->stdin_fd);
|
||||
s->stdout_fd = safe_close(s->stdout_fd);
|
||||
s->stderr_fd = safe_close(s->stderr_fd);
|
||||
|
||||
while (s->fd_store)
|
||||
service_fd_store_unlink(s->fd_store);
|
||||
|
||||
assert(s->n_fd_store == 0);
|
||||
if (inactive)
|
||||
service_release_fd_store(s);
|
||||
}
|
||||
|
||||
static void service_done(Unit *u) {
|
||||
@@ -350,7 +358,7 @@ static void service_done(Unit *u) {
|
||||
|
||||
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
|
||||
|
||||
service_release_resources(u);
|
||||
service_release_resources(u, true);
|
||||
}
|
||||
|
||||
static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) {
|
||||
@@ -360,6 +368,10 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
|
||||
assert(fs);
|
||||
|
||||
/* If we get either EPOLLHUP or EPOLLERR, it's time to remove this entry from the fd store */
|
||||
log_unit_debug(UNIT(fs->service),
|
||||
"Received %s on stored fd %d (%s), closing.",
|
||||
revents & EPOLLERR ? "EPOLLERR" : "EPOLLHUP",
|
||||
fs->fd, strna(fs->fdname));
|
||||
service_fd_store_unlink(fs);
|
||||
return 0;
|
||||
}
|
||||
@@ -368,20 +380,23 @@ static int service_add_fd_store(Service *s, int fd, const char *name) {
|
||||
ServiceFDStore *fs;
|
||||
int r;
|
||||
|
||||
/* fd is always consumed if we return >= 0 */
|
||||
|
||||
assert(s);
|
||||
assert(fd >= 0);
|
||||
|
||||
if (s->n_fd_store >= s->n_fd_store_max)
|
||||
return 0;
|
||||
return -EXFULL; /* Our store is full.
|
||||
* Use this errno rather than E[NM]FILE to distinguish from
|
||||
* the case where systemd itself hits the file limit. */
|
||||
|
||||
LIST_FOREACH(fd_store, fs, s->fd_store) {
|
||||
r = same_fd(fs->fd, fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
/* Already included */
|
||||
safe_close(fd);
|
||||
return 1;
|
||||
return 0; /* fd already included */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +424,7 @@ static int service_add_fd_store(Service *s, int fd, const char *name) {
|
||||
LIST_PREPEND(fd_store, s->fd_store, fs);
|
||||
s->n_fd_store++;
|
||||
|
||||
return 1;
|
||||
return 1; /* fd newly stored */
|
||||
}
|
||||
|
||||
static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
|
||||
@@ -417,10 +432,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
|
||||
|
||||
assert(s);
|
||||
|
||||
if (fdset_size(fds) <= 0)
|
||||
return 0;
|
||||
|
||||
while (s->n_fd_store < s->n_fd_store_max) {
|
||||
while (fdset_size(fds) > 0) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
fd = fdset_steal_first(fds);
|
||||
@@ -428,17 +440,17 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
|
||||
break;
|
||||
|
||||
r = service_add_fd_store(s, fd, name);
|
||||
if (r == -EXFULL)
|
||||
return log_unit_warning_errno(UNIT(s), r,
|
||||
"Cannot store more fds than FileDescriptorStoreMax=%u, closing remaining.",
|
||||
s->n_fd_store_max);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
|
||||
if (r > 0) {
|
||||
log_unit_debug(UNIT(s), "Added fd to fd store.");
|
||||
fd = -1;
|
||||
}
|
||||
return log_unit_error_errno(UNIT(s), r, "Failed to add fd to store: %m");
|
||||
if (r > 0)
|
||||
log_unit_debug(UNIT(s), "Added fd %u (%s) to fd store.", fd, strna(name));
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
if (fdset_size(fds) > 0)
|
||||
log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1225,6 +1237,7 @@ static int service_spawn(
|
||||
return r;
|
||||
|
||||
n_fds = r;
|
||||
log_unit_debug(UNIT(s), "Passing %i fds to service", n_fds);
|
||||
}
|
||||
|
||||
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
|
||||
@@ -2336,7 +2349,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
r = service_add_fd_store(s, fd, t);
|
||||
if (r < 0)
|
||||
log_unit_error_errno(u, r, "Failed to add fd to store: %m");
|
||||
else if (r > 0)
|
||||
else
|
||||
fdset_remove(fds, fd);
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -302,6 +302,7 @@ int unit_set_description(Unit *u, const char *description) {
|
||||
|
||||
bool unit_check_gc(Unit *u) {
|
||||
UnitActiveState state;
|
||||
bool inactive;
|
||||
assert(u);
|
||||
|
||||
if (u->job)
|
||||
@@ -311,16 +312,17 @@ bool unit_check_gc(Unit *u) {
|
||||
return true;
|
||||
|
||||
state = unit_active_state(u);
|
||||
inactive = state == UNIT_INACTIVE;
|
||||
|
||||
/* If the unit is inactive and failed and no job is queued for
|
||||
* it, then release its runtime resources */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(state) &&
|
||||
UNIT_VTABLE(u)->release_resources)
|
||||
UNIT_VTABLE(u)->release_resources(u);
|
||||
UNIT_VTABLE(u)->release_resources(u, inactive);
|
||||
|
||||
/* But we keep the unit object around for longer when it is
|
||||
* referenced or configured to not be gc'ed */
|
||||
if (state != UNIT_INACTIVE)
|
||||
if (!inactive)
|
||||
return true;
|
||||
|
||||
if (u->no_gc)
|
||||
|
||||
+1
-1
@@ -372,7 +372,7 @@ struct UnitVTable {
|
||||
|
||||
/* When the unit is not running and no job for it queued we
|
||||
* shall release its runtime resources */
|
||||
void (*release_resources)(Unit *u);
|
||||
void (*release_resources)(Unit *u, bool inactive);
|
||||
|
||||
/* Invoked on every child that died */
|
||||
void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
|
||||
|
||||
@@ -59,4 +59,12 @@ sleep 3
|
||||
systemctl stop forever-print-hola
|
||||
[[ ! -f "/i-lose-my-logs" ]]
|
||||
|
||||
# https://github.com/systemd/systemd/issues/4408
|
||||
rm -f /i-lose-my-logs
|
||||
systemctl start forever-print-hola
|
||||
sleep 3
|
||||
systemctl kill --signal=SIGKILL systemd-journald
|
||||
sleep 3
|
||||
[[ ! -f "/i-lose-my-logs" ]]
|
||||
|
||||
touch /testok
|
||||
|
||||
Reference in New Issue
Block a user