Escribir un servicio que depende de Xorg

30

Estoy tratando de escribir un servicio de nivel de usuario redshifty necesita esperar hasta que Xorg esté en funcionamiento. Mi archivo de servicio actual se ve así:

[Unit]
Description=Redshift
After=graphical.target

[Service]
Environment=DISPLAY=:0
ExecStart=/bin/redshift -l 28:-13 -t 5300:3300 -b 0.80:0.91 -m randr
Restart=always

[Install]
WantedBy=default.target

Sin embargo, parece que intenta iniciarse antes de que Xorg esté activo, y luego tengo que iniciar manualmente el servicio. Supongo que estoy usando el After=objetivo equivocado . ¿Alguna pista?

mkaito
fuente

Respuestas:

20

He estado investigando esto y la respuesta de Grawity parece desactualizada. Ahora puede configurar servicios de usuario con systemd que se ejecutan como parte de la sesión del usuario. Pueden tener DISPLAY y XAUTHORITY establecidos (actualmente en Arch y Debian Stretch).

Esto tiene sentido sobre las recomendaciones anteriores de usar archivos de inicio automático de escritorio, ya que obtiene la administración de procesos como lo haría con una aplicación de nivel de sistema (reinicio, etc.).

Los mejores documentos en este momento son Arch wiki; Systemd / Usuario

Versión TLDR;

  1. Cree el archivo * .service deseado en ~/.config/systemd/user/
  2. Ejecutar systemctl --user enable [service](excluir sufijo .service)
  3. Opcionalmente, ejecute systemctl --user start [service]para comenzar ahora
  4. Úselo systemctl --user status [service]para ver cómo le va

Un par de otros comandos útiles.

  • systemctl --user list-unit-files - ver todas las unidades de usuario
  • s ystemctl --user daemon-reload: si edita un archivo .service

-- Luego...

Actualicé y convertí la mayoría de mis daemons de sesión en archivos systemd .service. Entonces puedo agregar un par de notas adicionales.

No había un enlace predeterminado para ejecutar los servicios al iniciar sesión, por lo que debe activarlo usted mismo. Lo hago desde mi archivo ~ / .xsession.

systemctl --user import-environment PATH DBUS_SESSION_BUS_ADDRESS
systemctl --no-block --user start xsession.target

La primera línea importa algunas variables de entorno en la sesión del usuario systemd y la segunda inicia el objetivo. Mi archivo xsession.target;

[Unit]
Description=Xsession running
BindsTo=graphical-session.target

Mi xbindkeys.service como ejemplo.

[Unit]
Description=xbindkeys
PartOf=graphical-session.target

[Service]
ExecStart=/usr/bin/xbindkeys -n -f ${HOME}/projects/dotfiles/.xbindkeysrc
Restart=always

[Install]
WantedBy=xsession.target
John Eikenberry
fuente
2
Si puede proporcionar un archivo de unidad de ejemplo y explicar cómo hacer que la unidad pueda usar DISPLAY y XAUTHORITY, me complacerá cambiar la respuesta aceptada.
mkaito
@mkaito Lo investigaré una vez que Debian libere Stretch. Estoy ejecutando Debian estable y estaba esperando hasta entonces para jugar más.
John Eikenberry
@mkaito En github.com/systemd/systemd/blob/v219/NEWS#L194 dice "Ahora se envía un scriptlet de sesión X11 que carga $ DISPLAY y $ XAUTHORITY en el entorno del systemd --user daemon si comienza una sesión. Esto debería mejorar la compatibilidad con las aplicaciones habilitadas para X11 que se ejecutan como servicios de usuario systemd ".
josch
Todavía me gustaría ver un archivo de unidad de ejemplo, solo para aclarar si se necesita algo especial.
mkaito
11

La pista habitual es "no". redshiftno es un servicio de todo el sistema: tendría una instancia separada para cada sesión y necesita saber cómo conectarse al Xorg de esa sesión específica.

(Xorg tampoco es un servicio del sistema, solo el administrador de pantalla lo es, y también lanza un Xorg separado para cada sesión. // graphical.targetle indicará cuándo está listo el administrador de pantalla, pero no dice nada sobre cuándo el DM realmente inicia el primera - o todas - pantallas).

Simplemente iniciarlo en el arranque DISPLAY=:0no es suficiente, ya que no hay garantía de que haya exactamente una pantalla en un momento dado, ni de que siempre sea así :0(por ejemplo, si Xorg se bloquea dejando un archivo de bloqueo obsoleto, la siguiente se ejecutará :1como pensaría que :0todavía está ocupado); también necesita establecer la ruta a su XAUTHORITYarchivo ya que X11 requiere autenticación; y asegúrese de que redshiftse reinicie si alguna vez cierra sesión y vuelve a iniciar sesión.

Entonces, ¿cómo comenzarlo? Casi siempre, el entorno de escritorio tiene varios métodos para iniciar sus propios servicios de sesión . Vea una publicación anterior que ya describe las dos habituales; El ~/.xprofileguión y la ~/.config/autostart/*.desktopubicación.

Si usa startx , puede usar ~/.xinitrcpara comenzar tales cosas. Los administradores de ventanas independientes a menudo tienen sus propios scripts de inicio / inicio; por ejemplo ~/.config/openbox/autostartpara Openbox.

Lo que es común a todos estos métodos es que el programa se inicia desde dentro de la sesión - evitando todos los problemas mencionados anteriormente.

Gravedad
fuente
Si bien el desplazamiento hacia el rojo no es un servicio de todo el sistema, en muchos casos tiene sentido ser un servicio de usuario, que es exactamente lo que el OP está tratando de hacer.
simotek
5

Esto es lo que acabo de crear como solución alternativa a lo que aún no está disponible graphical-session.target(en mi sistema Kubuntu 16.04):

  1. Cree una unidad de usuario pseudo systemd que suba y baje el archivo graphical-session.target.

Crea ~/.config/systemd/user/xsession.targetcon los siguientes contenidos:

[Unidad]
Descripción = Xsession en funcionamiento
BindsTo = graphical-session.target

Dígale a systemd sobre esta nueva unidad:

$> systemctl --user daemon-reload
  1. Cree scripts de inicio automático y apagado que controlen a xsession.targettravés de la mecánica actualmente disponible del escritorio Ubuntu 16.04.

Crea ~/.config/autostart-scripts/xsession.target-login.shcon los siguientes contenidos:

#! / bin / bash

Si ! systemctl --user is-active xsession.target &> / dev / null
luego
  / bin / systemctl --user import-environment DISPLAY XAUTHORITY
  / bin / systemctl --user start xsession.target
fi

Crea ~/.config/plasma-workspace/shutdown/xsession.target-logout.shcon los siguientes contenidos:

#! / bin / bash

if systemctl --user is-active xsession.target &> / dev / null
luego
  / bin / systemctl --user stop xsession.target
fi

Haga que los scripts sean ejecutables:

$> chmod + x ~ / .config / autostart-scripts / xsession.target-login.sh
$> chmod + x ~ / .config / plasma-workspace / shutdown / xsession.target-logout.sh

Nota: estos dos archivos se colocan donde KDE los recogerá para el inicio automático y el apagado. Los archivos pueden colocarse en otro lugar para otros entornos de escritorio (por ejemplo, Gnome), pero no sé acerca de esos entornos.

Nota: Esta solución alternativa carece de soporte para sesiones de escritorio múltiples. Solo se maneja graphical-session.targetcorrectamente siempre que solo se ejecute una sesión X11 activa en una máquina (pero ese es el caso para la mayoría de los usuarios de Linux).

  1. Cree sus propias unidades de usuario systemd que dependen graphical-session.targety haga que se ejecuten de manera limpia mientras está conectado en su escritorio.

Como ejemplo, la unidad de @ mkaito debería verse así:

[Unidad]
Descripción = Redshift
PartOf = graphical-session.target

[Servicio]
ExecStart = / bin / redshift -l 28: -13 -t 5300: 3300 -b 0.80: 0.91 -m randr
Reiniciar = siempre

(¡No olvides hacer un daemon-reloaddespués de editar tus unidades!)

  1. Reinicie su máquina, inicie sesión y verifique que sus unidades se inicien como se esperaba
$> systemctl - estado del usuario graphical-session.target
● graphical-session.target: sesión de usuario gráfica actual
   Cargado: cargado (/usr/lib/systemd/user/graphical-session.target; static; proveedor preestablecido: habilitado)
   Activo: activo desde Don 2017-01-05 15:08:42 CET; Hace 47min
     Documentos: man: systemd.special (7)
$> systemctl: estado del usuario de su unidad ...

En algún día futuro (¿será Ubuntu 17.04?) Mi solución se volverá obsoleta ya que el sistema se encargará graphical-session.targetcorrectamente. En ese día, simplemente elimine la secuencia de comandos de inicio automático y apagado y también xsession.target: sus unidades de usuario personalizadas pueden permanecer intactas y funcionar.

gue
fuente
Sé que este es un comentario antiguo, pero también puede agregar scripts de inicio / inicio de sesión a través de la aplicación de configuración del sistema en Espacio de trabajo> Inicio y apagado> Inicio automático, si desea colocar esos scripts en un lugar donde los recordará.
AmbientCyan
2

Esta solución hace exactamente lo que pregunta el autor de la pregunta:

necesita esperar hasta que Xorg esté funcionando

Si bien podría haber mejores formas de hacerlo, como ya respondieron otros usuarios, este es otro enfoque para este problema.

Es similar al sistema systemd -networkd-wait-online.service que bloquea hasta que se cumplen ciertos criterios. Otros servicios que dependen de él se lanzarán tan pronto como este servicio se inicie correctamente o se agote el tiempo de espera.

Según el manual (sección "Archivos"), el servidor X creará un socket UNIX /tmp/.X11-unix/Xn(donde nhay un número de pantalla).

Al monitorear la presencia de este socket, podemos determinar que el servidor para una pantalla en particular ha comenzado.

confirm_x_started.sh:

#!/bin/bash
COUNTER=0

while [ 1 ]
do
  # Check whether or not socket exists
  if [ -S /tmp/.X11-unix/X0 ]
  then
    exit 0
  fi

  ((++COUNTER))

  if [ $COUNTER -gt 20 ]
  then
    exit 1
  fi

  sleep 0.5
done

x_server_started.service:

[Unit]
Description=Monitor X server start

[Service]
Type=oneshot
ExecStart=/path/to/confirm_x_started.sh

[Install]
WantedBy=example.target

Ahora, habilite x_server_started.servicecomenzar al mismo tiempo con el servidor X.

Hacer que otros servicios (que necesitan que se inicie el servidor X) dependan de x_server_started.service

unidad dependiente:

[Unit]
Description=Service that needs to have the X server started
Requires=x_server_started.service
After=x_server_started.service

[Service]
ExecStart=/path/to/binary

[Install]
WantedBy=example.target

Si el servidor X se inicia sin problemas, x_server_started.servicese iniciará casi de inmediato y systemd procederá a iniciar todas las unidades que dependen x_server_started.service.

VL-80
fuente
Esto funciona muy bien. El servicio extra es un buen toque. También puede usar ExecStartPre en su servicio de destino. Sin embargo, tuve que agregar un 'sueño 1' adicional antes de la 'salida 0', parece que fue un poco demasiado rápido para intentar atrapar X de inmediato.
TTimo