mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
utmp: drop setting runlevel entry in utmp
This removes systemd-update-utmp-runlevel.service and related command.
This commit is contained in:
2
TODO
2
TODO
@@ -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
|
||||
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user