mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
Merge pull request #26214 from YHNdnzj/sd-notify-change-notifyaccess
core: support overriding NOTIFYACCESS= through sd-notify during runtime
This commit is contained in:
commit
f1710073c7
@ -2560,7 +2560,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
readonly s Restart = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s PIDFile = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NotifyAccess = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly t RestartUSec = ...;
|
||||
|
@ -154,6 +154,16 @@
|
||||
system check…</literal></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>NOTIFYACCESS=…</term>
|
||||
|
||||
<listitem><para>Reset the access to the service status notification
|
||||
socket during runtime, overriding <varname>NotifyAccess=</varname> setting
|
||||
in the service unit file. See <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details, specifically <literal>NotifyAccess=</literal> for a list of
|
||||
accepted values.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>ERRNO=…</term>
|
||||
|
||||
|
@ -31,8 +31,8 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exit_type, service_exit_type, ServiceExitType);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_notify_access, "s", Service, service_get_notify_access, notify_access_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, service_timeout_abort_usec);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode);
|
||||
@ -223,7 +223,7 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
SD_BUS_PROPERTY("ExitType", "s", property_get_exit_type, offsetof(Service, exit_type), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
@ -125,6 +125,8 @@ static void service_init(Unit *u) {
|
||||
s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
|
||||
EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
|
||||
|
||||
s->notify_access_override = _NOTIFY_ACCESS_INVALID;
|
||||
|
||||
s->watchdog_original_usec = USEC_INFINITY;
|
||||
|
||||
s->oom_policy = _OOM_POLICY_INVALID;
|
||||
@ -202,6 +204,15 @@ void service_close_socket_fd(Service *s) {
|
||||
s->socket_peer = socket_peer_unref(s->socket_peer);
|
||||
}
|
||||
|
||||
static void service_override_notify_access(Service *s, NotifyAccess notify_access_override) {
|
||||
assert(s);
|
||||
|
||||
s->notify_access_override = notify_access_override;
|
||||
|
||||
log_unit_debug(UNIT(s), "notify_access=%s", notify_access_to_string(s->notify_access));
|
||||
log_unit_debug(UNIT(s), "notify_access_override=%s", notify_access_to_string(s->notify_access_override));
|
||||
}
|
||||
|
||||
static void service_stop_watchdog(Service *s) {
|
||||
assert(s);
|
||||
|
||||
@ -850,7 +861,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, yes_no(s->guess_main_pid),
|
||||
prefix, service_type_to_string(s->type),
|
||||
prefix, service_restart_to_string(s->restart),
|
||||
prefix, notify_access_to_string(s->notify_access),
|
||||
prefix, notify_access_to_string(service_get_notify_access(s)),
|
||||
prefix, notify_state_to_string(s->notify_state),
|
||||
prefix, oom_policy_to_string(s->oom_policy),
|
||||
prefix, signal_to_string(s->reload_signal));
|
||||
@ -1465,11 +1476,11 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
|
||||
|
||||
if (flags & EXEC_IS_CONTROL)
|
||||
/* A control process */
|
||||
return IN_SET(s->notify_access, NOTIFY_EXEC, NOTIFY_ALL);
|
||||
return IN_SET(service_get_notify_access(s), NOTIFY_EXEC, NOTIFY_ALL);
|
||||
|
||||
/* We only spawn main processes and control processes, so any
|
||||
* process that is not a control process is a main process */
|
||||
return s->notify_access != NOTIFY_NONE;
|
||||
return service_get_notify_access(s) != NOTIFY_NONE;
|
||||
}
|
||||
|
||||
static Service *service_get_triggering_service(Service *s) {
|
||||
@ -1910,6 +1921,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
|
||||
/* The next restart might not be a manual stop, hence reset the flag indicating manual stops */
|
||||
s->forbid_restart = false;
|
||||
|
||||
/* Reset NotifyAccess override */
|
||||
s->notify_access_override = _NOTIFY_ACCESS_INVALID;
|
||||
|
||||
/* We want fresh tmpdirs in case service is started again immediately */
|
||||
s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
|
||||
|
||||
@ -2404,6 +2418,8 @@ static void service_enter_restart(Service *s) {
|
||||
s->n_restarts ++;
|
||||
s->flush_n_restarts = false;
|
||||
|
||||
s->notify_access_override = _NOTIFY_ACCESS_INVALID;
|
||||
|
||||
log_unit_struct(UNIT(s), LOG_INFO,
|
||||
"MESSAGE_ID=" SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR,
|
||||
LOG_UNIT_INVOCATION_ID(UNIT(s)),
|
||||
@ -2613,6 +2629,7 @@ static int service_start(Unit *u) {
|
||||
s->status_text = mfree(s->status_text);
|
||||
s->status_errno = 0;
|
||||
|
||||
s->notify_access_override = _NOTIFY_ACCESS_INVALID;
|
||||
s->notify_state = NOTIFY_UNKNOWN;
|
||||
|
||||
s->watchdog_original_usec = s->watchdog_usec;
|
||||
@ -2864,6 +2881,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
}
|
||||
}
|
||||
|
||||
if (s->notify_access_override >= 0)
|
||||
(void) serialize_item(f, "notify-access-override", notify_access_to_string(s->notify_access_override));
|
||||
|
||||
(void) serialize_dual_timestamp(f, "watchdog-timestamp", &s->watchdog_timestamp);
|
||||
(void) serialize_bool(f, "forbid-restart", s->forbid_restart);
|
||||
|
||||
@ -3144,7 +3164,15 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
deserialize_dual_timestamp(value, &s->main_exec_status.start_timestamp);
|
||||
else if (streq(key, "main-exec-status-exit"))
|
||||
deserialize_dual_timestamp(value, &s->main_exec_status.exit_timestamp);
|
||||
else if (streq(key, "watchdog-timestamp"))
|
||||
else if (streq(key, "notify-access-override")) {
|
||||
NotifyAccess notify_access;
|
||||
|
||||
notify_access = notify_access_from_string(value);
|
||||
if (notify_access < 0)
|
||||
log_unit_debug(u, "Failed to parse notify-access-override value: %s", value);
|
||||
else
|
||||
s->notify_access_override = notify_access;
|
||||
} else if (streq(key, "watchdog-timestamp"))
|
||||
deserialize_dual_timestamp(value, &s->watchdog_timestamp);
|
||||
else if (streq(key, "forbid-restart")) {
|
||||
int b;
|
||||
@ -3670,7 +3698,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
* has been received */
|
||||
if (f != SERVICE_SUCCESS)
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
|
||||
else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN)
|
||||
else if (!s->remain_after_exit || service_get_notify_access(s) == NOTIFY_MAIN)
|
||||
/* The service has never been and will never be active */
|
||||
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
|
||||
break;
|
||||
@ -4137,12 +4165,14 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
|
||||
static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) {
|
||||
assert(s);
|
||||
|
||||
if (s->notify_access == NOTIFY_NONE) {
|
||||
NotifyAccess notify_access = service_get_notify_access(s);
|
||||
|
||||
if (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 (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
|
||||
@ -4151,7 +4181,7 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) {
|
||||
if (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);
|
||||
@ -4193,7 +4223,7 @@ static void service_notify_message(
|
||||
assert(u);
|
||||
assert(ucred);
|
||||
|
||||
if (!service_notify_message_authorized(SERVICE(u), ucred->pid, fds))
|
||||
if (!service_notify_message_authorized(s, ucred->pid, fds))
|
||||
return;
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
@ -4328,6 +4358,25 @@ static void service_notify_message(
|
||||
}
|
||||
}
|
||||
|
||||
/* Interpret NOTIFYACCESS= */
|
||||
e = strv_find_startswith(tags, "NOTIFYACCESS=");
|
||||
if (e) {
|
||||
NotifyAccess notify_access;
|
||||
|
||||
notify_access = notify_access_from_string(e);
|
||||
if (notify_access < 0)
|
||||
log_unit_warning_errno(u, notify_access,
|
||||
"Failed to parse NOTIFYACCESS= field value '%s' in notification message, ignoring: %m", e);
|
||||
|
||||
/* We don't need to check whether the new access mode is more strict than what is
|
||||
* already in use, since only the privileged process is allowed to change it
|
||||
* in the first place. */
|
||||
if (service_get_notify_access(s) != notify_access) {
|
||||
service_override_notify_access(s, notify_access);
|
||||
notify_dbus = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Interpret ERRNO= */
|
||||
e = strv_find_startswith(tags, "ERRNO=");
|
||||
if (e) {
|
||||
|
@ -195,6 +195,7 @@ struct Service {
|
||||
PathSpec *pid_file_pathspec;
|
||||
|
||||
NotifyAccess notify_access;
|
||||
NotifyAccess notify_access_override;
|
||||
NotifyState notify_state;
|
||||
|
||||
sd_bus_slot *bus_name_pid_lookup_slot;
|
||||
@ -229,6 +230,11 @@ static inline usec_t service_timeout_abort_usec(Service *s) {
|
||||
return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec;
|
||||
}
|
||||
|
||||
static inline NotifyAccess service_get_notify_access(Service *s) {
|
||||
assert(s);
|
||||
return s->notify_access_override < 0 ? s->notify_access : s->notify_access_override;
|
||||
}
|
||||
|
||||
static inline usec_t service_get_watchdog_usec(Service *s) {
|
||||
assert(s);
|
||||
return s->watchdog_override_enable ? s->watchdog_override_usec : s->watchdog_original_usec;
|
||||
|
@ -195,6 +195,10 @@ int sd_is_mq(int fd, const char *path);
|
||||
readable error message. Example: "STATUS=Completed
|
||||
66% of file system check..."
|
||||
|
||||
NOTIFYACCESS=...
|
||||
Reset the access to the service status notification socket.
|
||||
Example: "NOTIFYACCESS=main"
|
||||
|
||||
ERRNO=... If a daemon fails, the errno-style error code,
|
||||
formatted as string. Example: "ERRNO=2" for ENOENT.
|
||||
|
||||
|
1
test/TEST-80-NOTIFYACCESS/Makefile
Symbolic link
1
test/TEST-80-NOTIFYACCESS/Makefile
Symbolic link
@ -0,0 +1 @@
|
||||
../TEST-01-BASIC/Makefile
|
11
test/TEST-80-NOTIFYACCESS/test.sh
Executable file
11
test/TEST-80-NOTIFYACCESS/test.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="test NotifyAccess through sd-notify"
|
||||
TEST_NO_QEMU=1
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
do_test "$@"
|
@ -57,6 +57,9 @@ if install_tests
|
||||
install_subdir('testsuite-63.units',
|
||||
exclude_files : '.gitattributes',
|
||||
install_dir : testdata_dir)
|
||||
install_subdir('testsuite-80.units',
|
||||
exclude_files : '.gitattributes',
|
||||
install_dir : testdata_dir)
|
||||
|
||||
install_data(kbd_model_map,
|
||||
install_dir : testdata_dir + '/test-keymap-util')
|
||||
|
4
test/testsuite-80.units/notify.service
Normal file
4
test/testsuite-80.units/notify.service
Normal file
@ -0,0 +1,4 @@
|
||||
[Service]
|
||||
Type=notify
|
||||
NotifyAccess=all
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/test.sh
|
26
test/testsuite-80.units/test.sh
Executable file
26
test/testsuite-80.units/test.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# shellcheck disable=SC2016
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
systemd-notify --status="Test starts, waiting for 5 seconds"
|
||||
sleep 5
|
||||
|
||||
(
|
||||
systemd-notify --pid=auto
|
||||
systemd-notify "NOTIFYACCESS=main"
|
||||
|
||||
systemd-notify --status="Sending READY=1 in an unpriviledged process"
|
||||
(
|
||||
sleep 0.1
|
||||
systemd-notify --ready
|
||||
)
|
||||
sleep 10
|
||||
|
||||
systemd-notify "MAINPID=$$"
|
||||
)
|
||||
|
||||
systemd-notify --ready --status="OK"
|
||||
systemd-notify "NOTIFYACCESS=none"
|
||||
sleep infinity
|
8
test/units/testsuite-80.service
Normal file
8
test/units/testsuite-80.service
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-80-NOTIFYACCESS
|
||||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
32
test/units/testsuite-80.sh
Executable file
32
test/units/testsuite-80.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# shellcheck disable=SC2016
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/assert.sh
|
||||
. "$(dirname "$0")"/assert.sh
|
||||
|
||||
: >/failed
|
||||
|
||||
systemctl --no-block start notify.service
|
||||
sleep 2
|
||||
|
||||
assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts, waiting for 5 seconds"
|
||||
assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
|
||||
sleep 5
|
||||
|
||||
assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main"
|
||||
assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unpriviledged process"
|
||||
assert_rc 3 systemctl --quiet is-active notify.service
|
||||
sleep 10
|
||||
|
||||
systemctl --quiet is-active notify.service
|
||||
assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK"
|
||||
assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none"
|
||||
|
||||
systemctl stop notify.service
|
||||
assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all"
|
||||
|
||||
touch /testok
|
||||
rm /failed
|
Loading…
Reference in New Issue
Block a user