4.1.2.5. Personalización

Ya se ha explicado cómo consultar, arrancar y parar servicios y cómo funcionan los niveles de ejecución en systemd. Con esto es suficiente para manipular de modo básico los servicios. Sin embargo, en algunas ocasiones, es necesario manipular la manera en la que arrancan éstos (p.e. añadiendo o eliminando una opción a la orden que crea el proceso) o, incluso, crear lanzadores de un determinado servicio para systemd.

Bajo el presente epígrafe se describirá superficialmente cómo está organizado systemd, cuál es la estructura de los archivos que permiten arrancar y parar los servicios y cómo pueden modificarse éstos para modificar tales acciones.

4.1.2.5.1. Preliminares

Es muy importante tener presente que un servicio no es más que un proceso que actúa en segundo plano. Consecuentemente, tal servicio debió originarse cómo consecuencia de la invocación de una orden. Por ejemplo, el servicio ssh se crea mediante la ejecución del programa sshd, así que si nos aseguramos de que el servicio no está corriendo[1], podemos levantar el servidor directamente del siguiente modo:

# /usr/sbin/sshd -D

La terminal en este caso quedará ocupada y desde otras máquinas podremos acceder al servidor. Sin embargo, este manera de proceder sólo es útil cuando nos interesa ver mensajes de salida que pueda proporcionar el servidor en caso de depuración. Arrancar un servicio de forma adecuada exige más: controlar si el servicio está arrancado o no, hacerlo automáticamente, etc. Por este motivo, los servicios no se arrancar directamente, sino que el proceso init (en nuestro caso particular, systemd) se encarga de ello.

4.1.2.5.2. Estructura

Cada servicio[2] dispone de una unidad de systemd que describe los requisitos, el ejecutable y los parámetros con que tiene que arrancarse.

En Debian los archivos que representan las unidades se incluyen dentro del directorio /lib/systemd/system. Por ejemplo, para el servicio SSH la instalación del paquete openssh-server proporciona un archivo llamado ssh.service. En caso de que se desee hacer algún cambio, es mejor no tocarlos, ya que dentro del directorio /etc/systemd/system se pueden incluir archivos homónimos que sobrescriben el archivo instalado por la distribución.

A partir de ahora nombraremos como unidad al archivo que representa a la unidad. Por tanto, tendremos un archivo proporcionado por la distribución que es /lib/systemd/system/unidad que puede ser remplazado por el archivo /etc/systemd/system/unidad.

Además de estos archivos, que representan la unidad completa, hay una serie de subdirectorios dentro de /etc/systemd/system/unidad asociados también a ella:

  • unidad.d, que permite incluir archivos con directivas adicionales al archivo de configuración principal. Si una directiva también existe en el archivo principal, el valor no se sobreescribirá, sino que pasará a tener ambos valores, lo cual puede ser lícito o no. Para sobreescribir el valor es necesario incluirla primero con valor vacío y luego con el nuevo calor:

    [Service]
    ExecStart =
    ExecStart = /usr/sbin/xxxx -p
    
  • unidad.wants, que contiene enlaces simbólicos a unidades de las que depende opcionalmente la unidad.

  • unidad.requires, que contiene enlaces simbólicos a unidades de las que depende forzosamente la unidad.

Por tanto, la configuración de la unidad que procesa systemd, procede de diversas fuentes y, si presenta distintas, a priori es posible que no tengamos muy claro cuál es el valor de un determinada directiva de configuración. Para asegurarnos de cuál es la configuración final podemos optar por:

$ systemctl cat ssh.service

que mostrará las directivas contenidas en todos los archivos involucrados en la configuración del servicio, indicando a qué archivo pertenece cada una, o bien:

$ systemctl show ssh.service

que nos devolverá la configuración resultante para el servicio ssh que incluye también las directivas que no se han escrito pero poseen valor predeterminado. Como es larga y puede que nos interese sólo una directiva en particular, podemos añadir la opción -p:

$ systemctl show -p "Wants" ssh.service
Wants=systemd-journald.socket systemd-journald-dev-log.socket system.slice

Nota

Sabiendo cuál es la estructura de archivos, podemos crear y editar archivos a mano, pero para ayudarnos en la labor existe systemctl edit.

4.1.2.5.3. Archivos de unidad

4.1.2.5.3.1. Descripción

Nota

El epígrafe es un descripción muy somera. Para una en mayor profundidad puede consultarse este artículo en inglés.

Los archivos de configuración de las unidades son un conjunto de parejas parámetro-valor agrupadas en distintas secciones:

[Seccion1]
Parametro1=Valor1
# [... otras directivas de esta sección ...]

[Sección2]
Parametro2=Valoe2
# [... otras directivas de esta sección ...]

Las secciones[3] posibles son:

[Unit]

Es una sección para describir la propia unidad y su relación con las restantes. Suele situarse al principio del archivo. Algunos de las directivas que se pueden definir en ella son:

Description

Permite describir brevemente para qué sirve la unidad. Esta descripción, por ejemplo, es la que aparece para cada unidad al ejecutar:

$ systemctl list-units
Documentation

Permite indicar URIs donde encontrar información sobre el servicio relacionado con la unidad.

Wants

Lista otras unidades necesarias para el servicio, aunque no indispensables. Cumple la misma función que el directorio unidad.wants.

Requires

Lista otras unidades indispensables para el servicio. Si alguna de ellas falla, esta unidad también lo hará. Cumple la misma función que el directorio unidad.requires

After

Lista las unidades que deberán estar cargadas cuando se lanza la unidad. Sólo implica orden y no requisito.

Before

Lista las unidades que requiren cargada la unidad representada por el archivo cuando sean lanzadas.

Condition*

Familia de directivas que permite indicar condiciones que deben cumplirse para lanzar la unidad. Dependiendo de qué se compruebe, así es el hombre exacto de la directiva. Por ejemplo, ConditionPathExists comprueba si existe la ruta que se indica como valor. Para saber el nombre del resto de directivas de esta familia puede consultarse:

$ man systemd.unit
[Install]

Contiene directivas relacionadas con acciones que deben realizarse al habilitar o deshabilitar la unidad. Suele incluirse al final del archivo. Algunas directivas interesantes son:

WantedBy

Permite indicar las unidades que requieren la desarrollada en el archivo. Que una unidad aparezca aquí (llamémosla otraunidad):

[Install]
WantedBy=otraunidad

implica que, al habilitarse la unidad del archivo, se creará en el directorio /etc/systemd/system/otraunidad.wantsi/ un enlace simbólico a la unidad del archivo, a fin de que se registre la dependencia y Systemd la conozca al arrancar los servicios.

Esta directiva es la complementaria a Wants de la sección Unit, de manera que esta directiva en el archivo unidad provoca el mismo efecto que esto otro en el archivo otraunidad:

[Unit]
Wants=unidad

En cambio, la primera directiva permite mayor versatilidad. Por ejemplo, ssh.service es un servicio que se levanta en el modo multi-user.target El problema es que muchos otros servicios también se levantan en este modo (un servidor FTP o un web, por ejemplo). De la primera forma, no hay más que individualmente en la unidad de cada servicio indicar la dependencia de multi-user.target. En cambio, de la segunda forma, deberíamos tener presentes todos esos servicios al escribir la directiva Wants= en la unidad de multi-user.target.

RequiredBy

Análoga a WantedBy, pero el requisito es indispensable.

Alias

Permite indicar un nombre alternativo para el servicio.

[Service]

Es una sección que incluye directivas exclusivas de unidades que son servicios. Permite muchas directivas:

Type

El tipo de servicio. Son diversos. Consúltese la página recomendada para más información.

EnvironmentFile

Indica un archivo que define variables que pueden usarse en las directivas de esta sección. Consúltese el ejemplo posterior.

ExecStart

Orden que arranca el servicio.

ExecStartPre

Expresa comandos que deben realizarse antes de la orden que arranca el servicio.

ExecStartPost

Expresa comandos que deben ejecutarse tras haber arrancado el servicio.

ExecStop

Orden para parar el servicio. Si no se indica ninguna, lo que se hace es matar el proceso al parar el servicio.

ExecStopPost

Expresa comandos a ejecutar después de parar el servicio.

ExecReload

Orden que permite recargar la configuración.

Restart

Expresa la circunstancia bajo la cual se reiniciará el servicio. Hay varias distintas.

TimeoutSec

Temporización en segundos después de la cual se marcará como fallado el arranque de un servicio o se forzará su parada.

[Socket]

Directivas para unidades socket

[Mount]

Directivas de unidades mount que gestionan puntos de montaje. Estas unidades (y las citadas a continuación= cumplen la función del archivo tradicional /etc/fstab. No obstante, systemd tiene un traductor que al vuelo traduce el contenido de este archivo a estas unidades, por lo que es perfectamente funcional seguir usando este archivo.

[Automount]

Directivas para las unidades automount que permiten montar automáticamente durante el arranque los dispositivos expresados en unidades mount.

[Swap]

Directivas para unidades swap que permiten configurar la memoria de intercambio. Pero esto también sigue siendo posible a través del archivo /etc/fstab.

[Path]

Directivas para unidades path que sirven para definir rutas que se desean monitorizar mediante systemd. Puede consultar este enlace <http://www.ocsmag.com/2015/09/02/monitoring-file-access-for-dummies/> para ver cómo se definen y funcionan.

[Timer]

Directivas para unidades timer cuya utilidad es la misma que la de los demonios cron y at. Véase Planificación con SystemD.

4.1.2.5.3.2. Análisis

Expuesto todo lo necesario para manipular de forma muy simple la gestión a través de systemd, lo más adecuado es analizar en profundidad un servicio. Tomemos a este efecto ssh.service. El archivo de unidad proporcionado por el paquete openssh-server se encuentra en /lib/systemd/system y es el siguiente:

 1[Unit]
 2Description=OpenBSD Secure Shell server
 3After=network.target auditd.service
 4ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
 5
 6[Service]
 7EnvironmentFile=-/etc/default/ssh
 8ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
 9ExecReload=/bin/kill -HUP $MAINPID
10KillMode=process
11Restart=on-failure
12RestartPreventExitStatus=255
13Type=notify
14
15[Install]
16WantedBy=multi-user.target
17Alias=sshd.service

En la sección [Unit] vemos la directiva Description, que describe qué servicio gestiona la unidad (OpenBSD Secure Shell server) y que aparece regularmente en las salidas de systemctl. Por ejemplo:

$ systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
[...]

La directiva After obliga a que esta unidad levante el servicio después de que se hayan activado por completo las unidades network.target y auditd.service. Además, se incluye con la directiva ConditionPathExists para que el servicio no arranque en caso de que se haya creado el archivo /etc/ssh/sshd_not_to_be_run.

En la sección [Service] encontramos la directiva EnvironmentFile que expresa un archivo en que se definen variables de ambiente: /etC/default/ssh:

# Default settings for openssh-server. This file is sourced by /bin/sh from
# /etc/init.d/ssh.

# Options to pass to sshd
SSHD_OPTS=

La ubicación del archivo no es caprichosa. Tradicionalmente con SystemV, los archivos de configuración para los scripts de arranque los almacenaba Debian bajo /etc/default. Estos son archivos en que se incluyen variables de entorno que tales scripts utilizan. Una variable muy comúnmente usada es la que define las opciones con las que arrancará el ejecutable que proporciona el servicio, como es el caso del ejemplo (variable $SSHD_OPTS que se usa luego en la directiva ExecStart). Con systemd se puede mantener esta filosofía gracias a la existencia de la directiva EnvironmentFile.

Por último, está la sección [Install] que se aplica al habilitar o deshabilitar la unidad. Contiene dos directivas:

WantedBy=multi-user.target

que indica que el modo de operación multi-user implica arrancar el servidor SSH y:

Alias=sshd.service

que provoca que el servicio también pueda llamarse sshd.service. Estas dos directivas provocan cambios en el sistema al habilitarse la unidad. La primera de las directivas provoca que aparezca un enlace simbólico bajo /etc/systemd/system/multi-user.target:

$ readlink /etc/systemd/system/multi-user.target.wants/ssh.service
/lib/systemd/system/ssh.service

La segunda que en /etc/systemd/system aparezca un enlace simbólico con nombre sshd.service que apunte hacia ssh.service:

$ readlink /etc/systemd/system/sshd.service
/lib/systemd/system/ssh.service

De hecho, si deshabilitamos la unidad veremos que tales enlaces simbólicos desaparacen.

4.1.2.5.3.3. Edición

Advertencia

Este apartado se refiere únicamente a alterar la configuración con la que systemd lanza el servicio. Cada servicio, a su vez, tendrá muy probablemente un archivo (o varios) de configuración particular (p.e. ssh tiene /etc/ssh/sshd_config). Si lo que se pretende es alterar esta configuración, basta con alterar tal archivo y reiniciar el servicio:

# systemctl reload-or-restart ssh.service

Si pretendemos modificar la configuración de arranque de un servicio, podemos optar por lo siguiente:

  1. Reescribir por completo el archivo original dentro de /etc/systemd/system/, para lo cual em principio deberíamos hacer algo así:

    # cp /lib/systemd/system/ssh.service /etc/systemd/system
    # vim /etc/systemd/system/ssh.service
    

    Sin embargo, el propio systemctl proporciona una herramienta que automatiza esto:

    # systemctl edit --full ssh.service
    

    que se encargará de tomar una copia del contenido original de la unidad (el situado dentro de /lib/systemd/system) y permitir su edición con el editor predefinido. El resultado se guardará dentro de /etc/systemd/system.

  2. Redefinir sólo algunas directivas, para lo cual podríamos obrar a mano:

    # mkdir -p /etc/systemd/system/ssh.service.d
    # cat > /etc/systemd/system/ssh.service.d/override.conf
    [Unit]
    Description=Servidor OpenSSH
    
    # systemctl stop ssh.service
    # systemctl daemon-reload
    # systemctl start ssh.service
    

    que requiere también para el servicio, hacerle saber a systemd que hemos cambiado la unidad, e iniciarlo otra vez. En vez de todo ello, es infinitamente más cómodo (y seguro) utilizar systemctl:

    # systemctl edit ssh.service
    

    Advertencia

    Al editar observe que aparece comentada la configuración del archivo con la configuración completa. No descomente y modifique, porque no funcionará: observe que todas esas líneas están la leyenda "Lines below this comment will be discarded".

    Es posible conocer los archivos que parchean configuraciones del archivos principales:

    $ systemd-delta --type extended
    

Notas al pie