Copiez une ligne de commande qui fonctionne dans ExecStart, rechargez, et l’unité échoue ou se termine silencieusement en traitant les arguments comme des noms de fichiers. Rien n’est cassé dans systemd. ExecStart est une liste d’arguments, pas un shell, donc les tubes, la redirection et les conditions perdent leur sens dès qu’ils quittent votre terminal. Décidez où le shell doit se trouver, puis écrivez une unité qui dit exactement ce qui doit s’exécuter.

Pourquoi le pipeline dans ExecStart ne fait rien

Quand une commande est saisie dans un terminal, le shell l’analyse d’abord. Il construit un pipeline, ouvre des fichiers pour la redirection et développe les variables avant qu’un programme ne démarre. systemd ne fait rien de tout cela : il découpe ExecStart selon ses propres règles et exécute le programme résultant avec ces arguments. Un > arrive donc comme un argument littéral, et un | est remis au programme au lieu de relier deux processus. L’unité ne se comporte pas mal ; la syntaxe voulait dire autre chose.

C’est délibéré et non négligent, car un traitement prévisible des arguments est ce qui rend les unités auditables. Cela ne surprend que ceux qui supposent qu’un shell est présent.

Écrivez une unité qui dit exactement ce qui s’exécute

Commencez par une petite unité dont le but est visible dans le journal, afin que les problèmes d’analyse restent séparés des problèmes de tunnel. Celle-ci ne fait que rapporter la version d’un binaire.

[Unit]
Description=Report the installed OpenVPN version

[Service]
Type=oneshot
ExecStart=/usr/sbin/openvpn --version
StandardOutput=journal
StandardError=journal

Le chemin est un espace réservé, remplacez-le donc par le binaire réellement installé. Type=oneshot convient à une commande qui s’exécute jusqu’au bout, et les réglages du journal placent sa sortie là où vous pouvez la lire sans terminal. Il n’y a délibérément pas de section d’installation, l’unité est donc appelée à la main plutôt qu’activée au démarrage. Validez le fichier avant de le démarrer, car une faute de frappe dans le nom d’une directive échoue différemment d’une faute dans le chemin.

systemd-analyze verify /etc/systemd/system/openvpn-version.service
systemctl start openvpn-version.service

Gardez la logique du shell dans un script

Quand un auxiliaire a réellement besoin de logique conditionnelle, mettez cette logique dans un script et appelez-le explicitement. Le script a besoin d’une ligne d’interpréteur, du droit d’exécution et de codes de sortie que le service peut rapporter. Lancer /bin/sh -c avec une commande entre guillemets fonctionne, mais cela cache les règles de citation à l’intérieur d’une unité et rend la relecture plus difficile. Un script nommé peut être testé à la main et remplacé plus tard sans modifier l’unité. Ne vous fiez pas au profil de shell de l’utilisateur interactif, car un service démarre avec son propre environnement.

Définissez WorkingDirectory lorsque les chemins relatifs comptent, et transmettez l’environnement via les directives prises en charge de l’unité au lieu de l’exporter dans un terminal. Les secrets appartiennent à des fichiers protégés ou à des identifiants, pas à des listes d’arguments que le journal peut enregistrer.

Vérifiez l’unité qui existe réellement

Les fichiers drop-in changent le comportement sans toucher au fichier que vous croyez avoir modifié. Lisez ce que le gestionnaire a chargé et ce que la dernière exécution a rapporté.

systemctl cat vpn-helper.service
journalctl -u vpn-helper.service -n 20 --no-pager

Le vidage de l’unité inclut les fichiers drop-in dans l’ordre de chargement, et c’est de là que vient généralement un ExecStart inattendu. Après chaque modification, rechargez le gestionnaire et confirmez que la nouvelle définition est bien ce qu’imprime systemctl cat. Le journal montre la ligne de commande exacte et le code de sortie, ce qui sépare un problème d’arguments d’un binaire manquant ou d’une erreur de permissions. Décidez ensuite si l’auxiliaire appartient au cycle de vie VPN existant ou doit rester un diagnostic manuel, car un second processus de tunnel en arrière-plan crée un propriétaire concurrent.

Une unité qui imprime la bonne sortie à la demande est un bon premier jalon. Ce n’est pas encore un tunnel, et elle ne doit pas prétendre l’être.

Articles similaires