utmp: drop setting runlevel entry in utmp

This removes systemd-update-utmp-runlevel.service and related command.
This commit is contained in:
Yu Watanabe
2025-04-21 06:27:35 +09:00
parent 8ba48d4bf8
commit dc1505555b
9 changed files with 6 additions and 260 deletions

2
TODO
View File

@@ -2268,7 +2268,7 @@ Features:
* 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

View File

@@ -1185,10 +1185,7 @@ manpages = [
'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'],

View File

@@ -17,26 +17,21 @@
<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>

View File

@@ -11,44 +11,6 @@
#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);
@@ -237,33 +199,3 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
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);
}

View File

@@ -6,11 +6,8 @@
#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);
@@ -27,18 +24,12 @@ static inline void utxent_cleanup(bool *initialized) {
#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;
}

View File

@@ -57,81 +57,6 @@ static int get_startup_monotonic_time(Context *c, usec_t *ret) {
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;
@@ -182,59 +107,10 @@ static int on_shutdown(int argc, char *argv[], void *userdata) {
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 },
{}
};

View File

@@ -45,19 +45,4 @@ systemctl daemon-reload
# 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

View File

@@ -818,11 +818,6 @@ units = [
'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'],

View File

@@ -1,25 +0,0 @@
# 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