Estou trabalhando em uma configuração systemd/udev que gostaria de compartilhar upstream, mas não consigo fazê-la funcionar de maneira não hackeada. Essencialmente, tenho este script como executivo de um serviço systemd:
ICON="somepath/dslr-camera-white.png"
function on-display() {
local sdisplay=$(echo $XDG_SESSION_TYPE)
if [ "$sdisplay" == "wayland" ]; then
local display=":$(echo $WAYLAND_DISPLAY)"
else
local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
fi
local user=$(who | grep '('$display')' | awk '{print $1}' | head -n 1)
local uid=$(id -u $user)
sudo -u $user DISPLAY=$display DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus "$@"
}
cleanup() {
on-display notify-send -i $ICON "Disconnected" "The DSLR Camera has been turned off." --app-name="DSLR Webcam"
trap - SIGTERM && kill -- -$$
}
trap cleanup SIGINT SIGTERM EXIT
on-display notify-send -i $ICON "Connected" "The DSLR Camera has been turned on and it is ready to use." --app-name="DSLR Webcam"
on-display yad --window-icon=$ICON --image=$ICON --no-buttons --title="DSLR Webcam" --notification --listen &
output=$(v4l2-ctl --list-devices)
line=$(echo "$output" | grep "Virtual Camera")
vdevice=$(echo "$output" | sed -n "/$line/{n;s/^\t\+//p;}")
gphoto2 --stdout --capture-movie | ffmpeg -i - -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 $vdevice
O objetivo do serviço é ser iniciado por uma regra do udev quando uma câmera específica suportada pelo gphoto2 estiver conectada, portanto, tenho estas regras do udev:
ACTION=="add", ATTR{idVendor}=="04a9", ATTR{idProduct}=="3218", RUN+="systemctl start dslr-webcam.service"
ACTION=="remove", ATTR{idVendor}=="04a9", ATTR{idProduct}=="3218", RUN+="systemctl stop dslr-webcam.service"
Até agora tudo bem, porque li sobre alternativas ao uso RUN
de um serviço systemd, mas independentemente... O problema aqui é especificamente a chamada para yad
.
yad
é um programa que permite exibir caixas de diálogo da CLI, estou aproveitando-o por sua capacidade de criar um ícone do sistema, pois gostaria que aparecesse quando a câmera estiver ativa.
O problema
Ao contrário do notify-send
, que provavelmente funciona em algum soquete comum, yad
requer o XAUTHORITY
conjunto apropriado para funcionar, caso contrário você obterá o cannot open display: :0
. A solução hacky no meu caso é simplesmente definir o Xauthority correto, já que estou usando SDDM (gerenciador de exibição), ele reside no /tmp
diretório, então posso adicionar isto ao script:
XAUTHORITY=$(ls /tmp/xauth*)
E então funciona... Mas isso é terrível, faz muitas suposições, na verdade toda a on-display
função também parece uma má ideia. Se eu levasse isso para um sistema diferente, provavelmente não funcionaria, porque o Xauthority adequado poderia estar em vários lugares, e eu nem experimentei o Wayland ainda (deixarei isso para mais tarde).
A respeitoxauth
Achei que poderia de alguma forma usar xauth
para recuperar o Xauthority correto, mas não parece ser o caso... Este script é um serviço do sistema, então meu xauth info
retorno Authority file: /root/.xauthWV7OfU
, e executá-lo como o usuário certo sudo -u $user xauth info
me dá Authority file: /home/myuser/.Xauthority
, nenhum dos quais funciona quando dado a yad
. O correto XAUTHORITY
é definido pelo gerenciador de exibição, então acho que só consegui obtê-lo a partir de processos filhos dele.
Eu também tentei todas as abordagens fornecidas por esta resposta , mas a primeira não funcionaria, pois XAUTHORITY
não está realmente no ambiente do sistema, e a segunda (além das armadilhas mencionadas), não funciona, diz o O arquivo xauth está em /run/sddm/xauth_KvyuHd
, mas tentar usá-lo não funciona, portanto não é o mesmo arquivo xauth que/tmp/xauth_FzoQqz
Pela mesma pergunta anterior, essa abordagem parece funcionar, mas não sei até que ponto ela é portátil. E ainda parece hacky.
Executar como serviço de usuário
Talvez este seja o mais promissor, já que nada nesse script impede que ele seja executado como um usuário (ele também se livra de on-display
), eu tentei essa abordagem e, embora se a câmera estivesse conectada e eu executasse, systemctl --user start dslr-webcam.service
funcionaria como esperado (inclusive yad
), agora tenho um problema com a udev
regra. Pesquisei em muitos lugares, inclusive aqui , mas não consigo descobrir como executar um serviço de usuário do systemd a partir de uma udev
regra, e para mim isso também não faz sentido, como saberia udev
qual usuário usar?
Você não executa serviços de usuário diretamente a partir das regras do udev; em vez disso, você deve fazer com que o udev informe ao systemd que o dispositivo deseja que um serviço de usuário específico seja executado.
Com um
TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="dslr-webcam.service"
assim:Como você
dslr-webcam.service
requer acesso à GUI, ele deve se declarar comoBindsTo=graphical-session.target
eAfter=graphical-session.target
. Quando declarado desta forma, o serviço deve obter automaticamente as variáveis DISPLAY e XAUTHORITY corretas.A propósito, obrigado por compartilhar sua ideia; isso me deu inspiração para montar algo muito parecido...