PID 1 will start to have the effect of shutting down the system
cleanly).
- * New D-Bus properties ExecMainHandoverTimestamp and
- ExecMainHandoverTimestampMonotonic are now published by services of
- type exec, dbus, notify, and notify-reload.
- This timestamp is taken as the very last operation before executing
- a service's binary, which allows users to accurately track when
- execution control of the process is handed over from systemd to the
- payload.
+ * New D-Bus properties ExecMainHandoffTimestamp and
+ ExecMainHandoffTimestampMonotonic are now published by services
+ units. This timestamp is taken as the very last operation before
+ handing off control to invoked binaries. This information is
+ available for other unit types that fork off processes (i.e. mount,
+ swap, socket units), but currently only via "systemd-analyze dump".
Journal:
readonly t ExecMainStartTimestampMonotonic = ...;
readonly t ExecMainExitTimestamp = ...;
readonly t ExecMainExitTimestampMonotonic = ...;
- readonly t ExecMainHandoverTimestamp = ...;
- readonly t ExecMainHandoverTimestampMonotonic = ...;
+ readonly t ExecMainHandoffTimestamp = ...;
+ readonly t ExecMainHandoffTimestampMonotonic = ...;
readonly u ExecMainPID = ...;
readonly i ExecMainCode = ...;
readonly i ExecMainStatus = ...;
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainExitTimestampMonotonic"/>
- <variablelist class="dbus-property" generated="True" extra-ref="ExecMainHandoverTimestamp"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExecMainHandoffTimestamp"/>
- <variablelist class="dbus-property" generated="True" extra-ref="ExecMainHandoverTimestampMonotonic"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExecMainHandoffTimestampMonotonic"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainPID"/>
<para><varname>ExecMainStartTimestamp</varname>, <varname>ExecMainStartTimestampMonotonic</varname>,
<varname>ExecMainExitTimestamp</varname>, <varname>ExecMainExitTimestampMonotonic</varname>,
- <varname>ExecMainHandoverTimestamp</varname>, <varname>ExecMainHandoverTimestampMonotonic</varname>,
+ <varname>ExecMainHandoffTimestamp</varname>, <varname>ExecMainHandoffTimestampMonotonic</varname>,
<varname>ExecMainPID</varname>, <varname>ExecMainCode</varname>, <varname>ExecMainStatus</varname>
contain information about the main process of the service as far as it is known. The
- <varname>ExecMainStartTimestamp</varname> timestamps record when the main child process is spawned by
- the service manager. <varname>ExecMainExitTimestamp</varname> timestamps record when the main child
- process exit has been detected by the service manager. <varname>ExecMainHandoverTimestamp</varname>
- timestamps record when the service executable is executed by <command>systemd-executor</command> for
- services of type <literal>exec</literal>, <literal>dbus</literal>, <literal>notify</literal>, and
- <literal>notify-reload</literal>. This is often the same runtime information that is stored in
- <varname>ExecStart=</varname>. However, it deviates for <varname>Type=forking</varname> services where
- the main process of the service is not forked off systemd directly. These fields either contain
- information of the last run of the process or of the current running process.</para>
+ <varname>ExecMainStartTimestamp</varname> timestamps record when the main process of the service is
+ created. <varname>ExecMainExitTimestamp</varname> timestamps record when the main process exit has been
+ detected by the service manager. <varname>ExecMainHandoffTimestamp</varname> timestamps records when
+ the service binary is about to be executed by <command>systemd-executor</command> (this timestamp is
+ recorded regardless if the immediately following <function>execve()</function> system call succeeds or
+ fails). This is often the same runtime information that is also maintained for
+ <varname>ExecStart=</varname>. However, it deviates for services with <varname>Type=forking</varname>
+ as well as services that use <varname>MAINPID=</varname> <function>sd_notify()</function> messages as
+ the main process of the service is not forked off by the service manager directly in that case. These
+ fields either contain information of the last run of the process or of the current running
+ process.</para>
<para><varname>MainPID</varname> and <varname>ControlPID</varname> contain the main and control PID of
the service. The main PID is the current main PID of the service and is 0 when the service currently
<varname>EffectiveMemoryMax</varname>,
<varname>EffectiveTasksMax</varname>,
<varname>MemoryZSwapWriteback</varname>,
- <varname>ExecMainHandoverTimestampMonotonic</varname>, and
- <varname>ExecMainHandoverTimestamp</varname> were added in version 256.</para>
+ <varname>ExecMainHandoffTimestampMonotonic</varname>, and
+ <varname>ExecMainHandoffTimestamp</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
#define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \
BUS_PROPERTY_DUAL_TIMESTAMP(prefix "StartTimestamp", (offset) + offsetof(ExecStatus, start_timestamp), flags), \
BUS_PROPERTY_DUAL_TIMESTAMP(prefix "ExitTimestamp", (offset) + offsetof(ExecStatus, exit_timestamp), flags), \
- BUS_PROPERTY_DUAL_TIMESTAMP(prefix "HandoverTimestamp", (offset) + offsetof(ExecStatus, handover_timestamp), flags), \
+ BUS_PROPERTY_DUAL_TIMESTAMP(prefix "HandoffTimestamp", (offset) + offsetof(ExecStatus, handoff_timestamp), flags), \
SD_BUS_PROPERTY(prefix "PID", "u", bus_property_get_pid, (offset) + offsetof(ExecStatus, pid), flags), \
SD_BUS_PROPERTY(prefix "Code", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, code), flags), \
SD_BUS_PROPERTY(prefix "Status", "i", bus_property_get_int, (offset) + offsetof(ExecStatus, status), flags)
(void) utmp_put_dead_process(context->utmp_id, pid, code, status);
}
+void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts) {
+ assert(s);
+ assert(ucred);
+ assert(ts);
+
+ if (ucred->pid != s->pid)
+ *s = (ExecStatus) {
+ .pid = ucred->pid,
+ };
+
+ s->handoff_timestamp = *ts;
+}
+
void exec_status_reset(ExecStatus *s) {
assert(s);
"%sStart Timestamp: %s\n",
prefix, FORMAT_TIMESTAMP(s->start_timestamp.realtime));
- if (dual_timestamp_is_set(&s->handover_timestamp))
+ if (dual_timestamp_is_set(&s->handoff_timestamp))
fprintf(f,
- "%sHandover Timestamp: %s\n",
- prefix, FORMAT_TIMESTAMP(s->handover_timestamp.realtime));
+ "%sHandoff Timestamp: %s\n",
+ prefix, FORMAT_TIMESTAMP(s->handoff_timestamp.realtime));
if (dual_timestamp_is_set(&s->exit_timestamp))
fprintf(f,
struct ExecStatus {
dual_timestamp start_timestamp;
dual_timestamp exit_timestamp;
- dual_timestamp handover_timestamp;
+ dual_timestamp handoff_timestamp;
pid_t pid;
int code; /* as in siginfo_t::si_code */
int status; /* as in siginfo_t::si_status */
int stdout_fd;
int stderr_fd;
- /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done. It
- * will also be used to send a timestamp taken as the very last operation before execve, for
- * tracking purposes. */
+ /* An fd that is closed by the execve(), and thus will result in EOF when the execve() is done. */
int exec_fd;
char *notify_socket;
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
+void exec_status_handoff(ExecStatus *s, const struct ucred *ucred, const dual_timestamp *ts);
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
void exec_status_reset(ExecStatus *s);
(void) serialize_item_format(f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
(void) serialize_dual_timestamp(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
(void) serialize_dual_timestamp(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
- (void) serialize_dual_timestamp(f, "main-exec-status-handover", &s->main_exec_status.handover_timestamp);
+ (void) serialize_dual_timestamp(f, "main-exec-status-handoff", &s->main_exec_status.handoff_timestamp);
if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
(void) serialize_item_format(f, "main-exec-status-code", "%i", s->main_exec_status.code);
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, "main-exec-status-handover"))
- deserialize_dual_timestamp(value, &s->main_exec_status.handover_timestamp);
+ else if (streq(key, "main-exec-status-handoff"))
+ deserialize_dual_timestamp(value, &s->main_exec_status.handoff_timestamp);
else if (streq(key, "notify-access-override")) {
NotifyAccess notify_access;
unit_add_to_dbus_queue(u);
}
+static void service_handoff_timestamp(
+ Unit *u,
+ const struct ucred *ucred,
+ const dual_timestamp *ts) {
+
+ Service *s = ASSERT_PTR(SERVICE(u));
+
+ assert(ucred);
+ assert(ts);
+
+ if (s->main_pid.pid == ucred->pid) {
+ if (s->main_command)
+ exec_status_handoff(&s->main_command->exec_status, ucred, ts);
+
+ exec_status_handoff(&s->main_exec_status, ucred, ts);
+ } else if (s->control_pid.pid == ucred->pid && s->control_command)
+ exec_status_handoff(&s->control_command->exec_status, ucred, ts);
+ else
+ return;
+
+ unit_add_to_dbus_queue(u);
+}
+
static int service_get_timeout(Unit *u, usec_t *timeout) {
Service *s = ASSERT_PTR(SERVICE(u));
uint64_t t;
.notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_cgroup_oom = service_notify_cgroup_oom_event,
.notify_message = service_notify_message,
+ .notify_handoff_timestamp = service_handoff_timestamp,
.main_pid = service_main_pid,
.control_pid = service_control_pid,
systemd-run --service-type notify --property NotifyAccess=all --unit notify.service --wait sh -c 'systemd-notify --ready; exit 1' || :
start=$(systemctl show --property=ExecMainStartTimestampMonotonic --value notify.service)
-handover=$(systemctl show --property=ExecMainHandoverTimestampMonotonic --value notify.service)
+handoff=$(systemctl show --property=ExecMainHandoffTimestampMonotonic --value notify.service)
active=$(systemctl show --property=ActiveEnterTimestampMonotonic --value notify.service)
exit=$(systemctl show --property=ExecMainExitTimestampMonotonic --value notify.service)
-[[ $start -le $handover ]]
-[[ $handover -le $active ]]
+[[ $start -le $handoff ]]
+[[ $handoff -le $active ]]
[[ $active -le $exit ]]