Usando regras udev para execução de aplicativos com interface gráfica no linux.

Olá pessoal! Neste post eu pretendo explicar como executar aplicativos de interface gráfica a partir de eventos disparados por dispositivos USB ou Bluetooth, através do systemd e de regras udev, enquanto aprendemos um pouco sobre Linux no caminho. Sou bem específico ao dizer “aplicativos de interface gráfica” porque a execução deste tipo de aplicativo, no contexto do udev, não é trivial; um ótimo tutorial para a execução de scripts e comandos simples a partir do mesmo mecanismo pode ser encontrado clicando aqui.

Como exemplo de aplicação para o que queremos fazer, vou mostrar como configurei meu sistema para executar o RetroArch toda vez que o meu controle DualSense é conectado!

Esse texto ensinará:

  • O que são regras udev
  • Como criar regras udev para um dispositivo específico
  • Como executar aplicativos gráficos a partir de regras udev, com o systemd.

É esperado que você tenha um pouco de familiaridade com terminais.

udev

Nosso interesse no udev é basicamente na interface que ele dá para o sistema trabalhar com eventos relacionados a dispositivos, com a aplicação de regras. Mas para além disto, ele é um gerenciador de dispositivos que permite gerenciar suas permissões, renomeá-los, entre várias outras coisas que você checar na página do manual do udev.

The udev daemon, systemd-udevd.service(8), receives device uevents
directly from the kernel whenever a device is added or removed
from the system, or it changes its state. When udev receives a
device event, it matches its configured set of rules against
various device attributes to identify the device.
udev man page

regras udev

A cada evento que um dispositivo dispara no sistema – como uma inserção ou remoção – um certo conjunto de regras são testadas contra as propriedades do evento/dispositivo para saber se alguma ação deve ser tomada. Estas regras podem estar localizadas em /etc/udev/rules.d/ ou /usr/lib/udev/rules.d/, dependendo do nível de aplicação delas (em arquivos de mesmo nome, a execução de regras em /etc terá maior precedência)

Arquivos de regras udev nestas pastas devem ter a extensão “.rules”.

Uma regra é uma linha em um destes arquivos, que contenham expressões de “match” para as propriedades do evento/dispositivo, e expressões de atribuições a serem realizadas caso as regras sejam validadas. Aqui temos uma ótima referência para a sintaxe das regras.

criando regras udev para um dispositivo

A maior dificuldade em criar regras udev para a tomada de ações é determinar qual o melhor evento para isso, dentre aqueles relacionados ao dispositivo de seu interesse. A simples conexão ou inserção de um dispositivo pode disparar vários eventos, e achar um que seja únicamente determinado pelas suas propriedades pode ser dificíl.

Primeiro, vamos criar um log com todos eventos disparados pelo seu dispositivo de interesse, para depois vermos qual o melhor evento para criarmos nossa regra.

Isto pode ser feito a partir do comando “udevadm monitor -pk”. Após a execução do comando (que fica em foreground), insira ou remova o dispositivo, e encerre o monitoramento. Inspecionaremos este arquivo depois para criarmos nossa regra.

# o comando pode precisar de privilégios de administrador

udevadm monitor -pk > ~/udev.log

# Insira ou remova o seu dispositivo, e depois encerre o monitoramento com ctrl-c

Vejamos, no meu exemplo, os eventos e ações disparados pela conexão do DualSense no sistema (com uma ajuda do comando grep e cut para limpar a saída):

grep “^KERNEL” ~/udev.log | cut -f -2 -d ” “

KERNEL[11652.444791] add
KERNEL[11652.444895] add
KERNEL[11652.444995] add
KERNEL[11652.445204] add
KERNEL[11652.445295] add
KERNEL[11652.445421] add
KERNEL[11652.446172] bind
KERNEL[11652.454171] change

Pois é. São vários eventos e ações(add,bind,change..) diferentes. No caso, dei sorte de ter somente uma ação “bind”, e posso usá-lo junto de suas propriedades para montar minha regra. Porém, dependendo do seu dispositivo, você precisará inspecionar as propriedades de cada regra para montar uma específica o suficiente (para que sua ação não seja executada várias vezes…). Podemos inspecionar o evento de bind para ver suas propriedades com o seguinte comando (ajudado novamente pelo grep!):

grep “^KERNEL.*bind.*” -A 15 -B 1 ~/udev.log

KERNEL[11652.446172] bind     /devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0/bluetooth/hci0/hci0:21/0005:054C:0CE6.0009 (hid)
ACTION=bind
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0/bluetooth/hci0/hci0:21/0005:054C:0CE6.0009
SUBSYSTEM=hid
DRIVER=playstation
HID_ID=0005:0000054C:00000CE6
HID_NAME=Wireless Controller
HID_PHYS=fd:01:7a:bf:1f:ca
HID_UNIQ=7c:61:ef:4b:c4:b0
MODALIAS=hid:b0005g0001v0000054Cp00000CE6
SEQNUM=8533

Normalmente, procuramos por atributos únicos como modelos ou fabricantes, que podem aparecer como ID_MODEL ou ID_VENDOR, respectivamente. No meu caso, o endereço MAC do meu controle em “HID_UNIQ” e outra propriedade “DRIVER” serão usadas. Na verdade eu sequer sei se esta é a melhor escolha, mas não encontrei muita coisa que me ajudasse a montar a melhor regra. A sintaxe é bem simples, como poderemos ver em seguida.

Uma regra que podemos montar para executar um script “dualteste” na pasta home está no bloco abaixo, no arquivo “/usr/lib/udev/rules.d/99-dualsense.rules”. Note que algumas das chaves com as quais comparamos valores (DRIVER, HID_UNIQ) podem precisar ser precedidas do seu contexto, como ENV ou ATTR. O comando “udevadm info <device-path>” com o dev-path do seu dispositivo diferencia as propriedades quanto a isso.

sudo touch /usr/lib/udev/rules.d/99-dualsense.rules

cat /usr/lib/udev/rules.d/99-dualsense.rules

ACTION==”bind”, ENV{HID_UNIQ}==”7c:61:ef:4b:c4:b0″ , ENV{DRIVER}=”playstation”, RUN+=”/home/afonsolpjr/dualteste”

Com a regra criada, é só executar “sudo udevadm control -R” para recarregarmos as regras.

O script “/home/afonsolpjr/dualteste” mencionado na atribuição RUN contém o seguinte:

#!/bin/bash

echo "$(date +%F\ %T) $(whoami)" >> "/tmp/dualteste.log"

Assim, após conectar o controle, podemos ver a data e hora da conexão e o usuário que executa o comando da regra.

Bem, estamos quase lá! Já que podemos executar um script ou comandos com a regra criada, para abrirmos um aplicativo com interface gráfica parece um pulo, certo?

Só que não! A execução dos comandos pelo udev são feitas em um ambiente sem conexão nenhuma com as sessões gráficas e saídas que vemos na nossa tela. Tente você mesmo colocar uma linha com “xcalc &” ou outro aplicativo gŕafico no final do script e veja que não dará certo. 🙁

Aliás, discussões na internet desencorajam a execução de programas de longa duração pelo udev, porque parece que eles são marcados para terminarem rapidamente pelo sistema.

usando systemd para execução de aplicações gráficas

Para solucionarmos este problema e executarmos aplicações gráficas no contexto de seu usuário e da sua sessão gráfica, precisamos usar o gerenciador de serviços do Linux, o systemd.

Primeiro, crie um serviço systemd (terminando com a extensão .service) na pasta ~/.config/systemd/user/ . Para o meu propósito, eu criei um serviço chamado retroarch_controller.service.

mkdir -p ~/.config/systemd/user/

touch ~/.config/systemd/user/retroarch_controller.service

Para o nosso propósito, unidades de serviço systemd tem uma sintaxe simples. No arquivo do nosso serviço podemos colocar o seguinte, para execução do retroarch:

[Unit]
Description=Runs RetroArch on device detection

[Service]
Type=simple
ExecStart=/usr/bin/retroarch

Para carregar os arquivos de serviço e testar o que criamos, executamos:

systemctl --user daemon-reload

systemctl --user start retroarch_controller.service

E se tudo estiver funcionando, dentro do próprio systemd podemos adicionar o serviço que criamos como uma dependência para o dispositivo conectado. Os detalhes de como isto funciona podem ser encontrados aqui. A nossa nova regra ficará da seguinte forma:

# Arquivo salvo em: /usr/lib/udev/rules.d/99-dualsense.rules

ACTION=="bind", ENV{HID_UNIQ}=="7c:61:ef:4b:c4:b0" , ENV{DRIVER}="playstation", RUN+="/home/afonsolpjr/dualteste", ENV{SYSTEMD_USER_WANTS}+="retroarch_controller.service", TAG+="systemd"

Note que adicionamos as seguintes expressões de atribuições:

  • ENV{SYSTEMD_USER_WANTS}+=”retroarch_controller.service”
  • TAG+=”systemd”

E assim, após recarregar as regras com “sudo udevadm control -R”, tudo deve estar funcionando! Não testei com outros tipos de dispositivos, mais tarde testo se a conexão de outros monitores também disparam eventos no udev 🙂

Qualquer dúvida deixe seu comentário para discussão abaixo!


referẽncias:

https://man7.org/linux/man-pages/man7/udev.7.html

https://unix.stackexchange.com/questions/790915/how-to-automatically-start-a-gui-application-when-plugging-in-a-usb-device

https://www.freedesktop.org/software/systemd/man/latest/udev.html

https://www.freedesktop.org/software/systemd/man/latest/systemd.device.html


Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

To respond on your own website, enter the URL of your response which should contain a link to this post’s permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post’s URL again. (Find out more about Webmentions.)