Sauvegarder des bases MySQL

Dans de précédents articles j'ai répondu brièvement aux questions de l'installation de MySQL 5 sur Mac OS X 10.5 et de la création d'un plist de démarrage launchd pour MySQL. J'ai aussi donné quelques ficelles pour trouver les points de blocage habituels au fonctionnement de MySQL.
Il me reste donc à aborder le problème des sauvegardes. Cet article s'adresse uniquement aux utilisateurs d'un serveur MySQL qui sont root/admin de leur serveur, si vous avez un serveur MySQL chez un hébergeur, alors vous pouvez passer votre chemin.

Le script que je propose ci-dessous doit être lancé en root, une fois par jour. Il fait une sauvegarde de chaque base de données qu'il trouve, et il conserve cette sauvegarde pendant 7 jours. En cas de pépin, il vous est donc possible de restaurer une ou plusieurs bases de données, en remontant de maximum 7 jours dans le passé.
Vous devez adapter absolument les lignes 2 à 5 pour indiquer les chemins suivants :

  • mysqlroot : chemin du dossier contenant les bases de données sur le serveur
  • monbkp : chemin du dossier contenant les sauvegardes des bases sur le serveur
  • hotcopy : chemin de l'exécutable mysqlhotcopy sur le serveur
  • mydump : chemin de l'exécutable mysqldump sur le serveur

Vous devez aussi remplacer LE_PASS_MYSQL par le mot de passe de root de MySQL. Attention, il n'y a pas d'espace entre -p et LE_PASS_MYSQL pour la commande mysqldump, mais il y a bien un espace entre le -p et LE_PASS_MYSQL pour la commande mysqlhotcopy.

Le script supporte un argument ("dump"). Si il est lancé avec cet argument, alors il utilisera mysqldump pour faire la sauvegarde. Vous obtenez alors un dump de chaque base, c'est à dire un fichier texte "plat" contenant les instructions SQL nécessaires à la reconstruction de la base de données. C'est la méthode que je recommande car le fichier obtenu peut être injecté dans presque n'importe quel serveur MySQL. Par ailleurs, le fichier obtenu est plus petit et plus facile à compresser.
Si le script est lancé sans l'argument "dump", alors la méthode de sauvegarde utilisée est mysqlhotcopy. Ce programme duplique physiquement le répertoire de chaque base de données. L'avantage c'est que la sauvegarde est prête à l'emploi, il n'est pas nécessaire de la ré-injecter dans le serveur. L'inconvénient, c'est qu'il vous sera probablement impossible d'utiliser cette sauvegarde sur un autre serveur que le votre, et dans la même version de MySQL. Si vous souhaitez archiver vos sauvegardes de bases de données et pouvoir les restaurer quelques mois ou années plus tard, il ne faut pas utiliser mysqlhotcopy.

Déroulement du script :

  1. création d'un dossier temporaire /tmp/mysql
  2. pour chaque base de données, création d'un dump ou d'une "hotcopy"
  3. archivage et compression de chaque dump/hotcopy (en .tgz)
  4. suppression de la version non-compressée
  5. déplacement de la version compressée vers le dossier de sauvegarde
  6. suppression du dossier temporaire /tmp/mysql
#!/bin/sh
mysqlroot=/var/db/mysql
monbkp=/backup/MYSQL
hotcopy=/usr/local/bin/mysqlhotcopy
mydump=/usr/local/bin/mysqldump

madate=`date "+%Y-%m-%d"`
monjour=`date "+%w"`

echo "lancement du backup des bases de donnees MySQL..."
echo

mkdir -m 0777 /tmp/mysql

cd $mysqlroot
for directory in *
do
if [ $directory != "" ]; then
if [ -d "$mysqlroot/$directory" ]; then
  echo -n "backup de $directory : "
  case $* in
    dump)
    $mydump -u root -pLE_PASS_MYSQL --opt $directory > "/tmp/mysql/$directory"
    ;;
    *)
    $hotcopy -u root -p LE_PASS_MYSQL -q "$directory" /tmp/mysql
    ;;
  esac
  if [ $? = 0 ]; then
    tar -czf "$monbkp/$madate$directory.tgz" -C /tmp/mysql/ "$directory"
    if [ $? = 0 ]; then
      rm -f "$monbkp/$directory.${monjour}.tgz"
      rm -r "/tmp/mysql/$directory"
      mv "$monbkp/$madate$directory.tgz" "$monbkp/$directory.${monjour}.tgz"
      echo "ok"
    else
      echo "Erreur targz".
    fi
  else
    echo "Erreur export".
  fi
fi
fi
done

rm -fR /tmp/mysql/

 

Pour lancer ce script toutes les nuits, j'utilise une crontab (car mon mysqld est installé sur un serveur FreeBSD). Le script est enregistré dans /usr/local/bin/ sous le nom BKP_SQL.sh. Voilà la ligne en question :

30 5 * * * /usr/local/bin/BKP_SQL.sh

Et si je souhaite faire des dumps plutôt que des "hotcopy" :

30 5 * * * /usr/local/bin/BKP_SQL.sh dump

 
Note : ce script n'est pas du tout une référence de fiabilité et encore moins d'élégance, néanmoins, je l'ai créé en 2003 et depuis il tourne tous les jours. Je n'ai jamais eu de problème avec.

Note 2 : selon le système, votre environnement, ... il vous faudra peut être indiquer le chemin complet pour l'application tar.

Related posts

L’agrégation RSS : suivre plusieurs sites en même temps

Icone RSS - Mozilla Public License Version 1.1Je vois pas mal de visiteurs ici qui pointent tous les jours, voire plusieurs fois par jour, leur navigateur sur ce blog. Je pense qu'il est donc assez pertinent de parler un peu des flux RSS et des lecteurs/agrégateurs qui permettent de les exploiter.

Un flux RSS, grossièrement présenté, c'est un fichier structuré utilisé pour mettre à disposition des utilisateurs les mises à jour d'informations. Si je prends le cas de ce blog, les lecteurs ont à leur disposition trois flux RSS :

  1. Le flux principal du blog
  2. Le flux des commentaires du blog
  3. Le flux de la galerie photo

Cela leur permet, à condition d'utiliser un logiciel compatible (Safari, Firefox, ou un lecteur de flux RSS dédié), de s'abonner à un fil d'information. S'abonner à un de ces fils, c'est enregistrer l'adresse du flux RSS dans son logiciel. Ce dernier va alors lire le flux RSS périodiquement, et prévenir l'utilisateur dès que les informations sont mises à jour.

Si je m'abonne au flux RSS principal de ce blog, mon lecteur RSS va lire ce flux toutes les 30 minutes, toutes les heures, ou à tout autre intervalle que j'aurai précisé. Dès qu'un nouvel article sera publié, mon lecteur RSS me préviendra. Ainsi, je n'ai plus besoin de venir manuellement consulter le site web qui m'intéresse, si ce dernier dispose d'un flux RSS.

Il existe une grande quantité de logiciels de lecture de flux RSS pour Mac : Mail (Mac OS X 10.5), Safari, Firefox, NetNewsWire, NewsFire, NewsMac, PulpFiction, Shrook, Vienna, ... Il existe aussi des logiciels en ligne comme Feedfetcher chez Google. Les logiciels spécialisés sont appelés des agrégateurs RSS, car ils permettent de suivre simultanément une grande quantité de flux RSS. Je peux m'abonner aux flux RSS des blogs de mes amis, de mon site d'informations favori, de la photo de la semaine du Times, etc. Chaque fil d'information sera vérifié périodiquement, sans que j'ai à intervenir. Ainsi, je suis mis au courant très rapidement des nouveaux articles ou commentaires publiés sur l'ensemble des sites qui m'intéressent.

Related posts

MySQL 5 : le checklist en cas de pépin

Comme les commentaires le montrent, si l'installation de MySQL peut prendre 5 minutes et se dérouler comme un charme, le moindre problème peut vite bloquer le débutant pendant des jours. Et plus le débutant se débat, plus il fait des dégâts sur son système.
Je vous propose donc ici une petite checklist des choses à vérifier si votre installation de MySQL sur Mac OS X tourne mal.

1) Si vous avez installé une ou plusieurs autres versions de MySQL et que vous souhaitez repartir de zéro, le plus simple est de localiser tous les éléments liés à MySQL et de les supprimer. On utilise pour cela la base locate, qu'il convient de mettre à jour au préalable :

sudo /usr/libexec/locate.updatedb 
locate -i mysql

ensuite faites le tri dans ce que vous voyez, et supprimez les éléments appartenant aux anciennes installation de MySQL (sans doute dans /usr/local/, /Library/LaunchDaemon/, /Library/StartupItems/, ...)

2) Vérifiez votre PATH : les binaires de MySQL doivent se trouver dans votre PATH pour être exécutables sans indiquer leur chemin complet. Si la commande which mysql ne renvoie rien, ajoutez /usr/local/mysql/bin/ à votre PATH.

3) Vérifiez que le serveur mysqld est lancé (ps auxwww | grep mysqld) ou se lance bien (sudo /usr/local/mysql/bin/mysqld_safe).

4) Vérifiez que le socket du serveur existe quand il est lancé :

netstat -f unix | grep mysql 
ls -l /tmp/mysql.sock

Si l'une de ces deux commandes ne retourne pas le chemin du socket, alors soit le socket est ouvert par le serveur mais supprimé par un autre process, soit le socket n'est pas ouvert par le serveur car il existe déjà.

5) Vérifiez que vous essayez bien d'accéder au serveur MySQL à partir d'un logiciel qui saura s'y connecter (php doit connaître le chemin du socket, le client mysql aussi). Si vous tentez une connexion réseau, vérifier aussi que mysqld ouvre un port réseau (netstat -alnf inet | grep 3306).

6) Jetez un œil aux fichiers de log de MySQL. Si le serveur ne se lance pas du tout, cela ne vous aidera probablement pas, mais si il se lance et quitte inopinément, ou si il se lance dans un état inutilisable, vous trouverez probablement des informations intéressantes dans ces fichiers. Pour l'installation de MySQL via le package officiel, les fichiers de log se trouvent normalement dans /usr/local/mysql/data/, sous le nom de localhost.err ou de votre-machine.err.

Je compléterai cette liste au fil du temps... N'hésitez pas à faire des suggestions !

Related posts

MySQL 5 : le plist de démarrage

Dans un précédent article j'ai exposé l'installation triviale d'un MySQL 5 sur une base de Mac OS X 10.5 propre. J'ai aussi mentionné le StartupItem fourni de base avec le package MySQL. Ce StartupItem étant conçu pour Mac OS X 10.4, il est intéressant de voir comment on doit maintenant lancer un serveur MySQL sur Leopard.

Si vous aviez installé le script de démarrage de MySQL 5 mentionné dans le précédent article, il faudra le désactiver ou le supprimer. Pour le supprimer, vous pouvez exécuter les commandes suivantes dans le terminal :

sudo /Library/StartupItems/MySQLCOM/MySQLCOM stop 
sudo rm -r /Library/StartupItems/MySQLCOM 
sudo rm /tmp/mysql.sock

Ensuite, vous pouvez créer le fichier /Library/LaunchDaemons/org.mysql.mysqld.plist avec votre éditeur favori, et coller dedans :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.mysql.mysqld</string>
	<key>OnDemand</key>
	<false/>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/local/mysql/bin/mysqld</string>
		<string>--socket=/tmp/mysql.sock</string>
		<string>--user=mysql</string>
		<string>--port=3306</string>
		<string>--datadir=/usr/local/mysql/data</string>
		<string>--pid-file=/usr/local/mysql/data/localhost.pid</string>
		<string>--bind-address=127.0.0.1</string>
	</array>
	<key>ServiceIPC</key>
	<false/>
</dict>
</plist>

Une fois que ce fichier est sauvé, il faut s'assurer qu'il appartient bien à root, sinon launchctl vous envoie sur les roses (cf. les commentaires de Pierre29). Dans le doute, exécutez la commande

sudo chown root /Library/LaunchDaemons/org.mysql.mysqld.plist

Vous pouvez ensuite activer ce plist par la commande

sudo launchctl load /Library/LaunchDaemons/org.mysql.mysqld.plist

MySQL devrait être alors lancé automatiquement pour vous. Il placera son socket dans /tmp/ et autorisera aussi les connexions TCP sur l'interface 127.0.0.1.

Je vous renvoie à la documentation de launchctl pour le reste des options et commandes disponibles et pour les détails sur l'utilisation de launchd. Votre serveur MySQL est désormais géré proprement par launchd, exactement comme sur un Mac OS X Serveur.

edit : ajout du chown root sur le fichier

Related posts

Recherchons administratrice système polyvalente

Une fois de plus, nous cherchons une perle rare pour notre service. Voici le texte de l'annonce :

Recherchons administratrice système polyvalente. 

Cette administratrice s'intègrera dans une équipe restreinte chargée de l'infrastructure matérielle de l'environnement numérique de travail de l'université Lumière Lyon 2. Cet ENT est utilisé par environ 30 000 personnes. Ces outils regroupent les outils du bureau virtuel (courrier électronique, agenda, carnet d'adresse, documents...), les sites web institutionnels et personnels (plus d'une cinquantaines de sites web), les outils administratifs (gestion de notes pour les enseignants, consultation des notes, emploi du temps pour les étudiants...), la plateforme d'enseignement en ligne…

Elle participera aux activités suivantes :

  • Administration, surveillance et maintenance d'une quarantaine de serveurs fonctionnant sous Mac OS X et Linux Debian.
  • Administration d'un réseau privé et  d'un commutateur d'applications Nortel utilisé pour de la répartition de charge applicative.
  • Installation physique et logicielle, câblage  de machines fonctionnant sous Mac OS X et Linux Debian à l'intérieur d'une salle serveur.
  • Installation, création d'outils de surveillance, d'analyse et de maintenance.
  • Participation à la définition des architectures matérielles et logicielles à mettre en place pour le déploiement d'applications WebObjects ou JEE, Ruby ou PHP.
  • Mise en place et administration d'applications fonctionnant avec Apache, MySQL et PHP.
  • Mise en place et administration d'applications de diffusion de fichiers vidéo ou sons.
  • Mise en place de procédure et solution de sauvegarde.
  • Administration d'une architecture Xsan.
  • Participation aux astreintes pour offrir une continuité de service 24h/24h, 7j/7j, 365j/365j.

Cette administratrice doit avoir des connaissances Linux, Mac OSX. Elle doit être capable d'installer un outil à partir de son code source. Elle doit être capable de lire et d'écrire des scripts shell et des scripts dans un langage comme Ruby, PHP ou Perl. Elle doit être avoir des connaissances dans le domaine de la sécurité, des annuaires.

Elle devra être capable de s'adapter à des technologies et des outils en évolution rapide.

Notez que si vous êtes un homme, vous pouvez quand même tenter votre chance ;-)
Par ailleurs, je ne précise pas à qui il faut écrire, on ne serait probablement pas intéressé par quelqu'un qui ne saurait pas retrouver cette information.

Related posts

MySQL 5 sur Mac OS X 10.5 en 5 minutes

Ça arrive toujours par vague, il suffit de suivre un peu des newsgroups comme fr.comp.mac-os.x pour s'en apercevoir. Un type un peu désespéré se pointe avec plein de questions sur comment installer MySQL sur son nouvel OS, puis c'est s'escalade, la surenchère à qui proposera la solution la plus compliquée ou l'idée la plus saugrenue. Bien sûr au départ c'est toujours la faute du système, ou celle de MySQL. C'est rarement celle de tous ces tutoriels ou de ces conseils mal avisés glanés sur les forums les plus inattendus. Et non, bien sûr ce n'est jamais la faute du pauvre bougre qui va copier-coller dans son terminal, sans les comprendre, les commandes dictées par des inconnus.
Pourtant, installer un MySQL 5 sur Mac OS X est d'une simplicité enfantine. C'est comme à l'école des fans : même les moins doués gagnent à la fin. Je ne vais pas détailler toutes les contortions cérébrales qu'il faut faire pour rater cette installation, on trouve suffisamment d'exemples sur les forums, et ce ne serait pas chic de ma part de moquer la paresse intellectuelle de certains.

La première étape pour une installation réussie, c'est de télécharger le package officiel fourni par MySQL. Ne cédez pas à la tentation de compiler vous même MySQL, si vous avez besoin d'aide pour installer un pkg, faites preuve de bon sens et oubliez immédiatement la compilation. On trouve le précieux paquet dans la zone "Developer" du site mysql.com. Cliquez sur "MySQL Community Server" dans la marge de gauche, et dans la nouvelle page qui se charge, trouvez le lien intitulé "Mac OS X (package format)". Je ne donne pas les liens directs, car ils sont susceptibles de changer au fil des versions de MySQL.
Vous voilà en face d'une liste de packages pour Mac OS X : Mac OS X (package format) downloads. Choisissez celui qui correspond le plus à votre version du système. Actuellement pour un G5 en Mac OS X 10.4 ou 10.5 ce sera "Mac OS X 10.4 (PowerPC, 64-bit)" par exemple.

Une fois l'image disque téléchargée et montée vous voici en face de deux packages, d'un "PrefPane", et d'un readme :

  • mysql-5.0.45-osx10.4-powerpc-64bit.pkg (dans mon cas)
  • MySQLStartupItem.pkg
  • MySQL.prefPane
  • ReadMe.txt

Le premier package installe tout MySQL sur votre machine. Le second installe un StartupItem à l'ancienne (comprendre : pré-launchd). Pour un lanceur à la mode de launchd : moderne et qui marche bien, voyez cet article. Le PrefPane permet d'installer un tableau de bord MySQL dans les préférences système.
Muni d'un log/pass d'administrateur vous pouvez maintenant installer les packages par simple double-click. Jusque là, pas besoin de comprendre la théorie de la relativité. Si vous avez su taper votre mot de passe, vous avez maintenant installé MySQL.
De la même manière, double-cliquez sur le PrefPane pour l'installer. Vous pouvez l'installer pour vous uniquement, ou pour tous les utilisateurs de la machine. C'est sans incidence sur le résultat, faites comme vous préférez. Ce tableau de bord est de toute manière partiellement non-fonctionnel sous Leopard (je ne l'ai pas testé sous Tiger). Il ne permet pas de lancer ou d'arrêter MySQL via le bouton de son interface. Par contre, il permet d'activer ou non le lancement automatique du serveur MySQL au démarrage de la machine.
À partir de là, votre serveur MySQL est complètement fonctionnel. Si vous avez opté pour un lancement automatique au démarrage, vous pouvez maintenant rebooter pour vérifier que cela fonctionne, tout en vous félicitant d'avoir installé MySQL 5 en moins de 5 minutes, sans taper une seule ligne de commande dans votre terminal. Si vous souhaitez lancer le serveur sans redémarrer et que, comme dans mon cas, le bouton ad hoc du tableau de bord de fonctionne pas, vous pouvez le faire via le terminal :

  • cochez la case pour lancer MySQL automatiquement au démarrage (cela édite pour vous un fichier de configuration qui autorise aussi le lancement manuel)
  • tapez dans une fenêtre de terminal la commande
    sudo /Library/StartupItems/MySQLCOM/MySQLCOM start

Normalement, le tableau de bord doit maintenant refléter l'état du serveur, et indiquer que le serveur est fonctionnel. Si ce n'est pas le cas, c'est que vous êtes parvenu à rater une des étapes précédentes.

Désormais, il est judicieux de tester la connexion au serveur. Ouvrez une fenêtre de terminal, et taper la commande suivante :

/usr/local/mysql/bin/mysql -u root

Cela doit vous donner le résultat suivant :

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 200
Server version: 5.0.45 MySQL Community Server (GPL)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> 

Vous êtes alors connecté à votre serveur mysql local. Il est important de noter que les packages officiels de MySQL sont compilés pour stocker le socket de connexion dans /tmp/mysql.sock, et non pas dans /var/mysql/mysql.sock. Le PHP fourni par Apple quant à lui, attend ce socket dans /var/mysql/mysql.sock. Si vous souhaitez faire fonctionner des scripts PHP, vous devrez indiquer à ce dernier le chemin réel de votre socket MySQL. On fait cela en éditant le fichier /private/etc/php.ini. Ouvrez ce fichier (probablement inexistant) avec votre éditeur de texte favori, et insérez la ligne mysql.default_socket = /tmp/mysql.sock. Maintenant, PHP doit trouver tout seul le socket de MySQL. C'est facile à vérifier dans le terminal :

php -r 'mysql_connect(localhost, root, ""); echo mysql_result(mysql_query("SELECT 2+2;"),0)." ";'

Si tout fonctionne, le résultat affiché sera "4". Si cela ne fonctionne pas, c'est probablement que vous êtes incapable d'utiliser MySQL de toute manière :).

edit : en cas de pépin, faites un tour vers la checklist.

Related posts

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.

Related posts

Passer de cron à launchd

Il existe des tas de bonnes raisons d'utiliser cron plutôt que launchd pour lancer des tâches périodiques sur un Mac OS X. La première étant l'écrasante complexité de launchd face à cron. Néanmoins, Apple fait presque tout pour nous décourager d'utiliser cron, donc il est peut être temps de jeter un œil à launchd. J'ai déjà montré comment ce dernier permet de créer des tunnels ssh à la demande pour la connexion d'applications. Voyons maintenant comment il peut aussi être utilisé, dans une certaine mesure, pour s'acquitter de tâches périodiques.

Vous êtes peut être arrivé sur cette page à cause d'un vilain message dans vos fichiers de log système :

...cron...Could not setup Mach task special port 9: (os/kern) no access

Si vous trouvez ce message dans votre fichier /var/log/system.log, c'est que vous utilisez, peut être à votre insu, des crontabs. Les crontabs sont des fichiers de configuration pour cron. Le système râle pour vous faire comprendre à demi-mot qu'il serait temps d'utiliser launchd, et de laisser cron à vos ancêtres.

Généralement, une crontab se présente sous cette forme (sortie de la commande crontab -l) :

# Collect system information every minute
* * * * *       /Users/patpro/bin/cpu_load.sh >/dev/null
# Status graphs generation
*/15 * * * *    /Users/patpro/bin/cpu_load_graphs.sh >/dev/null

J'ai donc ici un script lancé toutes les minutes, et un script lancé toutes les 15 minutes.

La première complexité de launchd c'est que nous allons avoir un fichier de configuration de plusieurs lignes pour chaque ligne de la configuration de cron. Chaque action gérée par launchd fait l'objet d'une déclaration dans un fichier XML. Pour les actions périodiques de type crontab, il faut que launchd les prennent en charge dès le démarrage de la machine. Il faut aussi que les scripts ou applications soient lancés indépendamment de l'utilisateur. Pour remplir ces conditions, il faut que les fichiers XML soient placés dans /Library/LaunchDaemons/.
Voici le fichier XML correspondant à la première ligne de ma crontab, il se nomme ma.crontab.cpu_load.plist et est enregistré dans /Library/LaunchDaemons/ :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
                        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>ma.crontab.cpu_load</string>
        <key>UserName</key>
        <string>patpro</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Users/patpro/bin/cpu_load.sh</string>
        </array>
        <key>StartInterval</key>
        <integer>60</integer>
</dict>
</plist>

Le champs Label doit être unique, on reprend en général le nom du fichier, ici ma.crontab.cpu_load.plist, en omettant le suffix .plist.
Le Username correspond au login de l'utilisateur sous le quel sera lancée la commande.
ProgramArguments est un tableau qui peut contenir plusieurs arguments. Le premier argument (string) est toujours le nom de la commande à exécuter. Les strings suivants (facultatifs) sont les arguments que l'on souhaite passer à la commande.
Pour finir, StartInterval est le nombre de secondes qui sépare chaque lancement de la commande. Il ne peut pas être inférieur à 60, car launchd ne scrute sa liste de lancement que toutes les 60 secondes. Pour la gestion de la périodicité de date (tous les jours à 3h, ou le dimanche à 12h...) on utilise à la place de StartInterval le mot clé StartCalendarInterval (voir le man de launchd.plist pour la syntaxe).

De la même manière, la seconde ligne de ma crontab se traduirait par un fichier ma.crontab.cpu_load_graphs.plist au contenu suivant :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
                        "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Label</key>
        <string>ma.crontab.cpu_load_graphs</string>
        <key>UserName</key>
        <string>patpro</string>
        <key>ProgramArguments</key>
        <array>
                <string>/Users/patpro/bin/cpu_load_graphs.sh</string>
        </array>
        <key>StartInterval</key>
        <integer>900</integer>
</dict>
</plist>

Il est possible d'ajouter une directive à ces fichiers plist si on souhaite logger les erreurs d'exécution :

        <key>StandardErrorPath</key>
        <string>/tmp/sortie-err.log</string>

Une fois que l'on a créé tous les fichiers adéquats dans /Library/LaunchDaemons/, on peut activer les plists launchd (sans oublier de neutraliser les crontabs) :

sudo launchctl load /Library/LaunchDaemons/ma.crontab.cpu_load*

Il peut arriver que certaines crontabs soient trop complexes pour launchd. Par exemple, je pense que launchd ne sait pas gérer une périodicité de lancement d'une fois toutes les 15 minutes du lundi au vendredi. J'avoue néanmoins que je n'ai pas vérifié ;)
edit : launchd peut gérer cela, mais ce n'est pas élégant, voir l'épisode 2.

Related posts