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.
382 lines
12 KiB
C
382 lines
12 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "sd-login.h"
|
|
|
|
#include "bus-error.h"
|
|
#include "bus-locator.h"
|
|
#include "process-util.h"
|
|
#include "systemctl-logind.h"
|
|
#include "systemctl-start-unit.h"
|
|
#include "systemctl-util.h"
|
|
#include "systemctl.h"
|
|
#include "terminal-util.h"
|
|
#include "user-util.h"
|
|
|
|
int logind_set_wall_message(void) {
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
_cleanup_free_ char *m = NULL;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
m = strv_join(arg_wall, " ");
|
|
if (!m)
|
|
return log_oom();
|
|
|
|
log_debug("%s wall message \"%s\".", arg_dry_run ? "Would set" : "Setting", m);
|
|
if (arg_dry_run)
|
|
return 0;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "SetWallMessage", &error, NULL, "sb", m, !arg_no_wall);
|
|
if (r < 0)
|
|
return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r));
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* Ask systemd-logind, which might grant access to unprivileged users through polkit */
|
|
int logind_reboot(enum action a) {
|
|
#if ENABLE_LOGIND
|
|
static const struct {
|
|
const char *method;
|
|
const char *description;
|
|
} actions[_ACTION_MAX] = {
|
|
[ACTION_POWEROFF] = { "PowerOff", "power off system" },
|
|
[ACTION_REBOOT] = { "Reboot", "reboot system" },
|
|
[ACTION_HALT] = { "Halt", "halt system" },
|
|
[ACTION_SUSPEND] = { "Suspend", "suspend system" },
|
|
[ACTION_HIBERNATE] = { "Hibernate", "hibernate system" },
|
|
[ACTION_HYBRID_SLEEP] = { "HybridSleep", "put system into hybrid sleep" },
|
|
[ACTION_SUSPEND_THEN_HIBERNATE] = { "SuspendThenHibernate", "suspend system, hibernate later" },
|
|
};
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
if (a < 0 || a >= _ACTION_MAX || !actions[a].method)
|
|
return -EINVAL;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
polkit_agent_open_maybe();
|
|
(void) logind_set_wall_message();
|
|
|
|
log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", actions[a].method);
|
|
|
|
if (arg_dry_run)
|
|
return 0;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, actions[a].method, &error, NULL, "b", arg_ask_password);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r));
|
|
|
|
return 0;
|
|
#else
|
|
return -ENOSYS;
|
|
#endif
|
|
}
|
|
|
|
int logind_check_inhibitors(enum action a) {
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
_cleanup_strv_free_ char **sessions = NULL;
|
|
const char *what, *who, *why, *mode;
|
|
uint32_t uid, pid;
|
|
sd_bus *bus;
|
|
unsigned c = 0;
|
|
char **s;
|
|
int r;
|
|
|
|
if (arg_ignore_inhibitors || arg_force > 0)
|
|
return 0;
|
|
|
|
if (arg_when > 0)
|
|
return 0;
|
|
|
|
if (geteuid() == 0)
|
|
return 0;
|
|
|
|
if (!on_tty())
|
|
return 0;
|
|
|
|
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
|
return 0;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "ListInhibitors", NULL, &reply, NULL);
|
|
if (r < 0)
|
|
/* If logind is not around, then there are no inhibitors... */
|
|
return 0;
|
|
|
|
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
|
|
while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
|
|
_cleanup_free_ char *comm = NULL, *user = NULL;
|
|
_cleanup_strv_free_ char **sv = NULL;
|
|
|
|
if (!streq(mode, "block"))
|
|
continue;
|
|
|
|
sv = strv_split(what, ":");
|
|
if (!sv)
|
|
return log_oom();
|
|
|
|
if (!pid_is_valid((pid_t) pid))
|
|
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Invalid PID "PID_FMT".", (pid_t) pid);
|
|
|
|
if (!strv_contains(sv,
|
|
IN_SET(a,
|
|
ACTION_HALT,
|
|
ACTION_POWEROFF,
|
|
ACTION_REBOOT,
|
|
ACTION_KEXEC) ? "shutdown" : "sleep"))
|
|
continue;
|
|
|
|
get_process_comm(pid, &comm);
|
|
user = uid_to_name(uid);
|
|
|
|
log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
|
|
who, (pid_t) pid, strna(comm), strna(user), why);
|
|
|
|
c++;
|
|
}
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
|
|
r = sd_bus_message_exit_container(reply);
|
|
if (r < 0)
|
|
return bus_log_parse_error(r);
|
|
|
|
/* Check for current sessions */
|
|
sd_get_sessions(&sessions);
|
|
STRV_FOREACH(s, sessions) {
|
|
_cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
|
|
|
|
if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
|
|
continue;
|
|
|
|
if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
|
|
continue;
|
|
|
|
if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "wayland", "tty", "mir"))
|
|
continue;
|
|
|
|
sd_session_get_tty(*s, &tty);
|
|
sd_session_get_seat(*s, &seat);
|
|
sd_session_get_service(*s, &service);
|
|
user = uid_to_name(uid);
|
|
|
|
log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
|
|
c++;
|
|
}
|
|
|
|
if (c <= 0)
|
|
return 0;
|
|
|
|
log_error("Please retry operation after closing inhibitors and logging out other users.\n"
|
|
"Alternatively, ignore inhibitors and users with 'systemctl %s -i'.",
|
|
action_table[a].verb);
|
|
|
|
return -EPERM;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int prepare_firmware_setup(void) {
|
|
|
|
if (!arg_firmware_setup)
|
|
return 0;
|
|
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "SetRebootToFirmwareSetup", &error, NULL, "b", true);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r));
|
|
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Booting into firmware setup not supported.");
|
|
#endif
|
|
}
|
|
|
|
int prepare_boot_loader_menu(void) {
|
|
|
|
if (arg_boot_loader_menu == USEC_INFINITY)
|
|
return 0;
|
|
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderMenu", &error, NULL, "t", arg_boot_loader_menu);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r));
|
|
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Booting into boot loader menu not supported.");
|
|
#endif
|
|
}
|
|
|
|
int prepare_boot_loader_entry(void) {
|
|
|
|
if (!arg_boot_loader_entry)
|
|
return 0;
|
|
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderEntry", &error, NULL, "s", arg_boot_loader_entry);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r));
|
|
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Booting into boot loader entry not supported.");
|
|
#endif
|
|
}
|
|
|
|
int logind_schedule_shutdown(void) {
|
|
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
char date[FORMAT_TIMESTAMP_MAX];
|
|
const char *action;
|
|
const char *log_action;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
switch (arg_action) {
|
|
case ACTION_HALT:
|
|
action = "halt";
|
|
log_action = "Shutdown";
|
|
break;
|
|
case ACTION_POWEROFF:
|
|
action = "poweroff";
|
|
log_action = "Shutdown";
|
|
break;
|
|
case ACTION_KEXEC:
|
|
action = "kexec";
|
|
log_action = "Reboot via kexec";
|
|
break;
|
|
case ACTION_EXIT:
|
|
action = "exit";
|
|
log_action = "Shutdown";
|
|
break;
|
|
case ACTION_REBOOT:
|
|
default:
|
|
action = "reboot";
|
|
log_action = "Reboot";
|
|
break;
|
|
}
|
|
|
|
if (arg_dry_run)
|
|
action = strjoina("dry-", action);
|
|
|
|
(void) logind_set_wall_message();
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
|
|
if (r < 0)
|
|
return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
|
|
|
|
if (!arg_quiet)
|
|
log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", log_action, format_timestamp_style(date, sizeof(date), arg_when, arg_timestamp_style));
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Cannot schedule shutdown without logind support, proceeding with immediate shutdown.");
|
|
#endif
|
|
}
|
|
|
|
int logind_cancel_shutdown(void) {
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
sd_bus *bus;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
(void) logind_set_wall_message();
|
|
|
|
r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL);
|
|
if (r < 0)
|
|
return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r));
|
|
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Not compiled with logind support, cannot cancel scheduled shutdowns.");
|
|
#endif
|
|
}
|
|
|
|
int help_boot_loader_entry(void) {
|
|
#if ENABLE_LOGIND
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
_cleanup_strv_free_ char **l = NULL;
|
|
sd_bus *bus;
|
|
char **i;
|
|
int r;
|
|
|
|
r = acquire_bus(BUS_FULL, &bus);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = bus_get_property_strv(bus, bus_login_mgr, "BootLoaderEntries", &error, &l);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r));
|
|
|
|
if (strv_isempty(l))
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No boot loader entries discovered.");
|
|
|
|
STRV_FOREACH(i, l)
|
|
puts(*i);
|
|
|
|
return 0;
|
|
#else
|
|
return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
|
|
"Not compiled with logind support, cannot display boot loader entries.");
|
|
#endif
|
|
}
|