我有安装了原装 raspbian 的 raspberry pi。我还安装了 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
emulationstation 正如预期的那样。但是,如果使用相同的脚本作为上述 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。我能以某种方式修复它吗?
PS. 我曾尝试运行在启动时启动的后台守护进程(使用命名管道),但效果完全相同 - 在脚本执行上下文中没有 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')"
在主终端上输入命令并按“Enter”键。
PS.writevt
是 Raspbian 的一部分console-tools
,并且至少在今天可用
答案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
登录服务()更改为,然后您可以从脚本中触发它。getty
tty1
例如这里了解可能的解决方法的详细信息,请根据需要进行更改。