From c17516a84a9766c5eb24dfda269db83e9ac400c9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 11:08:16 +0900 Subject: [PATCH 1/9] systemctl: move functions in systemctl-sysv-compat.[ch] - parse_shutdown_time_spec() is used only by systemctl-compat-shutdown.c, - talk_initctl() and action_to_runlevel() are used only by systemctl-compat-telinit.c, - the exit code enum is widely used in systemctl, hence moved to systemctl-util.h. No functional change, preparation for later changes. --- src/systemctl/systemctl-compat-shutdown.c | 60 ++++++++++- src/systemctl/systemctl-compat-telinit.c | 60 ++++++++++- src/systemctl/systemctl-is-active.c | 1 - src/systemctl/systemctl-show.c | 1 - src/systemctl/systemctl-sysv-compat.c | 119 ---------------------- src/systemctl/systemctl-sysv-compat.h | 31 ------ src/systemctl/systemctl-util.h | 23 +++++ 7 files changed, 141 insertions(+), 154 deletions(-) diff --git a/src/systemctl/systemctl-compat-shutdown.c b/src/systemctl/systemctl-compat-shutdown.c index 4084a4fa20..d85ff9ab11 100644 --- a/src/systemctl/systemctl-compat-shutdown.c +++ b/src/systemctl/systemctl-compat-shutdown.c @@ -4,12 +4,15 @@ #include "alloc-util.h" #include "log.h" +#include "parse-util.h" #include "pretty-print.h" #include "reboot-util.h" +#include "string-util.h" #include "strv.h" #include "systemctl.h" #include "systemctl-compat-shutdown.h" -#include "systemctl-sysv-compat.h" +#include "systemctl-logind.h" +#include "time-util.h" static int shutdown_help(void) { _cleanup_free_ char *link = NULL; @@ -47,6 +50,61 @@ static int shutdown_help(void) { return 0; } +static int parse_shutdown_time_spec(const char *t, usec_t *ret) { + int r; + + assert(t); + assert(ret); + + /* This parses SysV compat time spec. */ + + if (streq(t, "now")) + *ret = 0; + else if (!strchr(t, ':')) { + uint64_t u; + + if (safe_atou64(t, &u) < 0) + return -EINVAL; + + *ret = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; + } else { + char *e = NULL; + long hour, minute; + + errno = 0; + hour = strtol(t, &e, 10); + if (errno > 0 || *e != ':' || hour < 0 || hour > 23) + return -EINVAL; + + minute = strtol(e+1, &e, 10); + if (errno > 0 || *e != 0 || minute < 0 || minute > 59) + return -EINVAL; + + usec_t n = now(CLOCK_REALTIME); + struct tm tm = {}; + + r = localtime_or_gmtime_usec(n, /* utc= */ false, &tm); + if (r < 0) + return r; + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + tm.tm_sec = 0; + + usec_t s; + r = mktime_or_timegm_usec(&tm, /* utc= */ false, &s); + if (r < 0) + return r; + + while (s <= n) + s += USEC_PER_DAY; + + *ret = s; + } + + return 0; +} + int shutdown_parse_argv(int argc, char *argv[]) { enum { ARG_HELP = 0x100, diff --git a/src/systemctl/systemctl-compat-telinit.c b/src/systemctl/systemctl-compat-telinit.c index bc0e7a2918..c8794ef255 100644 --- a/src/systemctl/systemctl-compat-telinit.c +++ b/src/systemctl/systemctl-compat-telinit.c @@ -4,13 +4,16 @@ #include #include "alloc-util.h" +#include "fd-util.h" +#include "initreq.h" +#include "io-util.h" #include "log.h" #include "pretty-print.h" +#include "strv.h" #include "systemctl.h" #include "systemctl-compat-telinit.h" #include "systemctl-daemon-reload.h" #include "systemctl-start-unit.h" -#include "systemctl-sysv-compat.h" static int telinit_help(void) { _cleanup_free_ char *link = NULL; @@ -123,6 +126,61 @@ int telinit_parse_argv(int argc, char *argv[]) { return 1; } +#if HAVE_SYSV_COMPAT +static int talk_initctl(char rl) { + _cleanup_close_ int fd = -EBADF; + const char *path; + int r; + + /* Try to switch to the specified SysV runlevel. Returns == 0 if the operation does not apply on this + * system, and > 0 on success. */ + + if (rl == 0) + return 0; + + FOREACH_STRING(_path, "/run/initctl", "/dev/initctl") { + path = _path; + + fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno != ENOENT) + return log_error_errno(errno, "Failed to open %s: %m", path); + if (fd >= 0) + break; + } + if (fd < 0) + return 0; + + struct init_request request = { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL, + .runlevel = rl, + }; + + r = loop_write(fd, &request, sizeof(request)); + if (r < 0) + return log_error_errno(r, "Failed to write to %s: %m", path); + + return 1; +} + +static int action_to_runlevel(void) { + static const char table[_ACTION_MAX] = { + [ACTION_HALT] = '0', + [ACTION_POWEROFF] = '0', + [ACTION_REBOOT] = '6', + [ACTION_RUNLEVEL2] = '2', + [ACTION_RUNLEVEL3] = '3', + [ACTION_RUNLEVEL4] = '4', + [ACTION_RUNLEVEL5] = '5', + [ACTION_RESCUE] = '1' + }; + + assert(arg_action >= 0 && arg_action < _ACTION_MAX); + return table[arg_action]; +} +#endif + int start_with_fallback(void) { int r; diff --git a/src/systemctl/systemctl-is-active.c b/src/systemctl/systemctl-is-active.c index efb18cbb65..19c24eaf7e 100644 --- a/src/systemctl/systemctl-is-active.c +++ b/src/systemctl/systemctl-is-active.c @@ -11,7 +11,6 @@ #include "strv.h" #include "systemctl.h" #include "systemctl-is-active.h" -#include "systemctl-sysv-compat.h" #include "systemctl-util.h" #include "unit-def.h" diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 1db9bf2bea..79e1908a23 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -48,7 +48,6 @@ #include "systemctl.h" #include "systemctl-list-machines.h" #include "systemctl-show.h" -#include "systemctl-sysv-compat.h" #include "systemctl-util.h" #include "terminal-util.h" #include "utf8.h" diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c index 915e2f8930..3905a678b6 100644 --- a/src/systemctl/systemctl-sysv-compat.c +++ b/src/systemctl/systemctl-sysv-compat.c @@ -5,12 +5,8 @@ #include #include "env-util.h" -#include "fd-util.h" -#include "initreq.h" #include "install.h" -#include "io-util.h" #include "log.h" -#include "parse-util.h" #include "path-lookup.h" #include "path-util.h" #include "process-util.h" @@ -18,101 +14,6 @@ #include "strv.h" #include "systemctl.h" #include "systemctl-sysv-compat.h" -#include "time-util.h" - -int talk_initctl(char rl) { -#if HAVE_SYSV_COMPAT - _cleanup_close_ int fd = -EBADF; - const char *path; - int r; - - /* Try to switch to the specified SysV runlevel. Returns == 0 if the operation does not apply on this - * system, and > 0 on success. */ - - if (rl == 0) - return 0; - - FOREACH_STRING(_path, "/run/initctl", "/dev/initctl") { - path = _path; - - fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); - if (fd < 0 && errno != ENOENT) - return log_error_errno(errno, "Failed to open %s: %m", path); - if (fd >= 0) - break; - } - if (fd < 0) - return 0; - - struct init_request request = { - .magic = INIT_MAGIC, - .sleeptime = 0, - .cmd = INIT_CMD_RUNLVL, - .runlevel = rl, - }; - - r = loop_write(fd, &request, sizeof(request)); - if (r < 0) - return log_error_errno(r, "Failed to write to %s: %m", path); - - return 1; -#else - return -EOPNOTSUPP; -#endif -} - -int parse_shutdown_time_spec(const char *t, usec_t *ret) { - int r; - - assert(t); - assert(ret); - - if (streq(t, "now")) - *ret = 0; - else if (!strchr(t, ':')) { - uint64_t u; - - if (safe_atou64(t, &u) < 0) - return -EINVAL; - - *ret = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; - } else { - char *e = NULL; - long hour, minute; - - errno = 0; - hour = strtol(t, &e, 10); - if (errno > 0 || *e != ':' || hour < 0 || hour > 23) - return -EINVAL; - - minute = strtol(e+1, &e, 10); - if (errno > 0 || *e != 0 || minute < 0 || minute > 59) - return -EINVAL; - - usec_t n = now(CLOCK_REALTIME); - struct tm tm = {}; - - r = localtime_or_gmtime_usec(n, /* utc= */ false, &tm); - if (r < 0) - return r; - - tm.tm_hour = (int) hour; - tm.tm_min = (int) minute; - tm.tm_sec = 0; - - usec_t s; - r = mktime_or_timegm_usec(&tm, /* utc= */ false, &s); - if (r < 0) - return r; - - while (s <= n) - s += USEC_PER_DAY; - - *ret = s; - } - - return 0; -} int enable_sysv_units(const char *verb, char **args) { int r = 0; @@ -264,23 +165,3 @@ int enable_sysv_units(const char *verb, char **args) { #endif return r; } - -int action_to_runlevel(void) { -#if HAVE_SYSV_COMPAT - static const char table[_ACTION_MAX] = { - [ACTION_HALT] = '0', - [ACTION_POWEROFF] = '0', - [ACTION_REBOOT] = '6', - [ACTION_RUNLEVEL2] = '2', - [ACTION_RUNLEVEL3] = '3', - [ACTION_RUNLEVEL4] = '4', - [ACTION_RUNLEVEL5] = '5', - [ACTION_RESCUE] = '1' - }; - - assert(arg_action >= 0 && arg_action < _ACTION_MAX); - return table[arg_action]; -#else - return -EOPNOTSUPP; -#endif -} diff --git a/src/systemctl/systemctl-sysv-compat.h b/src/systemctl/systemctl-sysv-compat.h index 159d0e2be7..72e823ffe2 100644 --- a/src/systemctl/systemctl-sysv-compat.h +++ b/src/systemctl/systemctl-sysv-compat.h @@ -1,35 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "forward.h" - -int talk_initctl(char runlevel); - -int parse_shutdown_time_spec(const char *t, usec_t *ret); - -/* The init script exit codes for the LSB 'status' verb. (This is different from the 'start' verb, whose exit - codes are defined in exit-status.h.) - - 0 program is running or service is OK - 1 program is dead and /var/run pid file exists - 2 program is dead and /var/lock lock file exists - 3 program is not running - 4 program or service status is unknown - 5-99 reserved for future LSB use - 100-149 reserved for distribution use - 150-199 reserved for application use - 200-254 reserved - - https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html -*/ -enum { - EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, - EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, - EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, - EXIT_PROGRAM_NOT_RUNNING = 3, - EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, -}; - typedef enum SysVUnitEnableState { SYSV_UNIT_NOT_FOUND = 0, SYSV_UNIT_DISABLED, @@ -37,5 +8,3 @@ typedef enum SysVUnitEnableState { } SysVUnitEnableState; int enable_sysv_units(const char *verb, char **args); - -int action_to_runlevel(void) _pure_; diff --git a/src/systemctl/systemctl-util.h b/src/systemctl/systemctl-util.h index 644cd1e7d9..f2560b6268 100644 --- a/src/systemctl/systemctl-util.h +++ b/src/systemctl/systemctl-util.h @@ -3,6 +3,29 @@ #include "systemctl.h" +/* The init script exit codes for the LSB 'status' verb. (This is different from the 'start' verb, whose exit + codes are defined in exit-status.h.) + + 0 program is running or service is OK + 1 program is dead and /var/run pid file exists + 2 program is dead and /var/lock lock file exists + 3 program is not running + 4 program or service status is unknown + 5-99 reserved for future LSB use + 100-149 reserved for distribution use + 150-199 reserved for application use + 200-254 reserved + + https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html +*/ +enum { + EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, + EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, + EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, + EXIT_PROGRAM_NOT_RUNNING = 3, + EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, +}; + typedef enum BusFocus { BUS_FULL, /* The full bus indicated via --system or --user */ BUS_MANAGER, /* The manager itself, possibly directly, possibly via the bus */ From 5572fb18c063a0b4706b1c491cd90e72797dffbd Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 13:14:30 +0900 Subject: [PATCH 2/9] systemctl/halt: drop support for calling in SysV init script Traditionally, halt is called at the end of the init script on reboot/shutdown. To support such usecase, previously we read the current runlevel from utmp and set force flag on reboot/shutdown. This drops the support for such the usecase. Note, neither supported nor tested, but hopefully still the command can be used in the end of the sysv init script by specifying -ff. --- src/systemctl/systemctl-compat-halt.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c index 0b05fe6702..c360a8d007 100644 --- a/src/systemctl/systemctl-compat-halt.c +++ b/src/systemctl/systemctl-compat-halt.c @@ -77,16 +77,11 @@ int halt_parse_argv(int argc, char *argv[]) { {} }; - int c, r, runlevel; + int c, r; assert(argc >= 0); assert(argv); - /* called in sysvinit system as last command in shutdown/reboot so this is always forceful */ - if (utmp_get_runlevel(&runlevel, NULL) >= 0) - if (IN_SET(runlevel, '0', '6')) - arg_force = 2; - while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) switch (c) { From af925f7eb349d33989164c639bca866286685570 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 05:48:53 +0900 Subject: [PATCH 3/9] systemctl: kill SysV compat 'runlevel' command --- docs/FAQ.md | 2 - man/rules/meson.build | 1 - man/runlevel.xml | 164 ---------------------- src/systemctl/meson.build | 3 +- src/systemctl/systemctl-compat-runlevel.c | 81 ----------- src/systemctl/systemctl-compat-runlevel.h | 6 - src/systemctl/systemctl-main.c | 5 - src/systemctl/systemctl.c | 5 - src/systemctl/systemctl.h | 1 - tools/command_ignorelist | 4 - 10 files changed, 1 insertion(+), 271 deletions(-) delete mode 100644 man/runlevel.xml delete mode 100644 src/systemctl/systemctl-compat-runlevel.c delete mode 100644 src/systemctl/systemctl-compat-runlevel.h diff --git a/docs/FAQ.md b/docs/FAQ.md index 02be27fcaf..6f2db49caa 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -47,8 +47,6 @@ A: Note that there might be more than one target active at the same time. So the $ systemctl list-units --type=target ``` -If you are just interested in a single number, you can use the venerable _runlevel_ command, but again, its output might be misleading. - **Q: I want to change a service file, but rpm keeps overwriting it in /usr/lib/systemd/system all the time, how should I handle this?** A: The recommended way is to copy the service file from /usr/lib/systemd/system to /etc/systemd/system and edit it there. The latter directory takes precedence over the former, and rpm will never overwrite it. If you want to use the distributed service file again you can simply delete (or rename) the service file in /etc/systemd/system again. diff --git a/man/rules/meson.build b/man/rules/meson.build index 12e9fd97aa..38d608db48 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -79,7 +79,6 @@ manpages = [ ['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'], ['resolved.conf', '5', ['resolved.conf.d'], 'ENABLE_RESOLVE'], ['run0', '1', [], ''], - ['runlevel', '8', [], 'HAVE_SYSV_COMPAT'], ['sd-bus-errors', '3', ['SD_BUS_ERROR_ACCESS_DENIED', diff --git a/man/runlevel.xml b/man/runlevel.xml deleted file mode 100644 index 7b7e24ba6c..0000000000 --- a/man/runlevel.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - runlevel - systemd - - - - runlevel - 8 - - - - runlevel - Print previous and current SysV runlevel - - - - - runlevel - options - - - - - Overview - - "Runlevels" are an obsolete way to start and stop groups of - services used in SysV init. systemd provides a compatibility layer - that maps runlevels to targets, and associated binaries like - runlevel. Nevertheless, only one runlevel can - be "active" at a given time, while systemd can activate multiple - targets concurrently, so the mapping to runlevels is confusing - and only approximate. Runlevels should not be used in new code, - and are mostly useful as a shorthand way to refer the matching - systemd targets in kernel boot parameters. - - - Mapping between runlevels and systemd targets - - - - - - Runlevel - Target - - - - - 0 - poweroff.target - - - 1 - rescue.target - - - 2, 3, 4 - multi-user.target - - - 5 - graphical.target - - - 6 - reboot.target - - - -
-
- - - Description - - runlevel prints the previous and current - SysV runlevel if they are known. - - The two runlevel characters are separated by a single space - character. If a runlevel cannot be determined, N is printed - instead. If neither can be determined, the word "unknown" is - printed. - - Unless overridden in the environment, this will check the - utmp database for recent runlevel changes. - - - - Options - - The following option is understood: - - - - - - - - - - - - - Exit status - - If one or both runlevels could be determined, 0 is returned, - a non-zero failure code otherwise. - - - - - Environment - - - - $RUNLEVEL - - If $RUNLEVEL is set, - runlevel will print this value as current - runlevel and ignore utmp. - - - - $PREVLEVEL - - If $PREVLEVEL is set, - runlevel will print this value as previous - runlevel and ignore utmp. - - - - - - Files - - - - /run/utmp - - The utmp database runlevel reads the previous and current runlevel - from. - - - - - - - - See Also - - systemd1 - systemd.target5 - systemctl1 - - -
diff --git a/src/systemctl/meson.build b/src/systemctl/meson.build index 4cff1f31de..ca83fc8937 100644 --- a/src/systemctl/meson.build +++ b/src/systemctl/meson.build @@ -32,7 +32,6 @@ systemctl_sources = files( ) systemctl_extract_sources = files( 'systemctl-compat-halt.c', - 'systemctl-compat-runlevel.c', 'systemctl-compat-shutdown.c', 'systemctl-compat-telinit.c', 'systemctl-daemon-reload.c', @@ -75,7 +74,7 @@ executables += [ ] foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] + - (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : [])) + (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['telinit'] : [])) install_symlink(alias, pointing_to : sbin_to_bin + 'systemctl', install_dir : sbindir) diff --git a/src/systemctl/systemctl-compat-runlevel.c b/src/systemctl/systemctl-compat-runlevel.c deleted file mode 100644 index e31bfd408b..0000000000 --- a/src/systemctl/systemctl-compat-runlevel.c +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include - -#include "alloc-util.h" -#include "log.h" -#include "pretty-print.h" -#include "systemctl-compat-runlevel.h" -#include "utmp-wtmp.h" - -static int runlevel_help(void) { - _cleanup_free_ char *link = NULL; - int r; - - r = terminal_urlify_man("runlevel", "8", &link); - if (r < 0) - return log_oom(); - - printf("%s [OPTIONS...]\n" - "\n%sPrints the previous and current runlevel of the init system.%s\n" - "\nOptions:\n" - " --help Show this help\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - link); - - return 0; -} - -int runlevel_parse_argv(int argc, char *argv[]) { - enum { - ARG_HELP = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - return runlevel_help(); - - case '?': - return -EINVAL; - - default: - assert_not_reached(); - } - - if (optind < argc) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Too many arguments."); - - return 1; -} - -int runlevel_main(void) { - int r, runlevel, previous; - - r = utmp_get_runlevel(&runlevel, &previous); - if (r < 0) { - puts("unknown"); - return r; - } - - printf("%c %c\n", - previous <= 0 ? 'N' : previous, - runlevel <= 0 ? 'N' : runlevel); - - return 0; -} diff --git a/src/systemctl/systemctl-compat-runlevel.h b/src/systemctl/systemctl-compat-runlevel.h deleted file mode 100644 index 658524bf1e..0000000000 --- a/src/systemctl/systemctl-compat-runlevel.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -int runlevel_parse_argv(int argc, char *argv[]); - -int runlevel_main(void); diff --git a/src/systemctl/systemctl-main.c b/src/systemctl/systemctl-main.c index cde07ab8cb..2e2dcd3e55 100644 --- a/src/systemctl/systemctl-main.c +++ b/src/systemctl/systemctl-main.c @@ -16,7 +16,6 @@ #include "systemctl-cancel-job.h" #include "systemctl-clean-or-freeze.h" #include "systemctl-compat-halt.h" -#include "systemctl-compat-runlevel.h" #include "systemctl-compat-telinit.h" #include "systemctl-daemon-reload.h" #include "systemctl-edit.h" @@ -236,10 +235,6 @@ static int run(int argc, char *argv[]) { r = logind_show_shutdown(); break; - case ACTION_RUNLEVEL: - r = runlevel_main(); - break; - case ACTION_EXIT: case ACTION_SLEEP: case ACTION_SUSPEND: diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 47a8867630..439e11699f 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -24,7 +24,6 @@ #include "strv.h" #include "systemctl.h" #include "systemctl-compat-halt.h" -#include "systemctl-compat-runlevel.h" #include "systemctl-compat-shutdown.h" #include "systemctl-compat-telinit.h" #include "systemctl-logind.h" @@ -1142,10 +1141,6 @@ int systemctl_dispatch_parse_argv(int argc, char *argv[]) { arg_action = _ACTION_INVALID; /* telinit_parse_argv() will figure out the actual action we'll execute */ return telinit_parse_argv(argc, argv); - - } else if (invoked_as(argv, "runlevel")) { - arg_action = ACTION_RUNLEVEL; - return runlevel_parse_argv(argc, argv); } arg_action = ACTION_SYSTEMCTL; diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index af30d2a6f5..91702d1252 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -25,7 +25,6 @@ enum action { ACTION_DEFAULT, ACTION_RELOAD, ACTION_REEXEC, - ACTION_RUNLEVEL, ACTION_CANCEL_SHUTDOWN, ACTION_SHOW_SHUTDOWN, ACTION_SYSTEMCTL_SHOW_SHUTDOWN, diff --git a/tools/command_ignorelist b/tools/command_ignorelist index 4a48d4e614..6639fb08bd 100644 --- a/tools/command_ignorelist +++ b/tools/command_ignorelist @@ -63,10 +63,6 @@ os-release.xml ./refsect1[title="Options"]/refsect2[title="Presentation informat pam_systemd.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="debug="] pam_systemd.xml ./refsect1[title="Environment"]/variablelist[1]/varlistentry[term="$XDG_SESSION_ID"] pam_systemd.xml ./refsect1[title="Environment"]/variablelist[1]/varlistentry[term="$XDG_RUNTIME_DIR"] -runlevel.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--help"] -runlevel.xml ./refsect1[title="Environment"]/variablelist/varlistentry[term="$RUNLEVEL"] -runlevel.xml ./refsect1[title="Environment"]/variablelist/varlistentry[term="$PREVLEVEL"] -runlevel.xml ./refsect1[title="Files"]/variablelist/varlistentry[term="/run/utmp"] shutdown.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--help"] shutdown.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="-H"] shutdown.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="-P"] From 8ba48d4bf8b021369b535307665a8576c460a3ff Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 04:17:45 +0900 Subject: [PATCH 4/9] core,initctl,systemctl: kill /dev/initctl support This also kills support for controlling system state through /sbin/init, initctl, and telinit. --- LICENSES/README.md | 4 +- man/rules/meson.build | 5 - man/systemd-initctl.service.xml | 49 ---- man/telinit.xml | 151 ---------- meson.build | 1 - src/core/main.c | 20 -- src/initctl/initctl.c | 351 ----------------------- src/initctl/meson.build | 9 - src/shared/initreq.h | 59 ---- src/systemctl/meson.build | 4 +- src/systemctl/systemctl-compat-halt.c | 3 +- src/systemctl/systemctl-compat-telinit.c | 214 -------------- src/systemctl/systemctl-compat-telinit.h | 6 - src/systemctl/systemctl-main.c | 17 +- src/systemctl/systemctl-start-unit.c | 4 - src/systemctl/systemctl.c | 19 -- src/systemctl/systemctl.h | 4 - tools/command_ignorelist | 9 - units/meson.build | 9 - units/systemd-initctl.service.in | 19 -- units/systemd-initctl.socket | 19 -- 21 files changed, 6 insertions(+), 970 deletions(-) delete mode 100644 man/systemd-initctl.service.xml delete mode 100644 man/telinit.xml delete mode 100644 src/initctl/initctl.c delete mode 100644 src/initctl/meson.build delete mode 100644 src/shared/initreq.h delete mode 100644 src/systemctl/systemctl-compat-telinit.c delete mode 100644 src/systemctl/systemctl-compat-telinit.h delete mode 100644 units/systemd-initctl.service.in delete mode 100644 units/systemd-initctl.socket diff --git a/LICENSES/README.md b/LICENSES/README.md index 1df87508fb..55203e9825 100644 --- a/LICENSES/README.md +++ b/LICENSES/README.md @@ -38,9 +38,7 @@ The following exceptions apply: verbatim from the Linux kernel source tree and are licensed under **GPL-2.0 WITH Linux-syscall-note** and are used within the scope of the Linux-syscall-note exception provisions - * the following sources are licensed under the **LGPL-2.0-or-later** license: - - src/basic/utf8.c - - src/shared/initreq.h + * the src/basic/utf8.c source is licensed under the **LGPL-2.0-or-later** license. * the src/basic/include/linux/bpf_insn.h header is copied from the Linux kernel source tree and is licensed under either **BSD-2-Clause** or **GPL-2.0-only**, and thus is included in the systemd build under the BSD-2-Clause license. diff --git a/man/rules/meson.build b/man/rules/meson.build index 38d608db48..246a059fa5 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -997,10 +997,6 @@ manpages = [ ['systemd-import-generator', '8', [], ''], ['systemd-importd.service', '8', ['systemd-importd'], 'ENABLE_IMPORTD'], ['systemd-inhibit', '1', [], ''], - ['systemd-initctl.service', - '8', - ['systemd-initctl', 'systemd-initctl.socket'], - 'HAVE_SYSV_COMPAT'], ['systemd-integritysetup-generator', '8', [], 'HAVE_LIBCRYPTSETUP'], ['systemd-integritysetup@.service', '8', @@ -1248,7 +1244,6 @@ manpages = [ ['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'], ['sysupdate.features', '5', [], 'ENABLE_SYSUPDATE'], ['sysusers.d', '5', [], 'ENABLE_SYSUSERS'], - ['telinit', '8', [], 'HAVE_SYSV_COMPAT'], ['timedatectl', '1', [], 'ENABLE_TIMEDATECTL'], ['timesyncd.conf', '5', ['timesyncd.conf.d'], 'ENABLE_TIMESYNCD'], ['tmpfiles.d', '5', [], ''], diff --git a/man/systemd-initctl.service.xml b/man/systemd-initctl.service.xml deleted file mode 100644 index ad0d49de48..0000000000 --- a/man/systemd-initctl.service.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - systemd-initctl.service - systemd - - - - systemd-initctl.service - 8 - - - - systemd-initctl.service - systemd-initctl.socket - systemd-initctl - /dev/initctl compatibility - - - - systemd-initctl.service - systemd-initctl.socket - /usr/lib/systemd/systemd-initctl - - - - Description - - systemd-initctl is a system service - that implements compatibility with the - /dev/initctl FIFO file system object, as - implemented by the SysV init system. - systemd-initctl is automatically activated on - request and terminates itself when it is unused. - - - - See Also - - systemd1 - - - - diff --git a/man/telinit.xml b/man/telinit.xml deleted file mode 100644 index 7ffd8cd9b7..0000000000 --- a/man/telinit.xml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - telinit - systemd - - - - telinit - 8 - - - - telinit - Change SysV runlevel - - - - - telinit OPTIONS COMMAND - - - - - Description - - telinit may be used to change the SysV - system runlevel. Since the concept of SysV runlevels is obsolete - the runlevel requests will be transparently translated into - systemd unit activation requests. - - - - - Options - - The following options are understood: - - - - - - - - - - - - Do not send wall message before - reboot/halt/power-off. - - - - The following commands are understood: - - - - 0 - - Power-off the machine. This is translated into - an activation request for poweroff.target - and is equivalent to systemctl - poweroff. - - - - 6 - - Reboot the machine. This is translated into an - activation request for reboot.target and - is equivalent to systemctl - reboot. - - - - 2 - 3 - 4 - 5 - - Change the SysV runlevel. This is translated - into an activation request for - runlevel2.target, - runlevel3.target, … and is equivalent - to systemctl isolate runlevel2.target, - systemctl isolate runlevel3.target, - … - - - - 1 - s - S - - Change into system rescue mode. This is - translated into an activation request for - rescue.target and is equivalent to - systemctl rescue. - - - - q - Q - - Reload daemon configuration. This is - equivalent to systemctl - daemon-reload. - - - - u - U - - Serialize state, reexecute daemon and - deserialize state again. This is equivalent to - systemctl daemon-reexec. - - - - - - - Exit status - - On success, 0 is returned, a non-zero failure - code otherwise. - - - - Notes - - This is a legacy command available for compatibility only. - It should not be used anymore, as the concept of runlevels is - obsolete. - - - - See Also - - systemd1 - systemctl1 - wall1 - - - diff --git a/meson.build b/meson.build index f5362ece8a..ac0ac59132 100644 --- a/meson.build +++ b/meson.build @@ -2319,7 +2319,6 @@ subdir('src/hostname') subdir('src/hwdb') subdir('src/id128') subdir('src/import') -subdir('src/initctl') subdir('src/integritysetup') subdir('src/journal') subdir('src/journal-remote') diff --git a/src/core/main.c b/src/core/main.c index 668b103d6f..6c44b4e00f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1591,23 +1591,6 @@ static int fixup_environment(void) { return 0; } -static void redirect_telinit(int argc, char *argv[]) { - - /* This is compatibility support for SysV, where calling init as a user is identical to telinit. */ - -#if HAVE_SYSV_COMPAT - if (getpid_cached() == 1) - return; - - if (!invoked_as(argv, "init")) - return; - - execv(SYSTEMCTL_BINARY_PATH, argv); - log_error_errno(errno, "Failed to execute %s: %m", SYSTEMCTL_BINARY_PATH); - exit(EXIT_FAILURE); -#endif -} - static int become_shutdown(int objective, int retval) { static const char* const table[_MANAGER_OBJECTIVE_MAX] = { [MANAGER_EXIT] = "exit", @@ -3041,9 +3024,6 @@ int main(int argc, char *argv[]) { assert_se(argc > 0 && !isempty(argv[0])); - /* SysV compatibility: redirect init → telinit */ - redirect_telinit(argc, argv); - /* Take timestamps early on */ dual_timestamp_from_monotonic(&kernel_timestamp, 0); dual_timestamp_now(&userspace_timestamp); diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c deleted file mode 100644 index a387fb0d28..0000000000 --- a/src/initctl/initctl.c +++ /dev/null @@ -1,351 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include -#include -#include -#include -#include - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-error.h" -#include "bus-locator.h" -#include "bus-util.h" -#include "constants.h" -#include "daemon-util.h" -#include "fd-util.h" -#include "initreq.h" -#include "list.h" -#include "log.h" -#include "main-func.h" -#include "reboot-util.h" -#include "special.h" -#include "time-util.h" - -#define SERVER_FD_MAX 16 -#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) - -typedef struct Fifo Fifo; - -typedef struct Server { - int epoll_fd; - - LIST_HEAD(Fifo, fifos); - unsigned n_fifos; - - sd_bus *bus; - - bool quit; -} Server; - -struct Fifo { - Server *server; - - int fd; - - struct init_request buffer; - size_t bytes_read; - - LIST_FIELDS(Fifo, fifo); -}; - -static const char *translate_runlevel(int runlevel, bool *isolate) { - static const struct { - const int runlevel; - const char *special; - bool isolate; - } table[] = { - { '0', SPECIAL_POWEROFF_TARGET, false }, - { '1', SPECIAL_RESCUE_TARGET, true }, - { 's', SPECIAL_RESCUE_TARGET, true }, - { 'S', SPECIAL_RESCUE_TARGET, true }, - { '2', SPECIAL_MULTI_USER_TARGET, true }, - { '3', SPECIAL_MULTI_USER_TARGET, true }, - { '4', SPECIAL_MULTI_USER_TARGET, true }, - { '5', SPECIAL_GRAPHICAL_TARGET, true }, - { '6', SPECIAL_REBOOT_TARGET, false }, - }; - - assert(isolate); - - FOREACH_ELEMENT(i, table) - if (i->runlevel == runlevel) { - *isolate = i->isolate; - if (runlevel == '6' && kexec_loaded()) - return SPECIAL_KEXEC_TARGET; - return i->special; - } - - return NULL; -} - -static int change_runlevel(Server *s, int runlevel) { - const char *target; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *mode; - bool isolate = false; - int r; - - assert(s); - - target = translate_runlevel(runlevel, &isolate); - if (!target) { - log_warning("Got request for unknown runlevel %c, ignoring.", runlevel); - return 0; - } - - if (isolate) - mode = "isolate"; - else - mode = "replace-irreversibly"; - - log_debug("Requesting %s/start/%s", target, mode); - - r = bus_call_method(s->bus, bus_systemd_mgr, "StartUnit", &error, NULL, "ss", target, mode); - if (r < 0) - return log_error_errno(r, "Failed to change runlevel: %s", bus_error_message(&error, r)); - - return 0; -} - -static void request_process(Server *s, const struct init_request *req) { - assert(s); - assert(req); - - if (req->magic != INIT_MAGIC) { - log_error("Got initctl request with invalid magic. Ignoring."); - return; - } - - switch (req->cmd) { - - case INIT_CMD_RUNLVL: - if (!isprint(req->runlevel)) - log_error("Got invalid runlevel. Ignoring."); - else - switch (req->runlevel) { - - /* we are async anyway, so just use kill for reexec/reload */ - case 'u': - case 'U': - if (kill(1, SIGTERM) < 0) - log_error_errno(errno, "kill() failed: %m"); - - /* The bus connection will be - * terminated if PID 1 is reexecuted, - * hence let's just exit here, and - * rely on that we'll be restarted on - * the next request */ - s->quit = true; - break; - - case 'q': - case 'Q': - if (kill(1, SIGHUP) < 0) - log_error_errno(errno, "kill() failed: %m"); - break; - - default: - (void) change_runlevel(s, req->runlevel); - } - return; - - case INIT_CMD_POWERFAIL: - case INIT_CMD_POWERFAILNOW: - case INIT_CMD_POWEROK: - log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!"); - return; - - case INIT_CMD_CHANGECONS: - log_warning("Received console change initctl request. This is not implemented in systemd."); - return; - - case INIT_CMD_SETENV: - case INIT_CMD_UNSETENV: - log_warning("Received environment initctl request. This is not implemented in systemd."); - return; - - default: - log_warning("Received unknown initctl request. Ignoring."); - return; - } -} - -static int fifo_process(Fifo *f) { - ssize_t l; - - assert(f); - - errno = EIO; - l = read(f->fd, - ((uint8_t*) &f->buffer) + f->bytes_read, - sizeof(f->buffer) - f->bytes_read); - if (l <= 0) { - if (errno == EAGAIN) - return 0; - - return log_warning_errno(errno, "Failed to read from fifo: %m"); - } - - f->bytes_read += l; - assert(f->bytes_read <= sizeof(f->buffer)); - - if (f->bytes_read == sizeof(f->buffer)) { - request_process(f->server, &f->buffer); - f->bytes_read = 0; - } - - return 0; -} - -static Fifo* fifo_free(Fifo *f) { - if (!f) - return NULL; - - if (f->server) { - assert(f->server->n_fifos > 0); - f->server->n_fifos--; - LIST_REMOVE(fifo, f->server->fifos, f); - } - - if (f->fd >= 0) { - if (f->server) - (void) epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL); - - safe_close(f->fd); - } - - return mfree(f); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(Fifo*, fifo_free); - -static void server_done(Server *s) { - assert(s); - - while (s->fifos) - fifo_free(s->fifos); - - s->epoll_fd = safe_close(s->epoll_fd); - s->bus = sd_bus_flush_close_unref(s->bus); -} - -static int server_init(Server *s, unsigned n_sockets) { - int r; - - /* This function will leave s partially initialized on failure. Caller needs to clean up. */ - - assert(s); - assert(n_sockets > 0); - - s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (s->epoll_fd < 0) - return log_error_errno(errno, "Failed to create epoll object: %m"); - - for (unsigned i = 0; i < n_sockets; i++) { - _cleanup_(fifo_freep) Fifo *f = NULL; - int fd = SD_LISTEN_FDS_START + i; - - r = sd_is_fifo(fd, NULL); - if (r < 0) - return log_error_errno(r, "Failed to determine file descriptor type: %m"); - if (!r) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong file descriptor type."); - - f = new0(Fifo, 1); - if (!f) - return log_oom(); - - struct epoll_event ev = { - .events = EPOLLIN, - .data.ptr = f, - }; - - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) - return log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); - - f->fd = fd; - f->server = s; - LIST_PREPEND(fifo, s->fifos, TAKE_PTR(f)); - s->n_fifos++; - } - - r = bus_connect_system_systemd(&s->bus); - if (r < 0) - return log_error_errno(r, "Failed to get D-Bus connection: %m"); - - return 0; -} - -static int process_event(Server *s, struct epoll_event *ev) { - int r; - _cleanup_(fifo_freep) Fifo *f = NULL; - - assert(s); - assert(ev); - - if (!(ev->events & EPOLLIN)) - return log_info_errno(SYNTHETIC_ERRNO(EIO), - "Got invalid event from epoll. (3)"); - - f = (Fifo*) ev->data.ptr; - r = fifo_process(f); - if (r < 0) - return log_info_errno(r, "Got error on fifo: %m"); - - TAKE_PTR(f); - - return 0; -} - -static int run(int argc, char *argv[]) { - _cleanup_(server_done) Server server = { .epoll_fd = -EBADF }; - _unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL; - int r, n; - - if (argc > 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "This program does not take arguments."); - - log_setup(); - - umask(0022); - - n = sd_listen_fds(true); - if (n < 0) - return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); - - if (n <= 0 || n > SERVER_FD_MAX) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "No or too many file descriptors passed."); - - r = server_init(&server, (unsigned) n); - if (r < 0) - return r; - - notify_stop = notify_start(NOTIFY_READY_MESSAGE, NOTIFY_STOPPING_MESSAGE); - - while (!server.quit) { - struct epoll_event event; - int k; - - k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT_MSEC); - if (k < 0) { - if (errno == EINTR) - continue; - return log_error_errno(errno, "epoll_wait() failed: %m"); - } - if (k == 0) - break; - - r = process_event(&server, &event); - if (r < 0) - return r; - } - - return 0; -} - -DEFINE_MAIN_FUNCTION(run); diff --git a/src/initctl/meson.build b/src/initctl/meson.build deleted file mode 100644 index c9fddc9903..0000000000 --- a/src/initctl/meson.build +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -executables += [ - libexec_template + { - 'name' : 'systemd-initctl', - 'conditions' : ['HAVE_SYSV_COMPAT'], - 'sources' : files('initctl.c'), - }, -] diff --git a/src/shared/initreq.h b/src/shared/initreq.h deleted file mode 100644 index 8d2e08c6c9..0000000000 --- a/src/shared/initreq.h +++ /dev/null @@ -1,59 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.0-or-later */ -/* - * Copyright (C) 1995-2004 Miquel van Smoorenburg - * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS - */ - -#pragma once - -#include "forward.h" - -#if defined(__FreeBSD_kernel__) -# define INIT_FIFO "/etc/.initctl" -#else -# define INIT_FIFO "/dev/initctl" -#endif - -#define INIT_MAGIC 0x03091969 -#define INIT_CMD_START 0 -#define INIT_CMD_RUNLVL 1 -#define INIT_CMD_POWERFAIL 2 -#define INIT_CMD_POWERFAILNOW 3 -#define INIT_CMD_POWEROK 4 -#define INIT_CMD_BSD 5 -#define INIT_CMD_SETENV 6 -#define INIT_CMD_UNSETENV 7 - -#define INIT_CMD_CHANGECONS 12345 -/* - * This is what BSD 4.4 uses when talking to init. - * Linux doesn't use this right now. - */ -struct init_request_bsd { - char gen_id[8]; /* Beats me.. telnetd uses "fe" */ - char tty_id[16]; /* Tty name minus /dev/tty */ - char host[HOST_NAME_MAX]; /* Hostname */ - char term_type[16]; /* Terminal type */ - int signal; /* Signal to send */ - int pid; /* Process to send to */ - char exec_name[128]; /* Program to execute */ - char reserved[128]; /* For future expansion. */ -}; - -/* - * Because of legacy interfaces, "runlevel" and "sleeptime" - * aren't in a separate struct in the union. - * - * The weird sizes are because init expects the whole - * struct to be 384 bytes. - */ -struct init_request { - int magic; /* Magic number */ - int cmd; /* What kind of request */ - int runlevel; /* Runlevel to change to */ - int sleeptime; /* Time between TERM and KILL */ - union { - struct init_request_bsd bsd; - char data[368]; - } i; -}; diff --git a/src/systemctl/meson.build b/src/systemctl/meson.build index ca83fc8937..2e207966da 100644 --- a/src/systemctl/meson.build +++ b/src/systemctl/meson.build @@ -33,7 +33,6 @@ systemctl_sources = files( systemctl_extract_sources = files( 'systemctl-compat-halt.c', 'systemctl-compat-shutdown.c', - 'systemctl-compat-telinit.c', 'systemctl-daemon-reload.c', 'systemctl-logind.c', 'systemctl-start-unit.c', @@ -73,8 +72,7 @@ executables += [ }, ] -foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] + - (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['telinit'] : [])) +foreach alias : ['halt', 'poweroff', 'reboot', 'shutdown'] install_symlink(alias, pointing_to : sbin_to_bin + 'systemctl', install_dir : sbindir) diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c index c360a8d007..614216fd08 100644 --- a/src/systemctl/systemctl-compat-halt.c +++ b/src/systemctl/systemctl-compat-halt.c @@ -11,7 +11,6 @@ #include "reboot-util.h" #include "systemctl.h" #include "systemctl-compat-halt.h" -#include "systemctl-compat-telinit.h" #include "systemctl-logind.h" #include "systemctl-start-unit.h" #include "systemctl-util.h" @@ -171,7 +170,7 @@ int halt_main(void) { arg_no_block = true; if (!arg_dry_run) - return start_with_fallback(); + return verb_start(0, NULL, NULL); } r = must_be_root(); diff --git a/src/systemctl/systemctl-compat-telinit.c b/src/systemctl/systemctl-compat-telinit.c deleted file mode 100644 index c8794ef255..0000000000 --- a/src/systemctl/systemctl-compat-telinit.c +++ /dev/null @@ -1,214 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include - -#include "alloc-util.h" -#include "fd-util.h" -#include "initreq.h" -#include "io-util.h" -#include "log.h" -#include "pretty-print.h" -#include "strv.h" -#include "systemctl.h" -#include "systemctl-compat-telinit.h" -#include "systemctl-daemon-reload.h" -#include "systemctl-start-unit.h" - -static int telinit_help(void) { - _cleanup_free_ char *link = NULL; - int r; - - r = terminal_urlify_man("telinit", "8", &link); - if (r < 0) - return log_oom(); - - printf("%s [OPTIONS...] COMMAND\n\n" - "%sSend control commands to the init daemon.%s\n" - "\nCommands:\n" - " 0 Power-off the machine\n" - " 6 Reboot the machine\n" - " 2, 3, 4, 5 Start runlevelX.target unit\n" - " 1, s, S Enter rescue mode\n" - " q, Q Reload init daemon configuration\n" - " u, U Reexecute init daemon\n" - "\nOptions:\n" - " --help Show this help\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n" - "\nSee the %s for details.\n", - program_invocation_short_name, - ansi_highlight(), - ansi_normal(), - link); - - return 0; -} - -int telinit_parse_argv(int argc, char *argv[]) { - enum { - ARG_HELP = 0x100, - ARG_NO_WALL - }; - - static const struct option options[] = { - { "help", no_argument, NULL, ARG_HELP }, - { "no-wall", no_argument, NULL, ARG_NO_WALL }, - {} - }; - - static const struct { - char from; - enum action to; - } table[] = { - { '0', ACTION_POWEROFF }, - { '6', ACTION_REBOOT }, - { '1', ACTION_RESCUE }, - { '2', ACTION_RUNLEVEL2 }, - { '3', ACTION_RUNLEVEL3 }, - { '4', ACTION_RUNLEVEL4 }, - { '5', ACTION_RUNLEVEL5 }, - { 's', ACTION_RESCUE }, - { 'S', ACTION_RESCUE }, - { 'q', ACTION_RELOAD }, - { 'Q', ACTION_RELOAD }, - { 'u', ACTION_REEXEC }, - { 'U', ACTION_REEXEC } - }; - - unsigned i; - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) - switch (c) { - - case ARG_HELP: - return telinit_help(); - - case ARG_NO_WALL: - arg_no_wall = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); - } - - if (optind >= argc) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: required argument missing.", - program_invocation_short_name); - - if (optind + 1 < argc) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Too many arguments."); - - if (strlen(argv[optind]) != 1) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Expected single character argument."); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (table[i].from == argv[optind][0]) - break; - - if (i >= ELEMENTSOF(table)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Unknown command '%s'.", argv[optind]); - - arg_action = table[i].to; - - optind++; - - return 1; -} - -#if HAVE_SYSV_COMPAT -static int talk_initctl(char rl) { - _cleanup_close_ int fd = -EBADF; - const char *path; - int r; - - /* Try to switch to the specified SysV runlevel. Returns == 0 if the operation does not apply on this - * system, and > 0 on success. */ - - if (rl == 0) - return 0; - - FOREACH_STRING(_path, "/run/initctl", "/dev/initctl") { - path = _path; - - fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); - if (fd < 0 && errno != ENOENT) - return log_error_errno(errno, "Failed to open %s: %m", path); - if (fd >= 0) - break; - } - if (fd < 0) - return 0; - - struct init_request request = { - .magic = INIT_MAGIC, - .sleeptime = 0, - .cmd = INIT_CMD_RUNLVL, - .runlevel = rl, - }; - - r = loop_write(fd, &request, sizeof(request)); - if (r < 0) - return log_error_errno(r, "Failed to write to %s: %m", path); - - return 1; -} - -static int action_to_runlevel(void) { - static const char table[_ACTION_MAX] = { - [ACTION_HALT] = '0', - [ACTION_POWEROFF] = '0', - [ACTION_REBOOT] = '6', - [ACTION_RUNLEVEL2] = '2', - [ACTION_RUNLEVEL3] = '3', - [ACTION_RUNLEVEL4] = '4', - [ACTION_RUNLEVEL5] = '5', - [ACTION_RESCUE] = '1' - }; - - assert(arg_action >= 0 && arg_action < _ACTION_MAX); - return table[arg_action]; -} -#endif - -int start_with_fallback(void) { - int r; - - /* First, try systemd via D-Bus. */ - r = verb_start(0, NULL, NULL); - if (r == 0) - return 0; - -#if HAVE_SYSV_COMPAT - /* Nothing else worked, so let's try /dev/initctl */ - if (talk_initctl(action_to_runlevel()) > 0) - return 0; -#endif - - return log_error_errno(r, "Failed to talk to init daemon: %m"); -} - -int reload_with_fallback(void) { - - assert(IN_SET(arg_action, ACTION_RELOAD, ACTION_REEXEC)); - - /* First, try systemd via D-Bus */ - if (daemon_reload(arg_action, /* graceful= */ true) > 0) - return 0; - - /* That didn't work, so let's try signals */ - if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) - return log_error_errno(errno, "kill() failed: %m"); - - return 0; -} diff --git a/src/systemctl/systemctl-compat-telinit.h b/src/systemctl/systemctl-compat-telinit.h deleted file mode 100644 index 1a2bcd4405..0000000000 --- a/src/systemctl/systemctl-compat-telinit.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -int telinit_parse_argv(int argc, char *argv[]); -int start_with_fallback(void); -int reload_with_fallback(void); diff --git a/src/systemctl/systemctl-main.c b/src/systemctl/systemctl-main.c index 2e2dcd3e55..c0596cfb86 100644 --- a/src/systemctl/systemctl-main.c +++ b/src/systemctl/systemctl-main.c @@ -16,7 +16,6 @@ #include "systemctl-cancel-job.h" #include "systemctl-clean-or-freeze.h" #include "systemctl-compat-halt.h" -#include "systemctl-compat-telinit.h" #include "systemctl-daemon-reload.h" #include "systemctl-edit.h" #include "systemctl-enable.h" @@ -213,19 +212,6 @@ static int run(int argc, char *argv[]) { r = halt_main(); break; - case ACTION_RUNLEVEL2: - case ACTION_RUNLEVEL3: - case ACTION_RUNLEVEL4: - case ACTION_RUNLEVEL5: - case ACTION_RESCUE: - r = start_with_fallback(); - break; - - case ACTION_RELOAD: - case ACTION_REEXEC: - r = reload_with_fallback(); - break; - case ACTION_CANCEL_SHUTDOWN: r = logind_cancel_shutdown(); break; @@ -235,6 +221,9 @@ static int run(int argc, char *argv[]) { r = logind_show_shutdown(); break; + case ACTION_RESCUE: + case ACTION_RELOAD: + case ACTION_REEXEC: case ACTION_EXIT: case ACTION_SLEEP: case ACTION_SUSPEND: diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index 7ff5548ba8..aa0882f757 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -232,10 +232,6 @@ const struct action_metadata action_table[_ACTION_MAX] = { [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, [ACTION_SOFT_REBOOT] = { SPECIAL_SOFT_REBOOT_TARGET, "soft-reboot", "replace-irreversibly" }, - [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 439e11699f..2eaf62a410 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -25,7 +25,6 @@ #include "systemctl.h" #include "systemctl-compat-halt.h" #include "systemctl-compat-shutdown.h" -#include "systemctl-compat-telinit.h" #include "systemctl-logind.h" #include "time-util.h" @@ -1123,24 +1122,6 @@ int systemctl_dispatch_parse_argv(int argc, char *argv[]) { } else if (invoked_as(argv, "shutdown")) { arg_action = ACTION_POWEROFF; return shutdown_parse_argv(argc, argv); - - } else if (invoked_as(argv, "init")) { - - /* Matches invocations as "init" as well as "telinit", which are synonymous when run - * as PID != 1 on SysV. - * - * On SysV "telinit" was the official command to communicate with PID 1, but "init" would - * redirect itself to "telinit" if called with PID != 1. We follow the same logic here still, - * though we add one level of indirection, as we implement "telinit" in "systemctl". Hence, - * for us if you invoke "init" you get "systemd", but it will execve() "systemctl" - * immediately with argv[] unmodified if PID is != 1. If you invoke "telinit" you directly - * get "systemctl". In both cases we shall do the same thing, which is why we do - * invoked_as(argv, "init") here, as a quick way to match both. - * - * Also see redirect_telinit() in src/core/main.c. */ - - arg_action = _ACTION_INVALID; /* telinit_parse_argv() will figure out the actual action we'll execute */ - return telinit_parse_argv(argc, argv); } arg_action = ACTION_SYSTEMCTL; diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index 91702d1252..5cbdfea48c 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -16,10 +16,6 @@ enum action { ACTION_HIBERNATE, ACTION_HYBRID_SLEEP, ACTION_SUSPEND_THEN_HIBERNATE, - ACTION_RUNLEVEL2, - ACTION_RUNLEVEL3, - ACTION_RUNLEVEL4, - ACTION_RUNLEVEL5, ACTION_RESCUE, ACTION_EMERGENCY, ACTION_DEFAULT, diff --git a/tools/command_ignorelist b/tools/command_ignorelist index 6639fb08bd..72544ae0dd 100644 --- a/tools/command_ignorelist +++ b/tools/command_ignorelist @@ -400,15 +400,6 @@ systemd.xml ./refsect1[title="Kernel Command Line"]/variablelist/varlistentry[te systemd.xml ./refsect1[title="Kernel Command Line"]/variablelist/varlistentry[term="systemd.setenv="] systemd.xml ./refsect1[title="Files"]/variablelist/varlistentry[term="/run/systemd/notify"] systemd.xml ./refsect1[title="Files"]/variablelist/varlistentry[term="/run/systemd/private"] -systemd.xml ./refsect1[title="Files"]/variablelist/varlistentry[term="/dev/initctl"] -telinit.xml ./refsect1[title="Options"]/variablelist[1]/varlistentry[term="--help"] -telinit.xml ./refsect1[title="Options"]/variablelist[1]/varlistentry[term="--no-wall"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="0"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="6"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="2"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="1"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="q"] -telinit.xml ./refsect1[title="Options"]/variablelist[2]/varlistentry[term="u"] tmpfiles.d.xml ./refsect1[title="Configuration File Format"]/refsect2[title="Type"]/variablelist/varlistentry[term="f"] tmpfiles.d.xml ./refsect1[title="Configuration File Format"]/refsect2[title="Type"]/variablelist/varlistentry[term="w"] tmpfiles.d.xml ./refsect1[title="Configuration File Format"]/refsect2[title="Type"]/variablelist/varlistentry[term="d"] diff --git a/units/meson.build b/units/meson.build index 3f3b047f3c..bc67a6ee8a 100644 --- a/units/meson.build +++ b/units/meson.build @@ -412,15 +412,6 @@ units = [ 'conditions' : ['ENABLE_IMPORTD'], 'symlinks' : ['sysinit.target.wants/'], }, - { - 'file' : 'systemd-initctl.service.in', - 'conditions' : ['HAVE_SYSV_COMPAT'], - }, - { - 'file' : 'systemd-initctl.socket', - 'conditions' : ['HAVE_SYSV_COMPAT'], - 'symlinks' : ['sockets.target.wants/'], - }, { 'file' : 'systemd-journal-catalog-update.service', 'symlinks' : ['sysinit.target.wants/'], diff --git a/units/systemd-initctl.service.in b/units/systemd-initctl.service.in deleted file mode 100644 index 6a19058186..0000000000 --- a/units/systemd-initctl.service.in +++ /dev/null @@ -1,19 +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=initctl Compatibility Daemon -Documentation=man:systemd-initctl.service(8) -DefaultDependencies=no - -[Service] -ExecStart={{LIBEXECDIR}}/systemd-initctl -NoNewPrivileges=yes -NotifyAccess=all -SystemCallArchitectures=native diff --git a/units/systemd-initctl.socket b/units/systemd-initctl.socket deleted file mode 100644 index 410213162d..0000000000 --- a/units/systemd-initctl.socket +++ /dev/null @@ -1,19 +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=initctl Compatibility Named Pipe -Documentation=man:systemd-initctl.socket(8) -DefaultDependencies=no -Before=sockets.target - -[Socket] -ListenFIFO=/run/initctl -Symlinks=/dev/initctl -SocketMode=0600 From dc1505555ba4058b0e17735f4e008540517e13ec Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 06:27:35 +0900 Subject: [PATCH 5/9] utmp: drop setting runlevel entry in utmp This removes systemd-update-utmp-runlevel.service and related command. --- TODO | 2 +- man/rules/meson.build | 5 +- man/systemd-update-utmp.service.xml | 13 +- src/shared/utmp-wtmp.c | 68 ---------- src/shared/utmp-wtmp.h | 9 -- src/update-utmp/update-utmp.c | 124 ------------------ test/units/TEST-01-BASIC.sh | 15 --- units/meson.build | 5 - units/systemd-update-utmp-runlevel.service.in | 25 ---- 9 files changed, 6 insertions(+), 260 deletions(-) delete mode 100644 units/systemd-update-utmp-runlevel.service.in diff --git a/TODO b/TODO index 7e23ddb2c8..916c3096aa 100644 --- a/TODO +++ b/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 diff --git a/man/rules/meson.build b/man/rules/meson.build index 246a059fa5..1fcf30e692 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -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'], diff --git a/man/systemd-update-utmp.service.xml b/man/systemd-update-utmp.service.xml index 2d7dee9d3a..a3044e9739 100644 --- a/man/systemd-update-utmp.service.xml +++ b/man/systemd-update-utmp.service.xml @@ -17,26 +17,21 @@ systemd-update-utmp.service - systemd-update-utmp-runlevel.service systemd-update-utmp - Write audit and utmp updates at bootup, runlevel - changes and shutdown + Write audit and utmp updates at bootup and shutdown systemd-update-utmp.service - systemd-update-utmp-runlevel.service /usr/lib/systemd/systemd-update-utmp Description - systemd-update-utmp-runlevel.service is - a service that writes SysV runlevel changes to utmp and wtmp, as - well as the audit logs, as they occur. - systemd-update-utmp.service does the same for - system reboots and shutdown requests. + + systemd-update-utmp.service is a service that writes system reboots and shutdown + requests to utmp and wtmp, as well as the audit logs. diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 8a69e9692b..6d150e7dcf 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -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); -} diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index f4b543e6c0..72e8ff375b 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -6,11 +6,8 @@ #if ENABLE_UTMP #include -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; } diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c index 78c693012e..4f96f3a377 100644 --- a/src/update-utmp/update-utmp.c +++ b/src/update-utmp/update-utmp.c @@ -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 }, {} }; diff --git a/test/units/TEST-01-BASIC.sh b/test/units/TEST-01-BASIC.sh index bb3ff2f1e9..780f37ee12 100755 --- a/test/units/TEST-01-BASIC.sh +++ b/test/units/TEST-01-BASIC.sh @@ -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 diff --git a/units/meson.build b/units/meson.build index bc67a6ee8a..34b3222f11 100644 --- a/units/meson.build +++ b/units/meson.build @@ -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'], diff --git a/units/systemd-update-utmp-runlevel.service.in b/units/systemd-update-utmp-runlevel.service.in deleted file mode 100644 index 17772d4576..0000000000 --- a/units/systemd-update-utmp-runlevel.service.in +++ /dev/null @@ -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 From e58ba80a40fb6e96543d56774a5bc5aa9cdadbf3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 09:47:04 +0900 Subject: [PATCH 6/9] units: drop runlevel[0-6].target --- docs/FAQ.md | 8 +------- man/systemd-sysv-generator.xml | 4 ---- man/systemd.special.xml | 27 --------------------------- man/systemd.target.xml | 5 +---- tools/command_ignorelist | 1 - units/meson.build | 27 +++++---------------------- 6 files changed, 7 insertions(+), 65 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 6f2db49caa..45ba2d0d74 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -11,13 +11,7 @@ Also check out the [Tips & Tricks](/TIPS_AND_TRICKS)! **Q: How do I change the current runlevel?** -A: In systemd runlevels are exposed via "target units". You can change them like this: - -```sh -# systemctl isolate runlevel5.target -``` - -Note however, that the concept of runlevels is a bit out of date, and it is usually nicer to use modern names for this. e.g.: +A: The concept of runlevels is obsolete. A set of target units are exposed that carry similar semantics, e.g. runlevel 5 -> `graphical.target`: ```sh # systemctl isolate graphical.target diff --git a/man/systemd-sysv-generator.xml b/man/systemd-sysv-generator.xml index e198c2b7a3..f5a9889e58 100644 --- a/man/systemd-sysv-generator.xml +++ b/man/systemd-sysv-generator.xml @@ -49,10 +49,6 @@ Note that compatibility is quite comprehensive but not 100%, for more details see Compatibility with SysV. - SysV runlevels have corresponding systemd targets - (runlevelX.target). The wrapper unit that is generated - will be wanted by those targets which correspond to runlevels for which the script is enabled. - systemd does not support SysV scripts as part of early boot, so all wrapper units are ordered after basic.target. diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 3e5e90cae8..d5420e9c2e 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -73,10 +73,6 @@ remote-fs.target, rescue.target, rpcbind.target, - runlevel2.target, - runlevel3.target, - runlevel4.target, - runlevel5.target, shutdown.target, sigpwr.target, sleep.target, @@ -627,9 +623,6 @@ systemd-logind8's org.freedesktop.login1.Manager.PowerOff D-Bus method directly. - - runlevel0.target is an alias for - this target unit, for compatibility with SysV. @@ -646,9 +639,6 @@ See systemd-reboot.service8 for further details of the operation this target pulls in. - - runlevel6.target is an alias for this target unit, for compatibility - with SysV. @@ -707,28 +697,11 @@ multi-user.target, this target could be seen as single-user.target. - runlevel1.target is an alias for this target unit, for - compatibility with SysV. - Use the systemd.unit=rescue.target kernel command line option to boot into this mode. A short alias for this kernel command line option is 1, for compatibility with SysV. - - runlevel2.target - runlevel3.target - runlevel4.target - runlevel5.target - - These are targets that are called whenever the SysV - compatibility code asks for runlevel 2, 3, 4, 5, - respectively. It is a good idea to make this an alias for - (i.e. symlink to) graphical.target - (for runlevel 5) or multi-user.target - (the others). - - shutdown.target diff --git a/man/systemd.target.xml b/man/systemd.target.xml index 19dabe4497..3f83204b2d 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -45,10 +45,7 @@ for examples and descriptions of standard systemd targets. Target units provide a more flexible replacement for SysV runlevels in the classic SysV init - system. For compatibility reasons special target units such as runlevel3.target - exist which are used by the SysV runlevel compatibility code in systemd, see - systemd.special7 for - details. + system. Note that a target unit file must not be empty, lest it be considered a masked unit. It is recommended to provide a [Unit] section which includes informative Description= and diff --git a/tools/command_ignorelist b/tools/command_ignorelist index 72544ae0dd..aed479938f 100644 --- a/tools/command_ignorelist +++ b/tools/command_ignorelist @@ -326,7 +326,6 @@ systemd.special.xml ./refsect1[title="Units managed by the system service manage systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special Passive System Units "]/variablelist/varlistentry[term="remote-fs-pre.target"] systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special System Units"]/variablelist/varlistentry[term="rescue.target"] systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special Passive System Units "]/variablelist/varlistentry[term="rpcbind.target"] -systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special System Units"]/variablelist/varlistentry[term="runlevel2.target"] systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special System Units"]/variablelist/varlistentry[term="shutdown.target"] systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special System Units"]/variablelist/varlistentry[term="sigpwr.target"] systemd.special.xml ./refsect1[title="Units managed by the system service manager"]/refsect2[title="Special System Units"]/variablelist/varlistentry[term="sleep.target"] diff --git a/units/meson.build b/units/meson.build index 34b3222f11..4f47a3b2bd 100644 --- a/units/meson.build +++ b/units/meson.build @@ -1,7 +1,5 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -with_runlevels = conf.get('HAVE_SYSV_COMPAT') == 1 - units = [ { 'file' : 'basic.target' }, { 'file' : 'blockdev@.target' }, @@ -51,7 +49,7 @@ units = [ }, { 'file' : 'graphical.target', - 'symlinks' : ['default.target'] + (with_runlevels ? ['runlevel5.target'] : []), + 'symlinks' : ['default.target'], }, { 'file' : 'halt.target' }, { @@ -144,20 +142,14 @@ units = [ 'conditions' : ['ENABLE_MACHINED'], }, { 'file' : 'modprobe@.service' }, - { - 'file' : 'multi-user.target', - 'symlinks' : with_runlevels ? ['runlevel2.target', 'runlevel3.target', 'runlevel4.target'] : [], - }, + { 'file' : 'multi-user.target' }, { 'file' : 'network-online.target' }, { 'file' : 'network-pre.target' }, { 'file' : 'network.target' }, { 'file' : 'nss-lookup.target' }, { 'file' : 'nss-user-lookup.target' }, { 'file' : 'paths.target' }, - { - 'file' : 'poweroff.target', - 'symlinks' : with_runlevels ? ['runlevel0.target'] : [], - }, + { 'file' : 'poweroff.target' }, { 'file' : 'printer.target' }, { 'file' : 'proc-sys-fs-binfmt_misc.automount', @@ -182,7 +174,7 @@ units = [ }, { 'file' : 'reboot.target', - 'symlinks' : ['ctrl-alt-del.target'] + (with_runlevels ? ['runlevel6.target'] : []), + 'symlinks' : ['ctrl-alt-del.target'], }, { 'file' : 'remote-cryptsetup.target', @@ -202,10 +194,7 @@ units = [ 'symlinks' : ['initrd-root-device.target.wants/'], }, { 'file' : 'rescue.service.in' }, - { - 'file' : 'rescue.target', - 'symlinks' : with_runlevels ? ['runlevel1.target'] : [], - }, + { 'file' : 'rescue.target' }, { 'file' : 'rpcbind.target' }, { 'file' : 'serial-getty@.service.in' }, { 'file' : 'shutdown.target' }, @@ -983,10 +972,4 @@ else dbussessionservicedir / 'org.freedesktop.systemd1.service')) endif -if conf.get('HAVE_SYSV_COMPAT') == 1 - foreach i : [1, 2, 3, 4, 5] - install_emptydir(systemunitdir / 'runlevel@0@.target.wants'.format(i)) - endforeach -endif - subdir('user') From 75df71d1802d77e7d903fa7f09c205668ff6a8f9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 23 Apr 2025 10:48:11 +0900 Subject: [PATCH 7/9] NEWS: mention removal of several SysV compat features --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index 74a91f5e8e..5c72926d18 100644 --- a/NEWS +++ b/NEWS @@ -139,6 +139,15 @@ CHANGES WITH 258 in spe: * The command 'journalctl --follow' now exits with success on SIGTERM/SIGINT, or its pipe STDOUT is disconnected. + * Support for System V style system state control has been removed: + - The /dev/initctl device node has been removed. + - The initctl, runlevel and telinit commands have been removed. + - Support for system state control via the init command (e.g. + 'init 3') has been removed. + - The units runlevel[0-6].target have been removed. + - The concept of runlevels has been removed, so runlevel transitions + are no longer recorded in the utmp/wtmp databases. + Announcements of Future Feature Removals: * Support for System V service scripts is deprecated and will be From c3116558ade4fe6ddc81ca2d369d519aa01e73bf Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 21 Apr 2025 13:38:01 +0900 Subject: [PATCH 8/9] mkosi: drop SysV compat files from packages These workarounds can be dropped when the downstream .spec or filelists are updated. --- .../build/mkosi.conf.d/opensuse/mkosi.build.chroot | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mkosi/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.build.chroot b/mkosi/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.build.chroot index ac7202c1d0..8ec7500d7b 100755 --- a/mkosi/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.build.chroot +++ b/mkosi/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.build.chroot @@ -19,9 +19,10 @@ TS="${SOURCE_DATE_EPOCH:-$(date +%s)}" # disable manpage compression as the files cannot be found. Fix the issue by removing the compression # extension. # -# TODO: remove manual cgroups-agent and pubring patches when the upstream spec is updated +# TODO: remove patches for removal of cgroups-agent, renaming pubring file, and removal of sysv files +# when the upstream spec is updated while read -r filelist; do - sed 's/\.gz$//; /systemd-cgroups-agent/d; s/import-pubring.gpg/import-pubring.pgp/' "$filelist" >"/tmp/$(basename "$filelist")" + sed -E 's/\.gz$//; /systemd-cgroups-agent/d; s/import-pubring.gpg/import-pubring.pgp/; /(initctl|runlevel|telinit)/ d' "$filelist" >"/tmp/$(basename "$filelist")" mount --bind "/tmp/$(basename "$filelist")" "$filelist" done < <(find "pkg/$PKG_SUBDIR${GIT_SUBDIR:+/$GIT_SUBDIR}" -name "files.*") From f12cc0d3bec95381d94ec6f88286f59b28acf16d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 12 Jul 2025 20:40:47 +0900 Subject: [PATCH 9/9] mkosi: update debian commit reference to a8ad8e30e70c0b82ecb8fe016f2dde3a084236f0 * a8ad8e30e7 Stop installing legacy sysv tools/units for upstream builds * 9856b1b1bd Install new files for upstream build * b68bfb52d4 Update changelog for 257.7-2 release * 3bd5f6d2e0 systemd-boot-tools: change architecture to linux-any * 057ce29542 Move bootctl zsh completion file too to new package * b47be01659 Revert "Revert installing systemd-networkd-varlink.socket" * 71a425dd3b ukify: recommend sbsigntool | pesign for signing * 54f67475ee Revert installing systemd-networkd-varlink.socket --- mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/pkgenv.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/pkgenv.conf b/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/pkgenv.conf index 18a2e01ba3..62486e424a 100644 --- a/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/pkgenv.conf +++ b/mkosi/mkosi.conf.d/debian-ubuntu/mkosi.conf.d/pkgenv.conf @@ -5,5 +5,5 @@ Environment= GIT_URL=https://salsa.debian.org/systemd-team/systemd.git GIT_SUBDIR=debian GIT_BRANCH=debian/master - GIT_COMMIT=cc380fbc8af2e17165623d16630b7fc3ab4291d0 + GIT_COMMIT=a8ad8e30e70c0b82ecb8fe016f2dde3a084236f0 PKG_SUBDIR=debian