Skip to content

Commit

Permalink
Inhibit short press on remote control power off
Browse files Browse the repository at this point in the history
Manage short press upon power off ourselves, and not with systemd
anymore, so that we only listen to the Power Button, and not to power
off buttons located on keyboards or remote controls.

This means pressing the Power Button on the computer with a short press
directly shutdowns the computer, and pressing power off on a remote
control with a short press does nothing.
  • Loading branch information
guyonvarch committed Apr 30, 2024
1 parent b3f89e4 commit 1b347ab
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 13 deletions.
17 changes: 4 additions & 13 deletions application.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ rec {

module = { config, lib, pkgs, ... }: {

imports = [ ./application/playos-status.nix ];
imports = [
./application/playos-status.nix
./application/power-management/default.nix
];

# Kiosk runs as a non-privileged user
users.users.play = {
Expand Down Expand Up @@ -118,18 +121,6 @@ rec {
};
};

# Ignore system control keys that do not make sense for kiosk applications
services.logind.extraConfig = ''
HandleSuspendKey=ignore
HandleRebootKey=ignore
HandleHibernateKey=ignore
HandlePowerKey=poweroff
HandlePowerKeyLongPress=poweroff
HandleRebootKeyLongPress=poweroff
HandleSuspendKeyLongPress=poweroff
HandleHibernateKeyLongPress=poweroff
'';

# Driver service
systemd.services."dividat-driver" = {
description = "Dividat Driver";
Expand Down
73 changes: 73 additions & 0 deletions application/power-management/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{ pkgs, config, lib, ... }:

let

power-button-shutdown = pkgs.stdenv.mkDerivation {
name = "power-button-shutdown";
propagatedBuildInputs = [
(pkgs.python3.withPackages (pythonPackages: with pythonPackages; [
evdev
]))
];
dontUnpack = true;
installPhase = "install -Dm755 ${./power-button-shutdown.py} $out/bin/power-button-shutdown";
};

in {

# Ignore system control keys that do not make sense for kiosk applications
services.logind.extraConfig = ''
HandleSuspendKey=ignore
HandleRebootKey=ignore
HandleHibernateKey=ignore
HandlePowerKey=ignore
HandlePowerKeyLongPress=poweroff
HandleRebootKeyLongPress=poweroff
HandleSuspendKeyLongPress=poweroff
HandleHibernateKeyLongPress=poweroff
'';

hardware.uinput.enable = lib.mkDefault true;

systemd.services.power-button-shutdown = {
enable = true;
description = "Detect power off key press, by power button device only, then shutdown computer";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${power-button-shutdown}/bin/power-button-shutdown";
Restart = "always";

# Access to input devices without being root
# DynamicUser = true; # Currently prevent to shutdown the system
SupplementaryGroups = with config.users.groups; [ input.name uinput.name ];

# Hardening, see https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/hardware/kanata.nix#L117
# Not using DeviceAllow and DevicePolicy, as I couldn’t get access to device list with that.
CapabilityBoundingSet = [ "" ];
IPAddressDeny = [ "any" ];
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateNetwork = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
RestrictAddressFamilies = [ "AF_UNIX" ];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = [ "native" ];
SystemCallFilter = [
"@system-service"
"~@privileged"
"~@resources"
];
UMask = "0077";
};
};
}
23 changes: 23 additions & 0 deletions application/power-management/power-button-shutdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3

import evdev
import logging
import os
import sys

logging.basicConfig(level=logging.INFO)

devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
device_names = ', '.join([d.name for d in devices])
logging.info(f'Found devices: {device_names}')

power_off_device = next((d for d in devices if d.name == 'Power Button'), None)
if power_off_device is None:
logging.error(f'Power Off device not found')
sys.exit(1)

logging.info(f'Listenning to Power Off on device {power_off_device.path}')
for event in power_off_device.read_loop():
if event.type == evdev.ecodes.EV_KEY and evdev.ecodes.KEY[event.code] == 'KEY_POWER' and event.value:
logging.info('KEY_POWER detected on Power Off device, shutting down')
os.system('shutdown now')

0 comments on commit 1b347ab

Please sign in to comment.