Passer de cron à launchd, épisode 2

On l'a vu dans l'épisode 1 de "Passer de cron à launchd", la syntaxe de launchd est vraiment très verbeuse par rapport à celle des crontabs. De plus, launchd pose rapidement des problèmes quand il s'agit de définir des périodicités un peu excentriques ou sophistiquées.
Par exemple, si dans cron je veux créer une tâche qui s'exécutera toutes les 30 minutes tous les jours, j'écris ceci :

*/30 * * * *      /ma/commande

Alors, crond va lancer /ma/commande à 0h00, 0h30, 1h00, ... 23h30 tous les jours.
Pour faire la même chose avec launchd J'ai a priori deux possibilités : StartInterval et StartCalendarInterval. StartInterval est simple et permet de régler en 2 lignes mon intervalle de temps :

<key>StartInterval</key>
<integer>1800</integer>

Seulement, il existe une limitation de taille. StartInterval ne sait pas qu'il doit commencer à 0 minute pour assurer le rythme "heure + 0 minute, heure + 30 minutes". Si le plist launchd est chargé à 7h42, heure du démarrage de la machine par exemple, et bien la première exécution va se produire immédiatement. La prochaine aura bien lieu 30 minutes après, et ainsi de suite. On aura donc les heures de lancement suitantes : 7h42, 8h12, 8h42, ...

Si vous avez besoin d'un lancement à heure fixe, il faut donc oublier complètement StartInterval et vous rabattre sur StartCalendarInterval. Ce dernier impose les dates et heures de lancement, mais il souffre de deux gros défauts.
Premier défaut, et non des moindres : jusqu'à Mac OS X 10.5 le comportement de StartCalendarInterval était bugué, ce qui le rendait pratiquement inutilisable. D'ailleurs, je n'ai pas de certitude absolue sur son bon fonctionnement dans Leopard. Le second défaut, c'est la verbosité de sa syntaxe. Reprenons l'exemple ci-dessus, et transposons-le à StartCalendarInterval. On obtient le code suivant, juste pour la gestion de la périodicité :

<key>StartCalendarInterval</key>
<array>
	<dict>
	    <key>Minute</key>
	    <integer>0</integer>
	</dict>
	<dict>
        <key>Minute</key>
        <integer>30</integer>
	</dict>
</array>

Maintenant, imaginez le nombre de lignes nécessaires pour imposer un lancement aux minutes 0, 10, 20, 30, 40, et 50 : 27 lignes sans compter la gestion de la commande, de l'utilisateur, du header xml..., là où une seule ligne de crontab suffit.

Passons aux choses sérieuses avec un exemple de la vraie vie. Je veux lancer un script de sauvegarde sur ma machine professionnelle, à 12h30 et à 18h30, du lundi au vendredi. Avec cron, c'est simplissime :

30 12,18 * * 1,2,3,4,5      /mon/script/de/sauvegarde

Maintenant, si on veut reproduire la même chose avec launchd, on obtient un fichier xml très long. Je peux placer dans le même bloc dict au maximum une fois chaque mot clé (ici Minute, Hour, Weekday). Heureusement, les astérisques dans la crontab ne sont pas reportées dans launchd car un mot clé absent dans le plist correspond à une astérisque dans la crontab. Je n'ai qu'une valeur pour le mot clé Minute, j'ai deux valeurs pour le mot clé Hour, et 5 pour Weekday. Donc je vais avoir 2 blocs dict pour chaque jour (un pour chaque heure), et chaque bloc va faire 8 lignes. J'ai 5 jours, et 2 blocs de 8 lignes pour chacun, donc j'obtiens 80 lignes de xml uniquement pour la définition de la périodicité ! Ci-dessous, les 16 premières lignes :

<dict>
	<key>Weekday</key>
	<integer>1</integer>
	<key>Hour</key>
	<integer>12</integer>
	<key>Minute</key>
	<integer>30</integer>
</dict>
<dict>
	<key>Weekday</key>
	<integer>1</integer>
	<key>Hour</key>
	<integer>18</integer>
	<key>Minute</key>
	<integer>30</integer>
</dict>

Merci Apple pour cette simplification.

9 comments

  1. Même si je suis d'accord, c'est très bavard, ça vient surtout du XML en lui-même qui n'a jamais allégé le code à taper pour arriver à un résultat donné...

  2. Apple aurait pu faire plus court. En se limitant à "integer" pour la valeur des clés, on obtient une situation délirante. Si on avait pu mettre comme dans cron plusieurs valeurs on aurait économisé énormément de place, tout en rendant plus facile la maintenance et l'édition des plists.
    En reprenant l'exemple final que je donne, si on pouvait mettre 12,18 dans Hour, et 1,2,3,4,5 dans Weekday, on passerait de 80 lignes à 8.

  3. Oui, je suis d'accord mais ils ont une DTD ultra-basique pour les plist et n'ont certainement pas envie de refaire tout et donc de se taper un parseur supplémentaire. Je soupçonne le parsing des plist d'être fait par un moteur simpliste taillé sur mesure pour la DTD.

  4. En fait, j'ai déjà parlé de Lingon dans un article plus ancien. À l'époque c'était une perle ! Depuis la 10.5, je crois, Lingon n'est plus que l'ombre de lui même. Il est devenu bien moins intéressant (et c'est dommage).

  5. Oui, bien sûr, le dernier bout de code que je donne n'est qu'un extrait, il faut l'encapsuler dans

    <key>StartCalendarInterval</key>
    <array>
    ...
    </array>

    Lui même encapsulé dans une plist complète.

  6. Merci pour ces précieuses infos sur Launchd.
    Je préfère 1000 fois cron.
    Je ne sais pas si c'est moi, launchd ou mail, mais je suis en train de devenir fou avec un script d'envoi de mail d'erreur en cas de problème durant le déroulement d'une tache répétitive !!!

  7. OK c'est pas simple mais grâce à ces exemples et explications c'est nettement plus clair.
    MERCI

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.