Automontaje de USB, suspender con batería agotada, bloqueo por inactividad y TLP

- automontaje (servicio de runit, root): monta memorias USB y tarjetas SD
  en /mnt/ETIQUETA o /mnt/sdXY y las desmonta al sacarlas, con
  notificación. FAT/exFAT/NTFS (ntfs3 del kernel) a nombre del usuario;
  los discos internos (ID_BUS distinto de usb) no se tocan.
- bateria: al 3 % sin cargador avisa y, si en 10 s sigue descargando,
  suspende con sudo -n zzz (sudoers.d/zzz: solo zzz sin argumentos, sin
  contraseña, para wheel).
- .xinitrc: xss-lock bloquea con slock tras 30 min de inactividad y la
  pantalla se apaga un minuto después.
- install.sh: xss-lock, tlp y tlp-rdw; servicios tlp y automontaje; los
  sudoers se validan con visudo y se instalan con modo 440.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016UdJsHrbkmzhoCb6TthP2m
This commit is contained in:
Alejandro Guerrero 2026-09-27 21:48:36 +02:00
parent 2c2adaaa79
commit 2850a4fc73
Signed by: alejandrogs73
GPG key ID: 1CFF10953BEE333C
7 changed files with 148 additions and 10 deletions

View file

@ -0,0 +1,3 @@
# El script bateria suspende con "sudo -n zzz" cuando la batería llega al
# límite. Solo zzz sin argumentos, sin contraseña, para los de wheel.
%wheel ALL=(root) NOPASSWD: /usr/bin/zzz ""

3
sistema/etc/sv/automontaje/run Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
exec 2>&1
exec /usr/local/bin/automontaje

View file

@ -0,0 +1,98 @@
#!/bin/sh
# Monta solas las memorias USB y tarjetas SD en /mnt/ETIQUETA (o /mnt/sdXY
# si no tienen etiqueta) y las desmonta al sacarlas, avisando con una
# notificación al usuario de la sesión gráfica. Lo lanza runit como root
# (servicio automontaje). Los discos internos no se tocan.
# Usuario dueño de la sesión de dwm, o el primero normal (UID 1000).
usuario() {
pid=$(pgrep -xo dwm) && stat -c %u "/proc/$pid" || echo 1000
}
# avisar TÍTULO TEXTO: notificación en la sesión gráfica, si la hay
avisar() {
pid=$(pgrep -xo dwm) || return 0
nombre=$(getent passwd "$(stat -c %u "/proc/$pid")" | cut -d: -f1)
entorno=$(tr '\0' '\n' <"/proc/$pid/environ" |
grep -E '^(DISPLAY|DBUS_SESSION_BUS_ADDRESS|XDG_RUNTIME_DIR)=')
# shellcheck disable=SC2086
env $entorno chpst -u "$nombre" notify-send -i drive-removable-media \
"$1" "$2" >/dev/null 2>&1 &
}
montar() {
dev=$1 tipo=$2 etiqueta=$3
findmnt -rn -S "$dev" >/dev/null && return
dir=/mnt/$(printf %s "$etiqueta" | tr -c 'A-Za-z0-9._-' _)
if [ "$dir" = /mnt/ ] || mountpoint -q "$dir" ||
{ [ -d "$dir" ] && [ -n "$(ls -A "$dir")" ]; }; then
dir=/mnt/${dev#/dev/}
fi
# Los sistemas de archivos de Windows no guardan dueños: se montan a
# nombre del usuario para que pueda escribir sin sudo.
uid=$(usuario)
gid=$(getent passwd "$uid" | cut -d: -f4)
opts=nosuid,nodev,noatime
case $tipo in
vfat) opts=$opts,uid=$uid,gid=$gid,umask=022,utf8,flush ;;
exfat) opts=$opts,uid=$uid,gid=$gid,umask=022 ;;
ntfs) tipo=ntfs3 opts=$opts,uid=$uid,gid=$gid,umask=022 ;;
esac
mkdir -p "$dir"
if mount -t "$tipo" -o "$opts" "$dev" "$dir"; then
avisar "Memoria montada" "$dir"
else
rmdir "$dir" 2>/dev/null
avisar "No se pudo montar" "$dev ($tipo)"
fi
}
desmontar() {
dir=$(findmnt -rn -o TARGET -S "$1") || return 0
case $dir in /mnt/*) ;; *) return ;; esac
umount -l "$dir" && rmdir "$dir" 2>/dev/null
avisar "Memoria desconectada" "$dir"
}
limpiar() {
accion='' dev='' bus='' uso='' tipo='' etiqueta=''
}
# Lee eventos de udev (bloques KEY=VALOR separados por una línea vacía).
procesar() {
limpiar
while IFS= read -r linea; do
case $linea in
ACTION=*) accion=${linea#*=} ;;
DEVNAME=*) dev=${linea#*=} ;;
ID_BUS=*) bus=${linea#*=} ;;
ID_FS_USAGE=*) uso=${linea#*=} ;;
ID_FS_TYPE=*) tipo=${linea#*=} ;;
ID_FS_LABEL=*) etiqueta=${linea#*=} ;;
'')
case $bus:$dev in usb:* | *:/dev/mmcblk*) ;; *) dev='' ;; esac
if [ -n "$dev" ]; then
case $accion in
add | change) [ "$uso" = filesystem ] &&
montar "$dev" "$tipo" "$etiqueta" ;;
remove) desmontar "$dev" ;;
esac
fi
limpiar
;;
esac
done
}
{
# Primero lo que ya está conectado, luego los eventos nuevos
for d in /sys/class/block/*; do
echo ACTION=add
udevadm info --query=property --path="$d"
echo
done
exec udevadm monitor --udev --property --subsystem-match=block
} | procesar