Como lidar com eventos ACPI no Linux
ACPI é a sigla para Configuração Avançada e Interface de Energia; como padrão, foi implementado pela primeira vez no ano de 1996, como sucessor do APM (Advanced Power Management). Como principal característica, trouxe a capacidade de lidar com o gerenciamento de energia no nível do sistema operacional, enquanto antes era feito no BIOS. Alguns eventos ACPI no Linux são, por padrão, tratados via systemd-logind, mas configurações mais complexas podem ser obtidas instalando e executando o serviço acpid.
Neste artigo, veremos como configurar o systemd-logind e como lidar com eventos ACPI mais específicos por meio do daemon acpid.
Neste tutorial você aprenderá:
Como alguns eventos ACPI são tratados pelo systemd-logind
Como instalar e iniciar o serviço acpid
-
Como lidar com eventos ACPI
Eventos ACPI tratados pelo systemd-logind
Nos últimos anos, o systemd se tornou o sistema init padrão no Linux. Após algumas controvérsias, praticamente todas as principais distribuições o adotaram, exceto algumas que existem explicitamente para evitá-lo, como o Devuan, um derivado do Debian. Uma das principais críticas feitas ao Systemd é que ele não respeita a filosofia Unix de “fazer uma coisa e fazê-la bem”. Com o tempo, de fato, o Systemd foi estendido para executar outras tarefas além de simplesmente manipular a inicialização do sistema, como gerenciar logins de usuários: isso é feito através do serviço systemd-login
d.
O serviço systemd-logind, entre outras coisas, é responsável por criar e gerenciar IDs de sessões, fornecer acesso polkit a operações em nível de sistema, como desligamento ou suspensão, e lidar com eventos ACPI, como o comportamento do botão liga/desliga. Quais ações devem ser executadas quando tais eventos ocorrem podem ser configuradas editando o arquivo /etc/systemd/logind.conf
. Vamos dar uma olhada em seu conteúdo:
[Login]
[...]
#HandlePowerKey=poweroff
#HandleSuspendKey=suspend
#HandleHibernateKey=hibernate
#HandleLidSwitch=suspend
#HandleLidSwitchExternalPower=suspend
#HandleLidSwitchDocked=ignore
#HandleRebootKey=reboot
[...]
Truncamos a saída do arquivo por conveniência, para mostrar as linhas mais relevantes. Por padrão, todas as linhas do arquivo são comentadas e relatadas com os valores padrão. As diretivas relatadas controlam como o systemd-logind lida com as teclas de energia, suspensão, hibernação e chaveamento da tampa nas máquinas quando esse evento pode ocorrer, como notebooks. Alguns dos valores que podem ser atribuídos às diretivas são:
ignorar
desligar
reinício
suspender
hibernar
-
sono híbrido
A sintaxe a ser usada no arquivo é bastante autoexplicativa: com a linha HandlePowerKey=poweroff
, por exemplo, instruímos o systemd-logind para desligar a máquina quando o botão liga/desliga for pressionado e suspender o sistema para RAM quando a tecla suspender é pressionada (HandleSuspendKey=suspend
). Da mesma forma, podemos fazer com que um evento específico seja ignorado usando ignore
como valor da linha correspondente, como por padrão é feito para HandleLidSwitchDocked
.
Algumas das ações que podem ser realizadas pelo systemd-logind, nos ambientes desktop mais utilizados, são gerenciadas por gerenciadores gráficos de energia. Para evitar conflitos ou ações redundantes, como o sistema ser suspenso duas vezes quando o botão suspender é pressionado, tais aplicativos inibem o tratamento de tais eventos pelo systemd-logind.
Se precisarmos gerenciar eventos acpi não cobertos pelo systemd-logind, com ações mais complexas ou personalizadas, precisamos mudar de estratégia, instalar e executar o daemon acpid
. Vamos ver como proceder.
Instalando o daemon acpid
Instalar o daemon acpid é muito fácil, pois o pacote relacionado está incluído nos repositórios das principais distribuições Linux. Para instalar o pacote no Fedora, por exemplo, executamos o seguinte comando:
$ sudo dnf install acpid
Para instalar o mesmo pacote no Archlinux, executamos:
$ sudo pacman -Sy acpid
Finalmente, em distribuições Debian e baseadas em Debian, para instalar o pacote acpid usamos o wrapper apt
:
$ sudo apt install acpid
Após a conclusão da instalação, precisamos iniciar o serviço acpid e habilitá-lo na inicialização, para que ele seja iniciado automaticamente sempre que iniciarmos nosso sistema. Podemos realizar ambas as ações com um único comando:
$ sudo systemctl enable --now acpid
Tratamento de eventos ACPI
Com o serviço acpid em execução, quando precisamos configurar uma ação a ser executada em um evento específico, a primeira coisa que precisamos fazer é verificar como esse evento é reconhecido pelo sistema (o mesmo evento pode ser reconhecido em maneiras diferentes em máquinas diferentes: isso depende de como a ACPI é implementada). O utilitário acpi_listen
foi projetado para esse propósito específico e é instalado como parte do pacote acpid, portanto está pronto para uso.
Para identificar um evento precisamos lançar o utilitário em um emulador de terminal, acionar o evento e ver como ele é lido pela ACPI. Vejamos um exemplo prático. Suponha que queiramos realizar uma ação em um laptop quando a tecla Fn
for pressionada junto com a tecla F3
. Primeiro, invocamos acpi_listen
:
$ acpi_listen
Deveríamos obter um cursor piscando; isso significa que o sistema está aguardando e pronto para exibir como os eventos são lidos. Agora podemos pressionar a combinação de teclas. Imediatamente, as informações são impressas na tela. Neste caso, na máquina que estou utilizando, obtenho o seguinte resultado:
hotkey ATK0100:00 00000050 00000001
Na saída, podemos ver quatro colunas: a primeira representa a classe do dispositivo, a segunda é o nome do kernel do “dispositivo”, conforme aparece abaixo do Diretório /sys/bus/acpi/devices
. A terceira coluna relata o código do evento. Finalmente, o valor da quarta coluna assume um significado diferente dependendo do tipo de evento. Depois de conhecermos um código de evento, podemos associar uma ação a ele.
Associando um evento a uma ação
Associar um evento ACPI a uma ação é bastante fácil. Basicamente envolve dois arquivos: o primeiro é aquele analisado pelo serviço acpid (por padrão deve ser criado dentro do diretório /etc/acpi/events
) e é onde especificamos o código do evento a ser manipulado e o caminho do script que contém os comandos que devem ser executados; o segundo é o próprio script. O local onde os scripts são colocados no sistema de arquivos varia dependendo da distribuição. No Fedora, por exemplo, temos os seguintes diretórios por padrão:
├── actions
│ └── power.sh
└── events
├── powerconf
└── videoconf
Como já dissemos, o diretório /etc/acpi/events
contém os arquivos que tratam dos eventos. O diretório /etc/acpi/actions
, em vez disso, contém os scripts associados aos eventos. Por padrão, como você pode ver, os scripts powerconf
e power.sh
são instalados e gerenciam o manuseio do botão liga/desliga.
No Debian, os arquivos são organizados de forma diferente. O diretório /etc/acpi/actions
não existe:
/etc/acpi
├── events
│ └── powerbtn-acpi-support
└── powerbtn-acpi-support.sh
No Archlinux as coisas são tratadas de forma diferente: existe apenas um arquivo por padrão, no qual todos os eventos são tratados: /etc/acpi/events/anything
:
# Pass all events to our one handler script
event=.*
action=/etc/acpi/handler.sh %e
O que faz o arquivo tratar todos os eventos é a expressão regular .*
. O código dos eventos é então passado como argumento para o arquivo /etc/acpi/handler.sh
(a string %e
será substituída pelo código do evento). No script, as ações a serem executadas são selecionadas em função desta, em uma instrução case.
A forma como os arquivos são organizados, entretanto, não importa muito. Isso não muda a forma como as coisas funcionam. Vamos gerenciar o evento que falamos anteriormente: como não existe tecla de hibernação na máquina que estou usando, farei com que quando a combinação de teclas Fn+f3
for pressionada, o sistema seja colocado em hibernação. Primeiramente vamos criar o arquivo de evento, vamos chamá-lo de hibernateconf
; conterá apenas duas linhas:
event=hotkey ATK0100:00 00000050
action=/etc/acpi/actions/hibernate.sh
Na primeira linha especificamos o código do evento como valor de event: neste caso simplesmente usamos o código literal que descobrimos usando o utilitário acpi_listen. Na segunda linha especificamos o caminho do script que deve ser executado: /etc/acpi/actions/hibernate.sh
.
Uma coisa importante a lembrar é que se estiver usando SELinux (no Fedora ele está ativo por padrão), devemos ter certeza de que o arquivo de “evento” possui o contexto apropriado associado a ele, caso contrário o evento não será tratado. Para saber qual contexto deve ser aplicado ao arquivo, podemos dar uma olhada nos arquivos existentes no diretório /etc/acpi/events
como referência. Tudo o que precisamos fazer é executar ls
com a opção -Z
:
$ ls -lZ /etc/acpi/events
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 168 Oct 5 21:27 powerconf
-rw-r--r--. 1 root root system_u:object_r:etc_t:s0 236 Oct 5 21:27 videoconf
Como podemos ver, no Fedora, os dois arquivos existentes no diretório /etc/acpi/events possuem o contexto system_u:object_r:etc_t
. Para aplicar o mesmo contexto ao nosso arquivo hibernateconf
, podemos usá-los como referência com o comando chcon
:
$ sudo chcon --reference /etc/acpi/events/powerconf /etc/acpi/events/hibernateconf
O script invocado pelo arquivo de evento conterá as seguintes linhas:
#!/bin/bash
systemctl hibernate
No script, simplesmente usamos systemctl
para hibernar o sistema. Tudo o que resta fazer é tornar o script executável:
$ sudo chmod +x /etc/acpi/actions/hibernate.sh
Para que nossas novas configurações entrem em vigor, precisamos reiniciar o daemon acpid:
$ sudo systemctl restart acpid.service
Quando pressionamos Fn+F3
, o sistema agora deve estar hibernado.
Conclusões
Neste artigo vimos como os eventos ACPI podem ser tratados no Linux. Vimos como eventos básicos como pressionar o botão liga/desliga, a tecla de suspensão e o fechamento da tampa de um laptop são gerenciados pelo systemd-logind e podem ser configurados por meio do arquivo /etc/systemd/logind.conf. Também vimos como lidar com eventos personalizados instalando o daemon acpid em algumas das distribuições Linux mais usadas.