Apagado programado con notificación para el usuario en Ubuntu 20.04

per Victor Carceler darrera modificació 2021-01-07T10:25:08+02:00

https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Gnomelogo.svg/198px-Gnomelogo.svg.pngEn el instituto los ordenadores de las aulas y diferentes espacios están programado para apagarse 5 minutos después de que acaben las clases de la mañana y de la tarde. Esta función, que se implementa con un par de tareas en cron, evita que se queden encendidos de forma innecesaria.

Pero en alguna ocasión provoca disgustos cuando el usuario se encuentra de forma inesperada con el apagado de la máquina.

Ahora que estoy preparando los playbooks de Ansible para utilizar con Ubuntu 20.04 se me ocurre que puede ser conveniente advertir al usuario con una notificación de Gnome sobre el inminente apagado para que guarde su trabajo o se prepare para disfrutar del shutdown :-)

Notificaciones en GNOME y comando notify-send

El escritorio Gnome tiene un elegante mecanismo de notificaciones al que se puede acceder desde la línea de comandos con la herramienta notify-send. El manual advierte de que la notificación que se presenta al usuario puede tener:

  • summary: un texto corto.
  • body: el cuerpo multilínea de la notificación que puede incluir cierto código markup codificado en UTF-8.
  • urgency: Un valor entre low, normal y critical. Por lo que he experimentado si se utiliza critical la notificación permanece en pantalla hasta que el usuario la cierra.

La especificación está disponible en: http://www.galago-project.org/specs/notification/

Así un usuario de Gnome puede abrir un terminal y probar el siguiente comando:

VirtualBox_u1_22_05_2020_11_29_48.png

Pero no es posible enviar notificaciones desde una tarea periódica en crontab o al menos no lo es de manea inmediata. Para que el comando notify-send pueda enviar la notificación al usuario es necesario:

  • Ser el mismo usuario (lo podemos conseguir con sudo -s -u <usuario>)
  • Y tener definida la variable de entorno XDG_RUNTIME_DIR apuntando al directorio correcto con el UID del usuario, por ejemplo, /run/user/1000.

Esto se debe al funcionamiento de D-Bus que se utiliza como mecanismo de comunicación.

Así que para lanzar desde crontab notificaciones tendremos que escribir algún script que:

  • Descubre las sesiones abiertas
  • Toma nota del UID de cada usuario con sesión abierta
  • Para cada UID se utiliza sudo, se exporta la variable de entorno XDG_RUNTIME_DIR y finalmente se ejecuta notify-send.

Posible pero tedioso :-(

https://upload.wikimedia.org/wikipedia/commons/3/33/Systemd-logo.svgUna solución más sencilla: timers por usuario de systemd

Los timers son unidades de systemd que permiten lanzar servicios de manera periódica como hace el tradicional cron. La documentación de Arch Linux sobre los timers de systemd es especialmente concisa: https://wiki.archlinux.org/index.php/Systemd/Timers.

Una característica muy importante es que se pueden declarar timers por usuario que únicamente se activan cuando el usuario tiene una sesión abierta. Por ejemplo, de esta manera se invoca a zsysctl en Ubuntu 20.04 para realizar snapshots cuando el usuario inicia sesión y después cada hora.

En nuestro caso los ordenadores se apagan a las 14:55. Si queremos avisar a los usuarios que tengan una sesión abierta 5 minutos y 1 minuto antes del apagado deberemos:

  1. Definir el fichero /usr/lib/systemd/user/notifica-apagado-1455.timer con el siguiente contenido:
    [Unit]
    Description=Notifica al usuario el apagado a las 14:55
    
    [Timer]
    OnCalendar=14:50
    OnCalendar=14:54
    
    [Install]
    WantedBy=timers.target
  2. Definir el fichero /usr/lib/systemd/user/notifica-apagado-1455.service con el siguiente contenido:
    [Unit]
    Description=Notifica al usuario el apagado a las 14:55
    
    [Service]
    Type=oneshot
    ExecStart=/usr/bin/notify-send -u critical "Apagat programat a les 14:55" "L'ordinador està programat per apagar-se a les 14:55. Guardi tot el seu treball y tanqui les aplicacions obertes."
  3. Enlazar el timer en /etc/systemd/user/timers.target.wants/ para que esté activo:
    root@ubuntu2004:/etc/systemd/user/timers.target.wants# ln -s /usr/lib/systemd/user/notifica-apagado-1455.timer notifica-apagado-1455.timer
    root@ubuntu2004:/etc/systemd/user/timers.target.wants# ll
    total 12
    drwxr-xr-x 2 root root  4 de maig  22 14:18 ./
    drwxr-xr-x 5 root root  5 d’abr.   23 09:40 ../
    lrwxrwxrwx 1 root root 49 de maig  22 14:18 notifica-apagado-1455.timer -> /usr/lib/systemd/user/notifica-apagado-1455.timer
    lrwxrwxrwx 1 root root 47 de maig  21 12:41 zsys-user-savestate.timer -> /usr/lib/systemd/user/zsys-user-savestate.timer
    root@ubuntu2004:/etc/systemd/user/timers.target.wants#

La instalación de este sistema en el centro se realizará con Ansible copiando las unidades a las máquinas controladas.

Resulta interesante observar que la herramienta systemd-analyze calendar permite interpretar el valor especificado en OnCalendar.

usuario@laika:~$ systemd-analyze calendar 14:54
  Original form: 14:54                       
Normalized form: *-*-* 14:54:00              
    Next elapse: Sat 2020-05-23 14:54:00 CEST
       (in UTC): Sat 2020-05-23 12:54:00 UTC 
       From now: 23h left                    
usuario@laika:~$

Y el comando systemctl list-timers --user que muestra información sobre los timers de usuario.

root@stallman-181:~# systemctl list-timers --user 
NEXT                         LEFT       LAST                         PASSED       UNIT                        ACTIVATES                    
Sat 2020-05-23 14:50:00 CEST 33min left n/a                          n/a          notifica-apagado-1455.timer notifica-apagado-1455.service
Sat 2020-05-23 15:13:19 CEST 57min left Sat 2020-05-23 14:13:19 CEST 2min 52s ago zsys-user-savestate.timer   zsys-user-savestate.service  
Sat 2020-05-23 21:25:00 CEST 7h left    n/a                          n/a          notifica-apagado-2130.timer notifica-apagado-2130.service

3 timers listed.
Pass --all to see loaded but inactive timers, too.
root@stallman-181:~#

Más información: