스크립트가 UDEV 트리거로 호출되면 /dev/tty가 없습니다.

스크립트가 UDEV 트리거로 호출되면 /dev/tty가 없습니다.

재고 Raspbian이 설치된 라즈베리 파이가 있습니다. 나는 또한 잘 작동하는 RetroPI를 설치했습니다.

제가 하고 싶었던 것은 게임 패드가 블루투스를 통해 연결되었을 때 에뮬레이션 스테이션(retropi 메인 실행 스크립트)을 시작하는 작은 자동 시작 스크립트를 작성하는 것입니다. 간단한 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"

예상대로 작동하며 게임패드가 페어링되면 스크립트가 실행됩니다.

문제는 예를 들어 SSH를 통해 스크립트를 실행하면

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

예상대로 에뮬레이션.그러나 위의 udev 규칙에 대한 작업 트리거로 동일한 스크립트를 사용하면 실패합니다.다음과 같은 오류가 있습니다.

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"

나는 어떤 이유로 (im linux newb) udev 트리거 컨텍스트에서 tty에 액세스할 수 없다고 생각합니다. 어떻게든 고칠 수 있을까요?

추신. 부팅 시 시작되는 백그라운드 데몬(명명된 파이프를 사용하기 위해)을 실행하려고 시도했지만 효과는 정확히 동일합니다. 스크립트 실행 컨텍스트에 tty가 없어 백그라운드 작업의 일반적인 동작이라고 생각하게 됩니다.

스크립트 자체는 IMHO와 관련이 없지만 명확성을 위해 제공하겠습니다.

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

답변1

스크립트를 TTY에 첨부하거나 tty를 스크립트에 할당하는 대신 다음을 사용하여 대상 tty의 입력 버퍼에 명령을 직접 주입하여 목표를 달성했습니다.

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

이것은 명령을 입력하고 기본 터미널에서 "enter"를 누르는 것입니다.

추신. Raspbian writevt의 일부이며 console-toolsRaspbian에서 사용 가능합니다(적어도 오늘은)

답변2

커널이 작업을 트리거하고 udev규칙의 일부로 스크립트를 실행하는 경우 해당 스크립트의 환경은 매우 제한됩니다.

단일 Linux 컴퓨터에 동시에 로그인한 많은 사용자가 있을 수 있으며, 각 사용자는 여러 개의 tty를 가지고 있습니다.물론커널에서 실행되는 스크립트에는 tty가 없습니다. 이 스크립트를 시작한 사용자가 없습니다. 따라서 tty를 할당할 수 있다고 하더라도 사용 중인 많은 tty 중 어느 것을 사용해야 할까요? X 연결(자주 묻는 질문)도 마찬가지입니다.

다음에서 볼 수 있듯이 더 많은 제한 사항이 있습니다 man 7 udev.

달리다

특정 장치에 대해 실행할 프로그램 목록에 프로그램을 추가합니다. 이는 매우 짧은 실행 작업에만 사용할 수 있습니다. 장기간 이벤트 프로세스를 실행하면 이 장치 또는 종속 장치에 대한 모든 추가 이벤트가 차단될 수 있습니다. 장기 실행 작업은 이벤트 프로세스 자체에서 즉시 분리되어야 합니다. RUN{fail_event_on_error} 옵션이 지정되고 실행된 프로그램이 0이 아닌 값을 반환하는 경우 나중에 처리할 수 있도록 이벤트가 실패한 것으로 표시됩니다.

귀하의 스크립트를 올바르게 읽었다면 귀하는 매우 오랜 시간 동안 실행될 특정 X 연결에서 무언가를 시작하거나 중지하기를 원할 것입니다. 이는 작업을 두 작업으로 분할한 경우에만 작동합니다. 해당 X 서버가 시작될 때 시작되는 일종의 악마를 갖고(인증 쿠키를 얻으려면 X 서버를 시작해야 함) 악마에게 다음과 같이 지시합니다. 서비스 시작 또는 중지와 같은 특정 작업을 수행해야 하는 udev 실행 스크립트.

이러한 방식으로 udev 실행 스크립트가 즉시 종료될 수 있습니다.

systemd를 사용하는 경우 udev 규칙에서 유닛을 시작할 수도 있습니다(예:여기).

편집하다

를 사용하려면 악마에서 실행되는 tty1로그인 서비스( )를 변경하는 것을 고려하세요 . 그러면 스크립트에서 이를 트리거할 수 있습니다.gettytty1

예를 들어여기가능한 방법에 대한 자세한 내용은 필요에 따라 다양합니다.

관련 정보