From: Mike Yuan Date: Fri, 25 Oct 2024 22:49:18 +0000 (+0200) Subject: core/service: support sd_notify() MAINPIDFD=1 and MAINPIDFDID= X-Git-Tag: v257-rc1~110^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=695323d90a6a097bb08c85c518d10b7601807fe8;p=thirdparty%2Fsystemd.git core/service: support sd_notify() MAINPIDFD=1 and MAINPIDFDID= These serve as race-free alternatives for MAINPID= notification. --- diff --git a/TODO b/TODO index 70e088210a1..286a09de86f 100644 --- a/TODO +++ b/TODO @@ -131,8 +131,7 @@ Features: * $LISTEN_PID, $MAINPID and $SYSTEMD_EXECPID env vars that the service manager sets should be augmented with $LISTEN_PIDFDID, $MAINPIDFDID and - $SYSTEMD_EXECPIDFD (and similar for other env vars we might send). Also, - MAINPID= in sd_notify() should be augmented with MAINPIDFDID=, and so on. + $SYSTEMD_EXECPIDFD (and similar for other env vars we might send). * port copy.c over to use LabelOps for all labelling. diff --git a/man/sd_notify.xml b/man/sd_notify.xml index f04251bd197..5e6c9d6dbcd 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -291,12 +291,35 @@ MAINPID=… - The main process ID (PID) of the service, in case the service manager did not fork - off the process itself. Example: MAINPID=4711. + Change the main process ID (PID) of the service. This is especially useful in the case + where the real main process isn't directly forked off by the service manager. + Example: MAINPID=4711. + + MAINPIDFDID=… + + The pidfd inode number of the new main process (specified through MAINPID=). + This information can be acquired through + fstat2 + on the pidfd and is used to identify the process in a race-free fashion. Alternatively, + a pidfd can be sent directly to the service manager (see MAINPIDFD=1 below). + + + + + + MAINPIDFD=1 + + Similar to MAINPID= with MAINPIDFDID=, but + the process is referenced directly by the pidfd passed to the service manager. This is useful + if pidfd id is not supported on the system. Exactly one fd is expected for this notification. + + + + WATCHDOG=1 diff --git a/src/core/service.c b/src/core/service.c index 1ac158f1eab..f48e15c2ebc 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -4540,6 +4540,72 @@ static bool service_notify_message_authorized(Service *s, PidRef *pid) { } } +static int service_notify_message_parse_new_pid( + Unit *u, + char * const *tags, + FDSet *fds, + PidRef *ret) { + + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + const char *e; + int r; + + assert(u); + assert(ret); + + /* MAINPIDFD=1 always takes precedence */ + if (strv_contains(tags, "MAINPIDFD=1")) { + unsigned n_fds = fdset_size(fds); + if (n_fds != 1) + return log_unit_warning_errno(u, SYNTHETIC_ERRNO(EINVAL), + "Got MAINPIDFD=1 with %s fd, ignoring.", n_fds == 0 ? "no" : "more than one"); + + r = pidref_set_pidfd_consume(&pidref, ASSERT_FD(fdset_steal_first(fds))); + if (r < 0) + return log_unit_warning_errno(u, r, "Failed to create reference to received new main pidfd: %m"); + + goto finish; + } + + e = strv_find_startswith(tags, "MAINPID="); + if (!e) { + *ret = PIDREF_NULL; + return 0; + } + + r = pidref_set_pidstr(&pidref, e); + if (r < 0) + return log_unit_warning_errno(u, r, "Failed to parse MAINPID=%s field in notification message, ignoring: %m", e); + + e = strv_find_startswith(tags, "MAINPIDFDID="); + if (!e) + goto finish; + + uint64_t pidfd_id; + + r = safe_atou64(e, &pidfd_id); + if (r < 0) + return log_unit_warning_errno(u, r, "Failed to parse MAINPIDFDID= in notification message, refusing: %s", e); + + r = pidref_acquire_pidfd_id(&pidref); + if (r < 0) { + if (!ERRNO_IS_NEG_NOT_SUPPORTED(r)) + log_unit_warning_errno(u, r, + "Failed to acquire pidfd id of process " PID_FMT ", not validating MAINPIDFDID=%" PRIu64 ": %m", + pidref.pid, pidfd_id); + goto finish; + } + + if (pidref.fd_id != pidfd_id) + return log_unit_warning_errno(u, SYNTHETIC_ERRNO(ESRCH), + "PIDFD ID of process " PID_FMT " (%" PRIu64 ") mismatches with received MAINPIDFDID=%" PRIu64 ", not changing main PID.", + pidref.pid, pidref.fd_id, pidfd_id); + +finish: + *ret = TAKE_PIDREF(pidref); + return 1; +} + static void service_notify_message( Unit *u, PidRef *pidref, @@ -4565,38 +4631,34 @@ static void service_notify_message( bool notify_dbus = false; const char *e; - /* Interpret MAINPID= */ - e = strv_find_startswith(tags, "MAINPID="); - if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, - SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, - SERVICE_STOP, SERVICE_STOP_SIGTERM)) { - - _cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL; + /* Interpret MAINPID= (+ MAINPIDFDID=) / MAINPIDFD=1 */ + _cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL; - r = pidref_set_pidstr(&new_main_pid, e); - if (r < 0) - log_unit_warning_errno(u, r, "Failed to parse MAINPID=%s field in notification message, ignoring: %m", e); - else if (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid)) { + r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid); + if (r > 0 && + IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, + SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, + SERVICE_STOP, SERVICE_STOP_SIGTERM) && + (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid))) { - r = service_is_suitable_main_pid(s, &new_main_pid, LOG_WARNING); - if (r == 0) { - /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */ + r = service_is_suitable_main_pid(s, &new_main_pid, LOG_WARNING); + if (r == 0) { + /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */ - if (ucred->uid == 0) { - log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid); - r = 1; - } else - log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid); - } - if (r > 0) { - (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid), /* start_timestamp = */ NULL); + if (ucred->uid == 0) { + log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid); + r = 1; + } else + log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid); + } + if (r > 0) { + (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid), /* start_timestamp = */ NULL); - r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); - if (r < 0) - log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", s->main_pid.pid); + r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false); + if (r < 0) + log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", s->main_pid.pid); - notify_dbus = true; - } + notify_dbus = true; } }