]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: support overriding NOTIFYACCESS= through sd-notify during runtime
authorMike Yuan <me@yhndnzj.com>
Thu, 26 Jan 2023 09:44:03 +0000 (17:44 +0800)
committerMike Yuan <me@yhndnzj.com>
Tue, 21 Mar 2023 22:33:12 +0000 (06:33 +0800)
Closes #25963

man/org.freedesktop.systemd1.xml
man/sd_notify.xml
src/core/dbus-service.c
src/core/service.c
src/core/service.h
src/systemd/sd-daemon.h

index 141fde05b424657736408fc7cf497417b65996cd..bc5f8ea388e6abdd70f992a8282878c8a47d3d7c 100644 (file)
@@ -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 = ...;
index 8a0911286570d2c8bb463cdc61388873da17fc1b..cb07add95eb2f35c1a51b331d711fde17c000030 100644 (file)
         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>
 
index 079cb9b92d2bef92ea6776e6edf2c9ea9f890aa9..24297b52a012981e5dba28330b57508edca8f03c 100644 (file)
@@ -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),
index addd4e66a730eb9a9945eed3f0c5adeda38e1d58..f8f5d6b1ec8b698cddfeb0df747156638cb7c73e 100644 (file)
@@ -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) {
index fd242a7cbfd9126621a77ff85bf6bcdf29220d9b..7663f26f70a12a1f4ec797f405a6b1fdbbd9ff30 100644 (file)
@@ -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;
index 53a1d7ccfe9c0f825457d47e718af75e70fdbccf..6f88cf02fae8b26a4ce32fbc8bab5c00c0dfb8a1 100644 (file)
@@ -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.