當腳本作為 UDEV 觸發器時沒有 /dev/tty

當腳本作為 UDEV 觸發器時沒有 /dev/tty

我有樹莓派,安裝了樹莓派。我還安裝了 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"

我認為由於某些原因(我是linux新手)無法從udev觸發器上下文存取tty。我能以某種方式修復它嗎?

附言。我嘗試運行在啟動時啟動的後台守護進程(使用命名管道),但效果完全相同 - 腳本執行上下文中沒有 tty,這讓我認為這是後台任務的一般行為

腳本本身與恕我直言無關,但為了清楚起見,我也會提供它:

#!/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')"

輸入指令並在主終端機上點選「回車」。

附言。writevt是 Raspbian 的一部分console-tools並且可以在 Raspbian 上使用(至少今天)

答案2

當核心觸發操作並udev作為規則的一部分運行腳本時,這些腳本的環境受到很大限制。

可以有許多使用者同時登入一台 Linux 計算機,每個使用者有多個 tty,所以當然從內核運行的腳本沒有 tty:沒有用戶啟動該腳本,因此即使可以為其分配 tty,它應該是正在使用的眾多 tty 中的哪一個? X 連結也是如此(這也是常被問到的)。

還有更多限制,如您所見man 7 udev

跑步

將程式新增至要為特定裝置執行的程式清單中。這只能用於非常短的運行任務。長時間運行事件進程可能會阻止該裝置或從屬裝置的所有進一步事件。長時間運行的任務需要立即與事件流程本身分離。如果指定了選項 RUN{fail_event_on_error},並且執行的程式傳回非零,則該事件將標記為失敗,以便稍後處理。

如果我正確地閱讀了您的腳本,您想要啟動或停止給定 X 連接上將運行很長時間的某些操作。只有當您將任務分成兩個任務時,這才有效:擁有某種在相應的 X 伺服器啟動時啟動的惡魔(需要啟動 X 伺服器,以便您可以獲得授權 cookie),然後告訴該惡魔udev 運行腳本應該執行某些操作,例如啟動或停止服務。

這樣udev運行腳本就可以立即退出。

如果您使用 systemd,您也可以從 udev 規則啟動一個單元(請參閱例如這裡)。

編輯

如果您想使用,請考慮變更在您的守護程式上執行的tty1登入服務 ( ) ,然後您可以從腳本觸發該登入服務。gettytty1

所以例如這裡有關可能的方法的詳細信息,請根據需要進行更改。

相關內容