mirror of
https://github.com/systemd/systemd.git
synced 2024-12-26 03:22:00 +03:00
Merge pull request #7316 from poettering/fd-store-remove
add new FDSTOREREMOVE=1 sd_notify() message
This commit is contained in:
commit
edb5318cbc
2
TODO
2
TODO
@ -58,8 +58,6 @@ Features:
|
||||
* rework ExecOutput and ExecInput enums so that EXEC_OUTPUT_NULL loses its
|
||||
magic meaning and is no longer upgraded to something else if set explicitly.
|
||||
|
||||
* add a way to remove fds from the fdstore by name, and make logind use it
|
||||
|
||||
* in the long run: permit a system with /etc/machine-id linked to /dev/null, to
|
||||
make it lose its identity, i.e. be anonymous. For this we'd have to patch
|
||||
through the whole tree to make all code deal with the case where no machine
|
||||
|
@ -124,12 +124,10 @@
|
||||
<varlistentry>
|
||||
<term>READY=1</term>
|
||||
|
||||
<listitem><para>Tells the service manager that service startup
|
||||
is finished. This is only used by systemd if the service
|
||||
definition file has Type=notify set. Since there is little
|
||||
value in signaling non-readiness, the only value services
|
||||
should send is <literal>READY=1</literal> (i.e.
|
||||
<literal>READY=0</literal> is not defined).</para></listitem>
|
||||
<listitem><para>Tells the service manager that service startup is finished, or the service finished loading its
|
||||
configuration. This is only used by systemd if the service definition file has <varname>Type=notify</varname>
|
||||
set. Since there is little value in signaling non-readiness, the only value services should send is
|
||||
<literal>READY=1</literal> (i.e. <literal>READY=0</literal> is not defined).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -204,6 +202,14 @@
|
||||
watchdog is enabled. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>WATCHDOG_USEC=…</term>
|
||||
|
||||
<listitem><para>Reset <varname>watchdog_usec</varname> value during runtime.
|
||||
Notice that this is not available when using <function>sd_event_set_watchdog()</function>
|
||||
or <function>sd_watchdog_enabled()</function>.
|
||||
Example : <literal>WATCHDOG_USEC=20000000</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>FDSTORE=1</term>
|
||||
@ -229,33 +235,25 @@
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>FDNAME=…</term>
|
||||
<term>FDSTOREREMOVE=1</term>
|
||||
|
||||
<listitem><para>When used in combination with
|
||||
<varname>FDSTORE=1</varname>, specifies a name for the
|
||||
submitted file descriptors. This name is passed to the service
|
||||
during activation, and may be queried using
|
||||
<citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>. File
|
||||
descriptors submitted without this field set, will implicitly
|
||||
get the name <literal>stored</literal> assigned. Note that, if
|
||||
multiple file descriptors are submitted at once, the specified
|
||||
name will be assigned to all of them. In order to assign
|
||||
different names to submitted file descriptors, submit them in
|
||||
separate invocations of
|
||||
<function>sd_pid_notify_with_fds()</function>. The name may
|
||||
consist of any ASCII character, but must not contain control
|
||||
characters or <literal>:</literal>. It may not be longer than
|
||||
255 characters. If a submitted name does not follow these
|
||||
restrictions, it is ignored.</para></listitem>
|
||||
<listitem><para>Removes file descriptors from the file descriptor store. This field needs to be combined with
|
||||
<varname>FDNAME=</varname> to specify the name of the file descriptors to remove.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>WATCHDOG_USEC=…</term>
|
||||
<term>FDNAME=…</term>
|
||||
|
||||
<listitem><para>Reset <varname>watchdog_usec</varname> value during runtime.
|
||||
Notice that this is not available when using <function>sd_event_set_watchdog()</function>
|
||||
or <function>sd_watchdog_enabled()</function>.
|
||||
Example : <literal>WATCHDOG_USEC=20000000</literal></para></listitem>
|
||||
<listitem><para>When used in combination with <varname>FDSTORE=1</varname>, specifies a name for the submitted
|
||||
file descriptors. When used with <varname>FDSTOREREMOVE=1</varname>, specifies the name for the file
|
||||
descriptors to remove. This name is passed to the service during activation, and may be queried using
|
||||
<citerefentry><refentrytitle>sd_listen_fds_with_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>. File
|
||||
descriptors submitted without this field set, will implicitly get the name <literal>stored</literal>
|
||||
assigned. Note that, if multiple file descriptors are submitted at once, the specified name will be assigned to
|
||||
all of them. In order to assign different names to submitted file descriptors, submit them in separate
|
||||
invocations of <function>sd_pid_notify_with_fds()</function>. The name may consist of arbitrary ASCII
|
||||
characters except control characters or <literal>:</literal>. It may not be longer than 255 characters. If a
|
||||
submitted name does not follow these restrictions, it is ignored.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
@ -458,6 +458,21 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void service_remove_fd_store(Service *s, const char *name) {
|
||||
ServiceFDStore *fs, *n;
|
||||
|
||||
assert(s);
|
||||
assert(name);
|
||||
|
||||
LIST_FOREACH_SAFE(fd_store, fs, n, s->fd_store) {
|
||||
if (!streq(fs->fdname, name))
|
||||
continue;
|
||||
|
||||
log_unit_debug(UNIT(s), "Got explicit request to remove fd %i (%s), closing.", fs->fd, name);
|
||||
service_fd_store_unlink(fs);
|
||||
}
|
||||
}
|
||||
|
||||
static int service_arm_timer(Service *s, usec_t usec) {
|
||||
int r;
|
||||
|
||||
@ -3310,38 +3325,57 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool service_notify_message_authorized(Service *s, pid_t pid, char **tags, FDSet *fds) {
|
||||
assert(s);
|
||||
|
||||
if (s->notify_access == NOTIFY_NONE) {
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception is disabled.", pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
|
||||
if (s->main_pid != 0)
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
|
||||
else
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) {
|
||||
if (s->main_pid != 0 && s->control_pid != 0)
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT,
|
||||
pid, s->main_pid, s->control_pid);
|
||||
else if (s->main_pid != 0)
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
|
||||
else if (s->control_pid != 0)
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid);
|
||||
else
|
||||
log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) {
|
||||
Service *s = SERVICE(u);
|
||||
_cleanup_free_ char *cc = NULL;
|
||||
bool notify_dbus = false;
|
||||
const char *e;
|
||||
char **i;
|
||||
|
||||
assert(u);
|
||||
|
||||
cc = strv_join(tags, ", ");
|
||||
if (!service_notify_message_authorized(SERVICE(u), pid, tags, fds))
|
||||
return;
|
||||
|
||||
if (s->notify_access == NOTIFY_NONE) {
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception is disabled.", pid);
|
||||
return;
|
||||
} else if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
|
||||
if (s->main_pid != 0)
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
|
||||
else
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", pid);
|
||||
return;
|
||||
} else if (s->notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) {
|
||||
if (s->main_pid != 0 && s->control_pid != 0)
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT,
|
||||
pid, s->main_pid, s->control_pid);
|
||||
else if (s->main_pid != 0)
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid);
|
||||
else if (s->control_pid != 0)
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for control PID "PID_FMT, pid, s->control_pid);
|
||||
else
|
||||
log_unit_warning(u, "Got notification message from PID "PID_FMT", but reception only permitted for main PID and control PID which are currently not known", pid);
|
||||
return;
|
||||
} else
|
||||
if (log_get_max_level() >= LOG_DEBUG) {
|
||||
_cleanup_free_ char *cc = NULL;
|
||||
|
||||
cc = strv_join(tags, ", ");
|
||||
log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc);
|
||||
}
|
||||
|
||||
/* Interpret MAINPID= */
|
||||
e = strv_find_startswith(tags, "MAINPID=");
|
||||
@ -3352,51 +3386,50 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
|
||||
log_unit_warning(u, "A control process cannot also be the main process");
|
||||
else if (pid == getpid_cached() || pid == 1)
|
||||
log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field");
|
||||
else {
|
||||
else if (pid != s->main_pid) {
|
||||
service_set_main_pid(s, pid);
|
||||
unit_watch_pid(UNIT(s), pid);
|
||||
notify_dbus = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Interpret RELOADING= */
|
||||
if (strv_find(tags, "RELOADING=1")) {
|
||||
/* Interpret READY=/STOPPING=/RELOADING=. Last one wins. */
|
||||
STRV_FOREACH_BACKWARDS(i, tags) {
|
||||
|
||||
s->notify_state = NOTIFY_RELOADING;
|
||||
if (streq(*i, "READY=1")) {
|
||||
s->notify_state = NOTIFY_READY;
|
||||
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_reload_by_notify(s);
|
||||
/* Type=notify services inform us about completed
|
||||
* initialization with READY=1 */
|
||||
if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START)
|
||||
service_enter_start_post(s);
|
||||
|
||||
notify_dbus = true;
|
||||
}
|
||||
/* Sending READY=1 while we are reloading informs us
|
||||
* that the reloading is complete */
|
||||
if (s->state == SERVICE_RELOAD && s->control_pid == 0)
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
|
||||
/* Interpret READY= */
|
||||
if (strv_find(tags, "READY=1")) {
|
||||
notify_dbus = true;
|
||||
break;
|
||||
|
||||
s->notify_state = NOTIFY_READY;
|
||||
} else if (streq(*i, "RELOADING=1")) {
|
||||
s->notify_state = NOTIFY_RELOADING;
|
||||
|
||||
/* Type=notify services inform us about completed
|
||||
* initialization with READY=1 */
|
||||
if (s->type == SERVICE_NOTIFY && s->state == SERVICE_START)
|
||||
service_enter_start_post(s);
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_reload_by_notify(s);
|
||||
|
||||
/* Sending READY=1 while we are reloading informs us
|
||||
* that the reloading is complete */
|
||||
if (s->state == SERVICE_RELOAD && s->control_pid == 0)
|
||||
service_enter_running(s, SERVICE_SUCCESS);
|
||||
notify_dbus = true;
|
||||
break;
|
||||
|
||||
notify_dbus = true;
|
||||
}
|
||||
} else if (streq(*i, "STOPPING=1")) {
|
||||
s->notify_state = NOTIFY_STOPPING;
|
||||
|
||||
/* Interpret STOPPING= */
|
||||
if (strv_find(tags, "STOPPING=1")) {
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_stop_by_notify(s);
|
||||
|
||||
s->notify_state = NOTIFY_STOPPING;
|
||||
|
||||
if (s->state == SERVICE_RUNNING)
|
||||
service_enter_stop_by_notify(s);
|
||||
|
||||
notify_dbus = true;
|
||||
notify_dbus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Interpret STATUS= */
|
||||
@ -3425,13 +3458,13 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
|
||||
if (e) {
|
||||
int status_errno;
|
||||
|
||||
if (safe_atoi(e, &status_errno) < 0 || status_errno < 0)
|
||||
log_unit_warning(u, "Failed to parse ERRNO= field in notification message: %s", e);
|
||||
else {
|
||||
if (s->status_errno != status_errno) {
|
||||
s->status_errno = status_errno;
|
||||
notify_dbus = true;
|
||||
}
|
||||
status_errno = parse_errno(e);
|
||||
if (status_errno < 0)
|
||||
log_unit_warning_errno(u, status_errno,
|
||||
"Failed to parse ERRNO= field in notification message: %s", e);
|
||||
else if (s->status_errno != status_errno) {
|
||||
s->status_errno = status_errno;
|
||||
notify_dbus = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3439,18 +3472,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
|
||||
if (strv_find(tags, "WATCHDOG=1"))
|
||||
service_reset_watchdog(s);
|
||||
|
||||
if (strv_find(tags, "FDSTORE=1")) {
|
||||
const char *name;
|
||||
|
||||
name = strv_find_startswith(tags, "FDNAME=");
|
||||
if (name && !fdname_is_valid(name)) {
|
||||
log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
service_add_fd_store_set(s, fds, name);
|
||||
}
|
||||
|
||||
e = strv_find_startswith(tags, "WATCHDOG_USEC=");
|
||||
if (e) {
|
||||
usec_t watchdog_override_usec;
|
||||
@ -3460,6 +3481,30 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
|
||||
service_reset_watchdog_timeout(s, watchdog_override_usec);
|
||||
}
|
||||
|
||||
/* Process FD store messages. Either FDSTOREREMOVE=1 for removal, or FDSTORE=1 for addition. In both cases,
|
||||
* process FDNAME= for picking the file descriptor name to use. Note that FDNAME= is required when removing
|
||||
* fds, but optional when pushing in new fds, for compatibility reasons. */
|
||||
if (strv_find(tags, "FDSTOREREMOVE=1")) {
|
||||
const char *name;
|
||||
|
||||
name = strv_find_startswith(tags, "FDNAME=");
|
||||
if (!name || !fdname_is_valid(name))
|
||||
log_unit_warning(u, "FDSTOREREMOVE=1 requested, but no valid file descriptor name passed, ignoring.");
|
||||
else
|
||||
service_remove_fd_store(s, name);
|
||||
|
||||
} else if (strv_find(tags, "FDSTORE=1")) {
|
||||
const char *name;
|
||||
|
||||
name = strv_find_startswith(tags, "FDNAME=");
|
||||
if (name && !fdname_is_valid(name)) {
|
||||
log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
(void) service_add_fd_store_set(s, fds, name);
|
||||
}
|
||||
|
||||
/* Notify clients about changed status or main pid */
|
||||
if (notify_dbus)
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
@ -410,6 +410,17 @@ error:
|
||||
void session_device_free(SessionDevice *sd) {
|
||||
assert(sd);
|
||||
|
||||
if (sd->pushed_fd) {
|
||||
const char *m;
|
||||
|
||||
/* Remove the pushed fd again, just in case. */
|
||||
|
||||
m = strjoina("FDSTOREREMOVE=1\n"
|
||||
"FDNAME=session-", sd->session->id);
|
||||
|
||||
(void) sd_notify(false, m);
|
||||
}
|
||||
|
||||
session_device_stop(sd);
|
||||
session_device_notify(sd, SESSION_DEVICE_RELEASE);
|
||||
close_nointr(sd->fd);
|
||||
@ -489,26 +500,30 @@ unsigned int session_device_try_pause_all(Session *s) {
|
||||
}
|
||||
|
||||
int session_device_save(SessionDevice *sd) {
|
||||
_cleanup_free_ char *state = NULL;
|
||||
const char *m;
|
||||
int r;
|
||||
|
||||
assert(sd);
|
||||
|
||||
/* Store device fd in PID1. It will send it back to us on
|
||||
* restart so revocation will continue to work. To make things
|
||||
* simple, send fds for all type of devices even if they don't
|
||||
* support the revocation mechanism so we don't have to handle
|
||||
* them differently later.
|
||||
/* Store device fd in PID1. It will send it back to us on restart so revocation will continue to work. To make
|
||||
* things simple, send fds for all type of devices even if they don't support the revocation mechanism so we
|
||||
* don't have to handle them differently later.
|
||||
*
|
||||
* Note: for device supporting revocation, PID1 will drop a
|
||||
* stored fd automatically if the corresponding device is
|
||||
* revoked. */
|
||||
r = asprintf(&state, "FDSTORE=1\n"
|
||||
"FDNAME=session-%s", sd->session->id);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
* Note: for device supporting revocation, PID1 will drop a stored fd automatically if the corresponding device
|
||||
* is revoked. */
|
||||
|
||||
return sd_pid_notify_with_fds(0, false, state, &sd->fd, 1);
|
||||
if (sd->pushed_fd)
|
||||
return 0;
|
||||
|
||||
m = strjoina("FDSTORE=1\n"
|
||||
"FDNAME=session", sd->session->id);
|
||||
|
||||
r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sd->pushed_fd = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void session_device_attach_fd(SessionDevice *sd, int fd, bool active) {
|
||||
|
@ -41,6 +41,7 @@ struct SessionDevice {
|
||||
int fd;
|
||||
bool active;
|
||||
DeviceType type;
|
||||
bool pushed_fd;
|
||||
|
||||
LIST_FIELDS(struct SessionDevice, sd_by_device);
|
||||
};
|
||||
|
@ -175,12 +175,22 @@ int sd_is_mq(int fd, const char *path);
|
||||
newline separated environment-style variable assignments in a
|
||||
string. The following variables are known:
|
||||
|
||||
READY=1 Tells systemd that daemon startup is finished (only
|
||||
relevant for services of Type=notify). The passed
|
||||
argument is a boolean "1" or "0". Since there is
|
||||
little value in signaling non-readiness the only
|
||||
MAINPID=... The main PID of a daemon, in case systemd did not
|
||||
fork off the process itself. Example: "MAINPID=4711"
|
||||
|
||||
READY=1 Tells systemd that daemon startup or daemon reload
|
||||
is finished (only relevant for services of Type=notify).
|
||||
The passed argument is a boolean "1" or "0". Since there
|
||||
is little value in signaling non-readiness the only
|
||||
value daemons should send is "READY=1".
|
||||
|
||||
RELOADING=1 Tell systemd that the daemon began reloading its
|
||||
configuration. When the configuration has been
|
||||
reloaded completely, READY=1 should be sent to inform
|
||||
systemd about this.
|
||||
|
||||
STOPPING=1 Tells systemd that the daemon is about to go down.
|
||||
|
||||
STATUS=... Passes a single-line status string back to systemd
|
||||
that describes the daemon state. This is free-form
|
||||
and can be used for various purposes: general state
|
||||
@ -195,25 +205,31 @@ int sd_is_mq(int fd, const char *path);
|
||||
BUSERROR=... If a daemon fails, the D-Bus error-style error
|
||||
code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut"
|
||||
|
||||
MAINPID=... The main pid of a daemon, in case systemd did not
|
||||
fork off the process itself. Example: "MAINPID=4711"
|
||||
|
||||
WATCHDOG=1 Tells systemd to update the watchdog timestamp.
|
||||
Services using this feature should do this in
|
||||
regular intervals. A watchdog framework can use the
|
||||
timestamps to detect failed services. Also see
|
||||
sd_watchdog_enabled() below.
|
||||
|
||||
WATCHDOG_USEC=...
|
||||
Reset watchdog_usec value during runtime.
|
||||
To reset watchdog_usec value, start the service again.
|
||||
Example: "WATCHDOG_USEC=20000000"
|
||||
|
||||
FDSTORE=1 Store the file descriptors passed along with the
|
||||
message in the per-service file descriptor store,
|
||||
and pass them to the main process again on next
|
||||
invocation. This variable is only supported with
|
||||
sd_pid_notify_with_fds().
|
||||
|
||||
WATCHDOG_USEC=...
|
||||
Reset watchdog_usec value during runtime.
|
||||
To reset watchdog_usec value, start the service again.
|
||||
Example: "WATCHDOG_USEC=20000000"
|
||||
FDSTOREREMOVE=1
|
||||
Remove one or more file descriptors from the file
|
||||
descriptor store, identified by the name specified
|
||||
in FDNAME=, see below.
|
||||
|
||||
FDNAME= A name to assign to new file descriptors stored in the
|
||||
file descriptor store, or the name of the file descriptors
|
||||
to remove in case of FDSTOREREMOVE=1.
|
||||
|
||||
Daemons can choose to send additional variables. However, it is
|
||||
recommended to prefix variable names not listed above with X_.
|
||||
|
Loading…
Reference in New Issue
Block a user