diff options
Diffstat (limited to 'src/systemctl/systemctl-sysv-compat.c')
-rw-r--r-- | src/systemctl/systemctl-sysv-compat.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c new file mode 100644 index 0000000..2aa1ec6 --- /dev/null +++ b/src/systemctl/systemctl-sysv-compat.c @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "env-util.h" +#include "fd-util.h" +#include "initreq.h" +#include "install.h" +#include "io-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "strv.h" +#include "systemctl-sysv-compat.h" +#include "systemctl.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) { + 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; + struct tm tm = {}; + time_t s; + usec_t n; + + 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; + + n = now(CLOCK_REALTIME); + s = (time_t) (n / USEC_PER_SEC); + + assert_se(localtime_r(&s, &tm)); + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + tm.tm_sec = 0; + + s = mktime(&tm); + assert(s >= 0); + + *ret = (usec_t) s * USEC_PER_SEC; + + while (*ret <= n) + *ret += USEC_PER_DAY; + } + + return 0; +} + +int enable_sysv_units(const char *verb, char **args) { + int r = 0; + +#if HAVE_SYSV_COMPAT + _cleanup_(lookup_paths_free) LookupPaths paths = {}; + unsigned f = 0; + SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND; + + /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ + + if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM) + return 0; + + if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) + return 0; + + if (!STR_IN_SET(verb, + "enable", + "disable", + "is-enabled")) + return 0; + + r = lookup_paths_init_or_warn(&paths, arg_runtime_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); + if (r < 0) + return r; + + r = 0; + while (args[f]) { + + const char *argv[] = { + LIBEXECDIR "/systemd-sysv-install", + NULL, /* --root= */ + NULL, /* verb */ + NULL, /* service */ + NULL, + }; + + _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL, *v = NULL; + bool found_native = false, found_sysv; + const char *name; + unsigned c = 1; + pid_t pid; + int j; + + name = args[f++]; + + if (!endswith(name, ".service")) + continue; + + if (path_is_absolute(name)) + continue; + + j = unit_file_exists(arg_runtime_scope, &paths, name); + if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) + return log_error_errno(j, "Failed to look up unit file state: %m"); + found_native = j != 0; + + /* If we have both a native unit and a SysV script, enable/disable them both (below); for + * is-enabled, prefer the native unit */ + if (found_native && streq(verb, "is-enabled")) + continue; + + p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name); + if (!p) + return log_oom(); + + p[strlen(p) - STRLEN(".service")] = 0; + found_sysv = access(p, F_OK) >= 0; + if (!found_sysv) + continue; + + if (!arg_quiet) { + if (found_native) + log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); + else + log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); + } + + if (!isempty(arg_root)) { + q = strjoin("--root=", arg_root); + if (!q) + return log_oom(); + + argv[c++] = q; + } + + /* Let's copy the verb, since it's still pointing directly into the original argv[] array we + * got passed, but safe_fork() is likely going to rewrite that for the new child */ + v = strdup(verb); + if (!v) + return log_oom(); + + argv[c++] = v; + argv[c++] = basename(p); + argv[c] = NULL; + + l = strv_join((char**)argv, " "); + if (!l) + return log_oom(); + + if (!arg_quiet) + log_info("Executing: %s", l); + + j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + if (j < 0) + return j; + if (j == 0) { + /* Child */ + execv(argv[0], (char**) argv); + log_error_errno(errno, "Failed to execute %s: %m", argv[0]); + _exit(EXIT_FAILURE); + } + + j = wait_for_terminate_and_check("sysv-install", pid, WAIT_LOG_ABNORMAL); + if (j < 0) + return j; + if (streq(verb, "is-enabled")) { + if (j == EXIT_SUCCESS) { + if (!arg_quiet) + puts("enabled"); + enable_state = SYSV_UNIT_ENABLED; + } else { + if (!arg_quiet) + puts("disabled"); + if (enable_state != SYSV_UNIT_ENABLED) + enable_state = SYSV_UNIT_DISABLED; + } + + } else if (j != EXIT_SUCCESS) + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ + + if (found_native) + continue; + + /* Remove this entry, so that we don't try enabling it as native unit */ + assert(f > 0); + f--; + assert(args[f] == name); + strv_remove(args + f, name); + } + + if (streq(verb, "is-enabled")) + return enable_state; +#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 +} |