No /dev/tty cuando se invoca el script como activador UDEV

No /dev/tty cuando se invoca el script como activador UDEV

Tengo raspberry pi con stock raspbian instalado. También instalé RetroPI que funciona bien.

Lo que quería hacer es escribir un pequeño script de inicio automático que iniciara la estación de emulación (script de ejecución principal de retropi) cuando el gamepad esté conectado a través de bluetooth. He hecho una regla udev simple

pi@raspberrypi:~ $ cat /etc/udev/rules.d/99-zlocal.rules
SUBSYSTEM=="bluetooth",SUBSYSTEMS=="amba", ATTRS{id}=="00241011", ACTION=="add", RUN+="/usr/local/bin/emulator-controll.sh start"

que funciona como se esperaba, el script se ejecuta cuando el gamepad está emparejado.

El problema es que si ejecuto mi script a través de, por ejemplo, SSH como

sudo emulator-controll.sh start        #sudo is optional here, but I want the same conditions as in udev trigger context

estación de emulación como se esperaba.Sin embargo, si se utiliza el mismo script como desencadenante de acción para la regla udev anterior, fallacon errores como

Wed Aug 21 00:02:33 CEST 2019
Invoking user: root
Invoked start command
Invoking emulation station start command
Done
'unknown': I need something more specific.
tput: unknown terminal "unknown"
Segmentation fault
/usr/bin/emulationstation: line 23: /dev/tty: No such device or address
tput: unknown terminal "unknown"

Creo que por algunas razones (soy novato en Linux), no se puede acceder a tty desde el contexto de activación de udev. ¿Puedo arreglarlo de alguna manera?

PD. Intenté ejecutar el demonio en segundo plano (para usar canalizaciones con nombre) que se inicia en el momento del arranque, pero el efecto es exactamente el mismo: no hay tty en el contexto de ejecución del script, lo que me hace pensar que es el comportamiento general de las tareas en segundo plano.

El script en sí es irrelevante en mi humilde opinión, pero también lo proporcionaré para mayor claridad:

#!/bin/bash
#/bin/date >> /tmp/udev.log
#env >> /tmp/udev.log
#echo "Connected" > /dev/pts/2

log=/var/log/emulator-controll.log

{
readarray -t PIDS <<< $(ps aux | grep [e]mulationstation | awk '{print $2}')
echo ">${PIDS[@]}<"
echo >> $log
/bin/date >> $log
echo "Invoking user: $(whoami)"
case "$1" in
        start )
                echo "Invoked start command" >> $log
                if [[ "${#PIDS[@]}" -ge 3 ]]; then
                        echo "Emulator is already running. No action is taken" >> $log
                else
                        echo "Invoking emulation station start command" >> $log
                        export DISPLAY=:0
                        sudo -u pi nohup emulationstation  &
                fi
                echo "Done" >> $log
                ;;
        stop )
                echo "Invoked stop command" >> $log
                if [[ -z "${PIDS[0]}" ]]; then
                        echo "Emulator is not running, no action is taken" >> $log
                else
                        echo "Killing processes with PIDs ${PIDS[@]}" >> $log
                        for pid in "${PIDS[@]}"; do
                                kill $pid
                        done
                        echo "Done" >> $log
                fi
                ;;
        *)
                echo "Usage: $0 {start|stop}"
esac

} >> $log 2>>$log

Respuesta1

En lugar de intentar adjuntar un script a TTY o asignar tty al script, logré mi objetivo inyectando el comando directamente en el búfer de entrada del tty de destino con esto:

sudo writevt /dev/tty1 "emulationstation $(echo -ne '\r')"

esto escribe el comando y presiona "enter" en la terminal principal.

PD. writevtes parte de console-toolsy que está disponible en Raspbian (al menos hoy)

Respuesta2

Cuando el kernel desencadena acciones y udevejecuta scripts como parte de las reglas, el entorno de esos scripts es muy restringido.

Puede haber muchos usuarios conectados simultáneamente en una sola computadora Linux, cada usuario con múltiples tty, por lo quepor supuestoun script ejecutado desde el kernel no tiene un tty: ningún usuario ha iniciado este script, por lo que incluso si se pudiera asignarle un tty, ¿cuál de los muchos tty en uso debería ser? Y lo mismo ocurre con una conexión X (que también es frecuente).

Hay más restricciones, como puedes ver en man 7 udev:

CORRER

Agregue un programa a la lista de programas que se ejecutarán para un dispositivo específico. Esto sólo se puede utilizar para tareas de ejecución muy corta. La ejecución de un proceso de evento durante un período prolongado puede bloquear todos los eventos adicionales para este dispositivo o un dispositivo dependiente. Las tareas de larga duración deben separarse inmediatamente del proceso del evento en sí. Si se especifica la opción RUN{fail_event_on_error} y el programa ejecutado devuelve un valor distinto de cero, el evento se marcará como fallido para un posible manejo posterior.

Si leí su script correctamente, querrá iniciar o detener algo en una conexión X determinada que se ejecutará durante mucho tiempo. Esto solo puede funcionar si divide su tarea en dos tareas: tener algún tipo de demonio que se inicie cuando se inicie el servidor X correspondiente (el servidor X debe iniciarse para que pueda obtener la cookie de autorización) y luego informarle al demonio en el script de ejecución de udev indica que debe hacer ciertas cosas, como iniciar o detener un servicio.

De esta manera, el script de ejecución de udev puede salir inmediatamente.

Si está utilizando systemd, también puede iniciar una unidad desde una regla udev (ver, por ejemploaquí).

Editar

Si desea utilizar tty1, considere cambiar el servicio de inicio de sesión ( getty) que se ejecuta tty1en su demonio, que luego puede activar desde el script.

Entonces, por ejemploaquíPara obtener detalles sobre una posible forma de hacerlo, varíe según sea necesario.

información relacionada