This removes systemd-update-utmp-runlevel.service and related command.
* clean up date formatting and parsing so that all absolute/relative timestamps we format can also be parsed
-* on shutdown: move utmp, wall, audit logic all into PID 1 (or logind?), get rid of systemd-update-utmp-runlevel
+* on shutdown: move utmp, wall, audit logic all into PID 1 (or logind?)
* make repeated alt-ctrl-del presses printing a dump
'systemd-udevd-varlink.socket'],
''],
['systemd-update-done.service', '8', ['systemd-update-done'], ''],
- ['systemd-update-utmp.service',
- '8',
- ['systemd-update-utmp', 'systemd-update-utmp-runlevel.service'],
- 'ENABLE_UTMP'],
+ ['systemd-update-utmp.service', '8', ['systemd-update-utmp'], 'ENABLE_UTMP'],
['systemd-user-sessions.service', '8', ['systemd-user-sessions'], 'HAVE_PAM'],
['systemd-userdbd.service', '8', ['systemd-userdbd'], 'ENABLE_USERDB'],
['systemd-validatefs@.service', '8', [], 'HAVE_BLKID'],
<refnamediv>
<refname>systemd-update-utmp.service</refname>
- <refname>systemd-update-utmp-runlevel.service</refname>
<refname>systemd-update-utmp</refname>
- <refpurpose>Write audit and utmp updates at bootup, runlevel
- changes and shutdown</refpurpose>
+ <refpurpose>Write audit and utmp updates at bootup and shutdown</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-update-utmp.service</filename></para>
- <para><filename>systemd-update-utmp-runlevel.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-update-utmp</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><filename>systemd-update-utmp-runlevel.service</filename> is
- a service that writes SysV runlevel changes to utmp and wtmp, as
- well as the audit logs, as they occur.
- <filename>systemd-update-utmp.service</filename> does the same for
- system reboots and shutdown requests.</para>
+ <para>
+ <filename>systemd-update-utmp.service</filename> is a service that writes system reboots and shutdown
+ requests to utmp and wtmp, as well as the audit logs.</para>
</refsect1>
<refsect1>
#include "time-util.h"
#include "utmp-wtmp.h"
-int utmp_get_runlevel(int *runlevel, int *previous) {
- _unused_ _cleanup_(utxent_cleanup) bool utmpx = false;
- struct utmpx *found, lookup = { .ut_type = RUN_LVL };
- const char *e;
-
- assert(runlevel);
-
- /* If these values are set in the environment this takes
- * precedence. Presumably, sysvinit does this to work around a
- * race condition that would otherwise exist where we'd always
- * go to disk and hence might read runlevel data that might be
- * very new and not apply to the current script being executed. */
-
- e = getenv("RUNLEVEL");
- if (!isempty(e)) {
- *runlevel = e[0];
- if (previous)
- *previous = 0;
-
- return 0;
- }
-
- if (utmpxname(UTMPX_FILE) < 0)
- return -errno;
-
- utmpx = utxent_start();
-
- found = getutxid(&lookup);
- if (!found)
- return -errno;
-
- *runlevel = found->ut_pid & 0xFF;
- if (previous)
- *previous = (found->ut_pid >> 8) & 0xFF;
-
- return 0;
-}
-
static void init_timestamp(struct utmpx *store, usec_t t) {
assert(store);
return write_utmp_wtmp(&store, &store_wtmp);
}
-
-int utmp_put_runlevel(int runlevel, int previous) {
- struct utmpx store = {};
- int r;
-
- assert(runlevel > 0);
-
- if (previous <= 0) {
- /* Find the old runlevel automatically */
-
- r = utmp_get_runlevel(&previous, NULL);
- if (r < 0) {
- if (r != -ESRCH)
- return r;
-
- previous = 0;
- }
- }
-
- if (previous == runlevel)
- return 0;
-
- init_entry(&store, 0);
-
- store.ut_type = RUN_LVL;
- store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
- strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
-
- return write_entry_both(&store);
-}
#if ENABLE_UTMP
#include <utmpx.h>
-int utmp_get_runlevel(int *runlevel, int *previous);
-
int utmp_put_shutdown(void);
int utmp_put_reboot(usec_t timestamp);
-int utmp_put_runlevel(int runlevel, int previous);
int utmp_put_dead_process(const char *id, pid_t pid, int code, int status);
int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user);
#else /* ENABLE_UTMP */
-static inline int utmp_get_runlevel(int *runlevel, int *previous) {
- return -ESRCH;
-}
static inline int utmp_put_shutdown(void) {
return 0;
}
static inline int utmp_put_reboot(usec_t timestamp) {
return 0;
}
-static inline int utmp_put_runlevel(int runlevel, int previous) {
- return 0;
-}
static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
return 0;
}
return 0;
}
-#define MAX_ATTEMPTS 64u
-
-static int get_current_runlevel(Context *c) {
- static const struct {
- const int runlevel;
- const char *special;
- } table[] = {
- /* The first target of this list that is active or has a job scheduled wins. We prefer
- * runlevels 5 and 3 here over the others, since these are the main runlevels used on Fedora.
- * It might make sense to change the order on some distributions. */
- { '5', SPECIAL_GRAPHICAL_TARGET },
- { '3', SPECIAL_MULTI_USER_TARGET },
- { '1', SPECIAL_RESCUE_TARGET },
- };
- int r;
-
- assert(c);
-
- for (unsigned n_attempts = 0;;) {
- if (n_attempts++ > 0) {
- /* systemd might have dropped off momentarily, let's not make this an error,
- * and wait some random time. Let's pick a random time in the range 100ms…2000ms,
- * linearly scaled by the number of failed attempts. */
- c->bus = sd_bus_flush_close_unref(c->bus);
-
- usec_t usec =
- UINT64_C(100) * USEC_PER_MSEC +
- random_u64_range(UINT64_C(1900) * USEC_PER_MSEC * n_attempts / MAX_ATTEMPTS);
- (void) usleep_safe(usec);
- }
-
- if (!c->bus) {
- r = bus_connect_system_systemd(&c->bus);
- if (r == -ECONNREFUSED && n_attempts < 64) {
- log_debug_errno(r, "Failed to %s to system bus, retrying after a slight delay: %m",
- n_attempts <= 1 ? "connect" : "reconnect");
- continue;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to reconnect to system bus: %m");
- }
-
- FOREACH_ELEMENT(e, table) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *state = NULL, *path = NULL;
-
- path = unit_dbus_path_from_name(e->special);
- if (!path)
- return log_oom();
-
- r = sd_bus_get_property_string(
- c->bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit",
- "ActiveState",
- &error,
- &state);
- if ((r == -ENOTCONN || bus_error_is_connection(&error)) &&
- n_attempts < MAX_ATTEMPTS) {
- log_debug_errno(r, "Failed to get state of %s, retrying after a slight delay: %s",
- e->special, bus_error_message(&error, r));
- break;
- }
- if (r < 0)
- return log_warning_errno(r, "Failed to get state of %s: %s", e->special, bus_error_message(&error, r));
-
- if (STR_IN_SET(state, "active", "reloading"))
- return e->runlevel;
- }
- if (r >= 0)
- return 0;
- }
-}
-
static int on_reboot(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
usec_t t = 0, boottime;
return q;
}
-static int on_runlevel(int argc, char *argv[], void *userdata) {
- Context *c = ASSERT_PTR(userdata);
- int r, q = 0, previous, runlevel;
-
- /* We finished changing runlevel, so let's write the utmp record and send the audit msg. */
-
- /* First, get last runlevel */
- r = utmp_get_runlevel(&previous, NULL);
- if (r < 0) {
- if (!IN_SET(r, -ESRCH, -ENOENT))
- return log_error_errno(r, "Failed to get the last runlevel from utmp: %m");
-
- previous = 0;
- }
-
- /* Secondly, get new runlevel */
- runlevel = get_current_runlevel(c);
- if (runlevel < 0)
- return runlevel;
- if (runlevel == 0) {
- log_warning("Failed to get the current runlevel, utmp update skipped.");
- return 0;
- }
-
- if (previous == runlevel)
- return 0;
-
-#if HAVE_AUDIT
- if (c->audit_fd >= 0) {
- char s[STRLEN("old-level=_ new-level=_") + 1];
-
- xsprintf(s, "old-level=%c new-level=%c",
- previous > 0 ? previous : 'N',
- runlevel);
-
- if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s,
- "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM)
- q = log_error_errno(errno, "Failed to send audit message: %m");
- }
-#endif
-
- r = utmp_put_runlevel(runlevel, previous);
- if (r < 0 && !IN_SET(r, -ESRCH, -ENOENT))
- return log_error_errno(r, "Failed to write utmp record: %m");
-
- return q;
-}
-
static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "reboot", 1, 1, 0, on_reboot },
{ "shutdown", 1, 1, 0, on_shutdown },
- { "runlevel", 1, 1, 0, on_runlevel },
{}
};
# of systemd-analyze blame. See issue #27187.
systemd-analyze blame
-# Test for 'systemd-update-utmp runlevel' vs 'systemctl daemon-reexec'.
-# See issue #27163.
-# shellcheck disable=SC2034
-if [[ -x /usr/lib/systemd/systemd-update-utmp ]]; then
- for _ in {0..10}; do
- systemctl daemon-reexec &
- pid_reexec=$!
- # shellcheck disable=SC2034
- for _ in {0..10}; do
- SYSTEMD_LOG_LEVEL=debug /usr/lib/systemd/systemd-update-utmp runlevel
- done
- wait "$pid_reexec"
- done
-fi
-
touch /testok
'file' : 'systemd-update-done.service.in',
'symlinks' : ['sysinit.target.wants/'],
},
- {
- 'file' : 'systemd-update-utmp-runlevel.service.in',
- 'conditions' : ['ENABLE_UTMP', 'HAVE_SYSV_COMPAT'],
- 'symlinks' : ['multi-user.target.wants/', 'graphical.target.wants/', 'rescue.target.wants/'],
- },
{
'file' : 'systemd-update-utmp.service.in',
'conditions' : ['ENABLE_UTMP'],
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-
-[Unit]
-Description=Record Runlevel Change in UTMP
-Documentation=man:systemd-update-utmp-runlevel.service(8) man:utmp(5)
-ConditionPathExists=!/etc/initrd-release
-
-DefaultDependencies=no
-RequiresMountsFor=/var/log/wtmp
-Conflicts=shutdown.target
-Requisite=systemd-update-utmp.service
-After=systemd-update-utmp.service
-After=runlevel1.target runlevel2.target runlevel3.target runlevel4.target runlevel5.target
-Before=shutdown.target
-
-[Service]
-Type=oneshot
-ExecStart={{LIBEXECDIR}}/systemd-update-utmp runlevel