J'écris un script bash pour une tâche de sauvegarde. Entre autres choses (gel du système de fichiers, verrouillage de la base de données, instantanés, ...), il faut arrêter Apache et le redémarrer après la sauvegarde. Comme ce script doit être exécuté comme une tâche cronjob et que les temps d'exécution peuvent varier considérablement (notamment parce qu'il attend un "bon moment" pour effectuer la sauvegarde), j'ai essayé de le protéger des exécutions multiples à l'aide de flock
.
Cependant, flock
conserve le verrou même après la fermeture du script de sauvegarde. Ce comportement est indépendant de la façon dont j'utilise flock
(utilisez un répertoire, un fichier ou un descripteur de fichier ouvert à partir du script).
J'ai traqué le problème jusqu'au redémarrage d'Apache2 et je peux le voir dans le one-liner suivant
flock -n /var/lock/startapache service Apache2 start
Voir la session interactive suivante pour illustrer le problème:
root@fermat:/home/ubuntu# service Apache2 stop
* Stopping web server Apache2 ... waiting . [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service Apache2 start || echo failed
* Starting web server Apache2 [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service Apache2 start || echo failed
failed
En effet, le script Apache démarré maintient mes descripteurs de fichier de verrouillage ouverts:
root@fermat:/home/ubuntu# lsof /var/lock/startapache
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
Apache2 23651 root 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23656 trac 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23674 trac 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23675 root 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23676 root 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23677 root 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23694 root 3u REG 0,17 0 6673997 /run/lock/startapache
Apache2 23696 root 3u REG 0,17 0 6673997 /run/lock/startapache
Une fois Apache arrêté, le verrou est libéré:
root@fermat:/home/ubuntu# service Apache2 stop
* Stopping web server Apache2 ... waiting [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service Apache2 start || echo failed
* Starting web server Apache2
C'est pourquoi ma question (s): Comment le processus Apache "hérite-t-il" de ces descripteurs de fichier? Pourquoi le même comportement ne se produit-il pas avec d'autres scripts de démarrage (par exemple, "service mysql start")? Et y a-t-il un moyen de l'éviter?
Puisque personne n'a écrit de réponse (malgré la prime), je vais m'expliquer comment j'ai finalement résolu le problème.
Le commentaire de muru (merci beaucoup!) était juste et m'a amené sur la bonne voie: dans Ubuntu 14.04, le démarrage de Apache est géré par un sysv script init, et étant un script shell, il hérite de tout (variables d’environnement, descripteurs de fichiers ouverts, etc.), et le processus Apache qu’il a démarré héritera également de tous ces éléments. Mysql, à son tour, est géré par Upstart, ce qui garantit un environnement propre pour les services démarrés. Ceci explique le comportement différent de Mysql et Apache.
Armé de cette connaissance, je savais mieux quoi chercher et j'ai trouvé cette réponse sur unix.stackexchange.com . Il suggère de fermer le descripteur de fichier dans le sous-shell qui appelle des scripts qui ne devraient pas en hériter, tout en maintenant le descripteur ouvert dans le shell externe, afin de s'assurer que le verrouillage reste effectif.
Malheureusement, cela signifie que j'ai dû déplacer les appels flock
dans le script Shell, sinon mon script ne saurait pas quel descripteur de fichier fermer. Ainsi, mon nouveau script Shell (qui tire également son inspiration de ce billet de blog sur flock
) a la structure suivante:
#!/bin/bash
FLOCK_FILE="/var/lock/backup-lock"
FLOCK_FD=20
# Locking
eval "exec $FLOCK_FD>'$FLOCK_FILE'"
if ! flock -n $FLOCK_FD
then
echo "FAILED! There is a backup script already running."
exit 1
fi
(
# Unlock in sub-Shell, so daemons with bad startup scripts
# (like Apache) don't inherit the look.
# Note that the lock is still alive in general because it's
# held by the outer Shell.
eval "exec $FLOCK_FD>-"
# ... normal backup stuff from the original script ...
# Among other stuff the mentioned vicious line:
service Apache2 start
# ... normal backup stuff from the original script ...
)
# Unlock in outer Shell because we're done.
eval "exec $FLOCK_FD>-"