mirror of
https://github.com/Kicksecure/security-misc.git
synced 2025-08-07 00:14:14 -04:00
emerg-shutdown: fix the hang-on-shutdown bug, add autodetection of new keyboards, shutdown key configuration, and instant shutdown option
This commit is contained in:
parent
a1d1c56033
commit
e42078e90d
5 changed files with 153 additions and 20 deletions
19
etc/security-misc/emerg-shutdown/30_security_misc.conf
Normal file
19
etc/security-misc/emerg-shutdown/30_security_misc.conf
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
|
||||||
|
## See the file COPYING for copying conditions.
|
||||||
|
|
||||||
|
## Please use "/etc/security-misc/emerg-shutdown/50_user.conf" for your custom
|
||||||
|
## configuration, which will override the defaults found here.
|
||||||
|
## When Kicksecure is updated, this file may be overwritten.
|
||||||
|
|
||||||
|
## Set the key combo for forcing immediate shutdown. See the "Keys and
|
||||||
|
## buttons" section of "/usr/include/linux/input-event-codes.h" for possibly
|
||||||
|
## supported values. Not all keys are supported.
|
||||||
|
##
|
||||||
|
## All specified keys must be depressed at the same time to trigger a
|
||||||
|
## shutdown. Use a comma (",") to separate keys. If you want to alias certain
|
||||||
|
## keys to each other from emerg-shutdown's standpoint, use a pipe
|
||||||
|
## character("|").
|
||||||
|
##
|
||||||
|
## The default key sequence triggers a shutdown when Ctrl+Alt+Delete is
|
||||||
|
## pressed, allowing the use of either the left or right Ctrl and Alt keys.
|
||||||
|
EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE"
|
|
@ -6,9 +6,8 @@ Description=Emergency shutdown when boot media is removed
|
||||||
Documentation=https://github.com/Kicksecure/security-misc
|
Documentation=https://github.com/Kicksecure/security-misc
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=exec
|
||||||
ExecStart=/usr/libexec/security-misc/emerg-shutdown
|
ExecStart=/usr/libexec/security-misc/emerg-shutdown
|
||||||
RemainAfterExit=true
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
9
usr/lib/udev/rules.d/95-emerg-shutdown.rules
Normal file
9
usr/lib/udev/rules.d/95-emerg-shutdown.rules
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
SUBSYSTEM!="input", GOTO="end"
|
||||||
|
|
||||||
|
# new keyboard or mouse attached or removed, restart emerg-shutdown
|
||||||
|
KERNEL=="event*", ACTION=="add", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/usr/bin/systemctl restart emerg-shutdown.service"
|
||||||
|
KERNEL=="event*", ACTION=="add", ENV{ID_INPUT_KEYBOARD}=="1", GOTO="end"
|
||||||
|
KERNEL=="event*", ACTION=="remove", ENV{ID_INPUT_KEYBOARD}=="1", RUN+="/usr/bin/systemctl restart emerg-shutdown.service"
|
||||||
|
KERNEL=="event*", ACTION=="remove", ENV{ID_INPUT_KEYBOARD}=="1", GOTO="end"
|
||||||
|
|
||||||
|
LABEL="end"
|
|
@ -3,25 +3,49 @@
|
||||||
# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
|
# Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
|
||||||
# See the file COPYING for copying conditions.
|
# See the file COPYING for copying conditions.
|
||||||
|
|
||||||
gcc \
|
set -o errexit
|
||||||
-o \
|
set -o nounset
|
||||||
/run/emerg-shutdown \
|
set -o errtrace
|
||||||
-static \
|
set -o pipefail
|
||||||
/usr/src/security-misc/emerg-shutdown.c \
|
|
||||||
|| {
|
|
||||||
printf "%s\n" 'Could not compile force-shutdown executable!'
|
|
||||||
exit 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/');
|
## Make sure globs sort in a predictable, reproducible fashion
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
## memlockd daemonizes itself, so no need to background it
|
## Read emergency shutdown key configuration
|
||||||
memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg
|
for config_file in /etc/security-misc/emerg-shutdown/*.conf; do
|
||||||
|
source "${config_file}"
|
||||||
|
done
|
||||||
|
if [ -z "${EMERG_SHUTDOWN_KEYS}" ]; then
|
||||||
|
## Default to Ctrl+Alt+Delete if nothing else is set
|
||||||
|
EMERG_SHUTDOWN_KEYS="KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Find the devices that make up the root device
|
||||||
|
readarray -t root_devices < <(/usr/libexec/helper-scripts/get-backing-devices-for-mountpoint '/') || true;
|
||||||
|
if [ "${#root_devices[@]}" = '0' ] \
|
||||||
|
|| [ "${root_devices[0]}" == '' ]; then
|
||||||
|
## /dev/sda1 might be the right one...
|
||||||
|
root_devices[0]='/dev/sda1'
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Build the actual emerg-shutdown executable
|
||||||
|
if [ ! -f '/run/emerg-shutdown' ]; then
|
||||||
|
gcc \
|
||||||
|
-o \
|
||||||
|
/run/emerg-shutdown \
|
||||||
|
-static \
|
||||||
|
/usr/src/security-misc/emerg-shutdown.c \
|
||||||
|
|| {
|
||||||
|
printf "%s\n" 'Could not compile force-shutdown executable!'
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
## memlockd daemonizes itself, so no need to background it.
|
||||||
|
memlockd -c /usr/share/security-misc/security-misc-memlockd.cfg || true
|
||||||
|
|
||||||
|
## Launch emerg-shutdown
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
IFS=','
|
IFS=','
|
||||||
/run/emerg-shutdown "--devices=${root_devices[*]}" '--keys=KEY_LEFTCTRL|KEY_RIGHTCTRL,KEY_LEFTALT|KEY_RIGHTALT,KEY_DELETE' &
|
/run/emerg-shutdown "--devices=${root_devices[*]}" "--keys=${EMERG_SHUTDOWN_KEYS}"
|
||||||
IFS="$OLDIFS"
|
|
||||||
sleep 1
|
|
||||||
disown
|
|
||||||
exit 0
|
|
||||||
|
|
|
@ -94,6 +94,8 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
#define fd_stdin 0
|
#define fd_stdin 0
|
||||||
|
@ -104,6 +106,8 @@
|
||||||
#define input_path_size 20
|
#define input_path_size 20
|
||||||
#define key_flags_len 12
|
#define key_flags_len 12
|
||||||
|
|
||||||
|
int console_fd = 0;
|
||||||
|
|
||||||
/* Adapted from kloak/src/keycodes.c */
|
/* Adapted from kloak/src/keycodes.c */
|
||||||
struct name_value {
|
struct name_value {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -283,6 +287,8 @@ void print(int fd, char *str) {
|
||||||
void print_usage() {
|
void print_usage() {
|
||||||
print(fd_stderr, "Usage:\n");
|
print(fd_stderr, "Usage:\n");
|
||||||
print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n");
|
print(fd_stderr, " emerg-shutdown --devices=DEVICE1[,DEVICE2...] --keys=KEY_1[,KEY_2|KEY_3...]\n");
|
||||||
|
print(fd_stderr, "Or:\n");
|
||||||
|
print(fd_stderr, " emerg-shutdown --instant-shutdown\n");
|
||||||
print(fd_stderr, "Example:\n");
|
print(fd_stderr, "Example:\n");
|
||||||
print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n");
|
print(fd_stderr, " emerg-shutdown --devices=/dev/sda3 --keys=KEY_POWER\n");
|
||||||
}
|
}
|
||||||
|
@ -363,7 +369,74 @@ void load_list(const char *arg, size_t *result_list_len_ref, char ***result_list
|
||||||
}
|
}
|
||||||
|
|
||||||
int kill_system() {
|
int kill_system() {
|
||||||
return syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
|
/*
|
||||||
|
* It isn't safe to simply call the reboot syscall here - there is a
|
||||||
|
* graphics driver bug in the i915 driver on Bookworm that will throw a
|
||||||
|
* kernel warning during shutdown. Kicksecure sets panic_on_oops and
|
||||||
|
* panic_on_warn to 1 during bootup, which means this bug will cause a
|
||||||
|
* kernel panic and thus hang the system rather than shutting down.
|
||||||
|
*
|
||||||
|
* To mitigate this, we do two things:
|
||||||
|
*
|
||||||
|
* - We disable the panic_on_oops and panic_on_warn kernel settings before
|
||||||
|
* calling the reboot syscall. This way if a warn or oops does occur, at
|
||||||
|
* least it isn't as likely to block shutdown.
|
||||||
|
* - We switch virtual terminals before initiating the shutdown. This should
|
||||||
|
* hopefully keep whatever is going wrong from going wrong in the first
|
||||||
|
* place.
|
||||||
|
*
|
||||||
|
* This is probably a good idea for any system, because switching TTYs is a
|
||||||
|
* rather basic operation that is likely to work, while forcibly shutting
|
||||||
|
* down while X11 or Wayland still has control of the display is probably
|
||||||
|
* not as well tested (if it's been tested at all).
|
||||||
|
*
|
||||||
|
* Above all else though, we want to at least *try* to shutdown. Even if all
|
||||||
|
* our attempts to switch VTs fail and /proc isn't available for us to tweak
|
||||||
|
* kernel settings, we still need to try. Therefore we absolutely do not
|
||||||
|
* crash or block, except when waiting for a VT to become activated. (If VT
|
||||||
|
* activation blocks forever, the kernel is probably horribly broken and
|
||||||
|
* would probably panic imminently anyway.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *panic_on_oops_path = "/proc/sys/kernel/panic_on_oops";
|
||||||
|
const char *panic_on_warn_path = "/proc/sys/kernel/panic_on_warn";
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
int tgt_vt = 0;
|
||||||
|
int sysctl_fd = 0;
|
||||||
|
|
||||||
|
/* Turn off panic_on_oops. */
|
||||||
|
sysctl_fd = open(panic_on_oops_path, O_WRONLY);
|
||||||
|
if (sysctl_fd != -1) {
|
||||||
|
write(sysctl_fd, "0", 1);
|
||||||
|
close(sysctl_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Turn off panic_on_warn. */
|
||||||
|
sysctl_fd = open(panic_on_warn_path, O_WRONLY);
|
||||||
|
if (sysctl_fd != -1) {
|
||||||
|
write(sysctl_fd, "0", 1);
|
||||||
|
close(sysctl_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine which VT to switch to. Anything that isn't open yet will do. */
|
||||||
|
ret = ioctl(console_fd, VT_OPENQRY, &tgt_vt);
|
||||||
|
if (ret == -1) {
|
||||||
|
goto trykill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to switch to it. */
|
||||||
|
ret = ioctl(console_fd, VT_ACTIVATE, tgt_vt);
|
||||||
|
if (ret == -1) {
|
||||||
|
goto trykill;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for it to become active. */
|
||||||
|
ioctl(console_fd, VT_WAITACTIVE, tgt_vt);
|
||||||
|
|
||||||
|
trykill:
|
||||||
|
return syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||||
|
LINUX_REBOOT_CMD_POWER_OFF, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
@ -405,6 +478,9 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (arg_idx = 1; arg_idx < argc; arg_idx++) {
|
for (arg_idx = 1; arg_idx < argc; arg_idx++) {
|
||||||
|
if (strncmp(argv[arg_idx], "--instant-shutdown", strlen("--instant-shutdown")) == 0) {
|
||||||
|
kill_system();
|
||||||
|
}
|
||||||
if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) {
|
if (strncmp(argv[arg_idx], "--devices=", strlen("--devices=")) == 0) {
|
||||||
if (target_dev_name_raw_list != NULL) {
|
if (target_dev_name_raw_list != NULL) {
|
||||||
print(fd_stderr, "--devices cannot be passed more than once!\n");
|
print(fd_stderr, "--devices cannot be passed more than once!\n");
|
||||||
|
@ -422,6 +498,12 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console_fd = open("/dev/console", O_RDWR);
|
||||||
|
if (console_fd == -1) {
|
||||||
|
print(fd_stderr, "Could not open /dev/console!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *));
|
target_dev_list = safe_calloc(target_dev_list_len, sizeof(char *));
|
||||||
panic_key_list = safe_calloc(panic_key_list_len, sizeof(int *));
|
panic_key_list = safe_calloc(panic_key_list_len, sizeof(int *));
|
||||||
panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool));
|
panic_key_active_list = safe_calloc(panic_key_list_len, sizeof(bool));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue