Não /dev/tty quando o script é invocado como gatilho UDEV

Não /dev/tty quando o script é invocado como gatilho UDEV

Eu tenho raspberry pi com estoque raspbian instalado. Também instalei o RetroPI, que funciona perfeitamente.

O que eu queria fazer era escrever um pequeno script de inicialização automática que iniciasse o emulationstation (script de execução principal do retropi) quando o game pad estiver conectado via bluetooth. Eu fiz uma regra simples do udev

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 conforme o esperado, o script é executado quando o gamepad é emparelhado.

O problema é que se eu executar meu script via, por exemplo, SSH

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

estação de emulação conforme esperado.No entanto, se o mesmo script for usado como gatilho de ação para a regra udev acima, ele falharácom erros 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"

Eu acho que por alguns motivos (sou linux newb) tty não está acessível no contexto do gatilho do udev. Posso consertar isso de alguma forma?

PS. Eu tentei executar o deamon em segundo plano (para usar pipes nomeados) que é iniciado no momento da inicialização, mas o efeito é exatamente o mesmo - não há tty no contexto de execução do script, o que me faz pensar que esse é o comportamento geral das tarefas em segundo plano

O script em si é IMHO irrelevante, mas também o fornecerei para maior clareza:

#!/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

Responder1

Em vez de tentar anexar o script ao TTY ou atribuir o tty ao script, consegui atingir meu objetivo injetando o comando diretamente no buffer de entrada do destino tty com isto:

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

isso digita o comando e pressiona “enter” no terminal principal.

PS. writevtfaz parte console-toolse está disponível no Raspbian (pelo menos hoje)

Responder2

Quando o kernel aciona ações e udevexecuta scripts como parte das regras, o ambiente desses scripts é muito restrito.

Pode haver muitos usuários logados simultaneamente em um único computador Linux, cada usuário com vários tty's, entãoclaroum script executado a partir do kernel não possui um tty: Nenhum usuário iniciou este script, portanto, mesmo que alguém pudesse atribuir um tty a ele, qual dos muitos tty em uso deveria ser? E o mesmo vale para uma conexão X (que também é solicitada com frequência).

Existem mais restrições, como você pode ver em man 7 udev:

CORRER

Adicione um programa à lista de programas a serem executados em um dispositivo específico. Isso só pode ser usado para tarefas de execução muito curta. A execução de um processo de evento por um longo período de tempo pode bloquear todos os eventos adicionais deste dispositivo ou de um dispositivo dependente. Tarefas de longa duração precisam ser imediatamente separadas do próprio processo de evento. Se a opção RUN{fail_event_on_error} for especificada, e o programa executado retornar diferente de zero, o evento será marcado como falhado para um possível tratamento posterior.

Se eu li seu script corretamente, você deseja iniciar ou parar algo em uma determinada conexão X que estará em execução por muito tempo. Isso só funcionará se você dividir sua tarefa em duas: ter algum tipo de demônio que seja iniciado quando o servidor X correspondente for iniciado (o servidor X precisa ser iniciado para que você possa obter o cookie de autorização) e então informar o demônio em o script de execução do udev que deve fazer certas coisas, como iniciar ou parar um serviço.

Dessa forma, o script de execução do udev pode sair imediatamente.

Se você estiver usando o systemd, você também pode iniciar uma unidade a partir de uma regra do udev (veja, por exemplo,aqui).

Editar

Se você quiser usar tty1, considere alterar o serviço de login ( getty) executado tty1em seu demônio, que você pode acionar a partir do script.

Então, por exemploaquipara obter detalhes sobre uma possível maneira de fazer isso, varie conforme necessário.

informação relacionada