daf71ef61c
This is just some refactoring: shifting around of code, not change in codeflow. This splits up the way too huge systemctl.c in multiple more easily digestable files. It roughly follows the rule that each family of verbs gets its own .c/.h file pair, and so do all the compat executable names we support. Plus three extra files for sysv compat (which existed before already, but I renamed slightly, to get the systemctl- prefix lik everything else), a -util file with generic stuff everything uses, and a -logind file with everything that talks directly to logind instead of PID1. systemctl is still a bit too complex for my taste, but I think this way itc omes in a more digestable bits at least. No change of behaviour, just reshuffling of some code.
272 lines
8.4 KiB
C
272 lines
8.4 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#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
|
|
struct init_request request;
|
|
_cleanup_close_ int fd = -1;
|
|
const char *p;
|
|
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(p, "/run/initctl", "/dev/initctl") {
|
|
fd = open(p, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
|
|
if (fd >= 0 || errno != ENOENT)
|
|
break;
|
|
}
|
|
if (fd < 0) {
|
|
if (errno == ENOENT)
|
|
return 0;
|
|
|
|
return log_error_errno(errno, "Failed to open initctl fifo: %m");
|
|
}
|
|
|
|
request = (struct init_request) {
|
|
.magic = INIT_MAGIC,
|
|
.sleeptime = 0,
|
|
.cmd = INIT_CMD_RUNLVL,
|
|
.runlevel = rl,
|
|
};
|
|
|
|
r = loop_write(fd, &request, sizeof(request), false);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to write to %s: %m", p);
|
|
|
|
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;
|
|
|
|
/* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
|
|
|
|
if (arg_scope != UNIT_FILE_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(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = 0;
|
|
while (args[f]) {
|
|
|
|
const char *argv[] = {
|
|
ROOTLIBEXECDIR "/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_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|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");
|
|
r = 1;
|
|
} else {
|
|
if (!arg_quiet)
|
|
puts("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);
|
|
}
|
|
|
|
#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
|
|
}
|