У меня есть Raspberry Pi с установленным стоковым Raspbian. Я также установил RetroPI, который работает просто отлично.
Что я хотел сделать, так это написать небольшой скрипт автозапуска, который запускал бы emulationstation (retropi main run script) при подключении геймпада через bluetooth. Я сделал простое правило 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) tty недоступен из контекста триггера udev. Можно ли это как-то исправить?
P.S. Я пробовал запустить фоновый демон (для использования именованных каналов), который запускается во время загрузки, но эффект точно такой же - нет 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» на главном терминале.
P.S. writevt
является частью console-tools
и доступен на Raspbian (по крайней мере сегодня)
решение2
Когда ядро инициирует действия и udev
запускает скрипты как часть правил, среда этих скриптов весьма ограничена.
На одном компьютере Linux может быть одновременно авторизовано много пользователей, у каждого из которых может быть несколько терминалов, поэтомуконечноскрипт, запущенный из ядра, не имеет tty: Ни один пользователь не запускал этот скрипт, поэтому даже если бы можно было назначить ему tty, какой из многих используемых tty должен быть? И то же самое касается X-соединения (о котором также часто спрашивают).
Как вы можете видеть, ограничений больше man 7 udev
:
БЕГАТЬ
Добавить программу в список программ, которые должны быть выполнены для определенного устройства. Это можно использовать только для очень коротких задач. Выполнение процесса события в течение длительного периода времени может заблокировать все дальнейшие события для этого или зависимого устройства. Длительно выполняемые задачи необходимо немедленно отсоединить от самого процесса события. Если указана опция RUN{fail_event_on_error}, а выполненная программа возвращает ненулевое значение, событие будет помечено как неудачное для возможной последующей обработки.
Если я правильно понял ваш скрипт, вы хотите запустить или остановить что-то на данном X-подключении, которое будет работать очень долгое время. Это может сработать только если вы разделите свою задачу на две: иметь некий демон, который запускается при запуске соответствующего X-сервера (X-сервер должен быть запущен, чтобы вы могли получить cookie авторизации), а затем сообщить демону в скрипте запуска udev, что он должен выполнять определенные действия, например, запускать или останавливать службу.
Таким образом, скрипт запуска udev может завершиться немедленно.
Если вы используете systemd, вы также можете запустить модуль из правила udev (см., например,здесь).
Редактировать
Если вы хотите использовать tty1
, рассмотрите возможность изменения службы входа ( getty
), которая работает на tty1
вашем демоне, который затем можно будет запустить из скрипта.
Так напримерздесьдля получения подробной информации о возможном способе выполнения этого задания измените его по мере необходимости.