Dépannage d'Apache à l'aide de strace

Gazette Linux n°132 — Novembre 2006

Deny

Adaptation française

Joëlle Cornavin

Relecture de la version française

Article paru dans le n°132 de la Gazette Linux de novembre 2006.

Article publié sous Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.


Table des matières

1. Introduction
2. Énoncé du problème
3. Tentatives de dépannage initiales
4. Détour par Apache
5. Utilisation de strace
6. Conclusion

1. Introduction

De temps en temps, les administrateurs système peuvent se trouver dans des situations où la manière conventionnelle de résoudre un problème peut ne pas donner de résultats. La manière conventionnelle signifie utiliser des scripts de test, observer les fichiers journaux, peaufiner des réglages de configuration, etc. Dans de tels cas, on devra creuser plus avant dans le fonctionnement interne du serveur. Strace s'avère être un outil précieux dans de telles situations. L'utilitaire strace intercepte et enregistre les appels système effectués par un processus et les signaux qui sont reçus par un processus. Cet article montre comment utiliser strace pour dépanner Apache en illustrant un problème concret. Je commencerai par l'énoncé du problème et poursuivrai en décrivant les tentatives de dépannage initiales. Les rouages internes du serveur Apache seront brièvement expliqués, juste avant d'examiner les résultats de strace. Comme les diverses options de strace et son fonctionnement interne dépassent la portée de cet article, veuillez consulter les pages de man s'y rapportant.

2. Énoncé du problème

Problème : je ne peux pas envoyer du courrier depuis des pages web à l'aide de la fonction mail() de PHP.

Système d'exploitation : RedHat©

Serveur SMTP : Sendmail

Serveur web : Apache/1.3.22 — c'est un environnement d'hébergement virtuel, il y a beaucoup de sites hébergés dans le serveur.

PHP version : 4.0.6.

3. Tentatives de dépannage initiales

Permettez-moi tout d'abord de recréer le problème et voir l'erreur pour ce qui me concerne. Je vais créer un script de test, mail.php, qui emploiera la fonction mail() pour envoyer du courrier. Le script de test est le suivant :


<?php
error_reporting(E_ALL);
$to = "Vishnu <we2cares@fastmail.fm>";
$subject = "Ceci est un test de message électronique";
$message = "Ceci est un test de message électronique, veuillez l'ignorer.";

if (mail($to,$subject,$message))
{
echo "Merci d'avoir envoyé un message électronique";
}
else
{
echo "Impossible d'envoyer le message électronique";
}
?>

J'ai placé mail.php dans la zone web du domaine virtuel en question, puis y ai accédé au moyen d'un navigateur. La page résultante a affiché un message en écho Impossible d'envoyer le message électronique, aucun message d'erreur propre à PHP ne s'affichait. L'analyse du fichier journal du courrier ne montrait aucune trace de courrier émanant du domaine virtuel en question. Je devais vérifier si le problème était spécifique à l'hôte virtuel ou s'il était à l'échelle du serveur. Le même script de test a été utilisé pour quelques autres hôtes virtuels et a produit le même résultat. Cela signifie que le problème n'est pas spécifique à un hôte virtuel, c'est un problème à l'échelle du serveur. On dirait qu' Apache et PHP sont défectueux.

Voyons si la fonction mail() est désactivée dans le fichier php.ini à l'aide de disable_functions.

[root@dns1 root]# grep disable_functions /etc/php.ini
disable_functions =

Non, la fonction mail n'est pas désactivée.

J'ai activé display_errors et display_startup_errors dans php.ini pour que toute erreur PHP interne soit affichée sur la page web, mais cela ne m'a pas aidé non plus. La page PHP de test n'affiche pas d'erreur, il n'y a pas de messages d'erreur dans Apache, sendmail ou d'autres fichiers journaux système. Que faire d'autre ?

 

Lorsque vous déboguez un système de production, veillez à employer votre système de développement (qui, d'ailleurs, devrait être identique et séparé de votre environnement de production). Si vous ne pouvez pas éviter de faire appel à votre système de production pour ce faire, assurez-vous que vos messages d'erreur ne sont jamais affichés dans le navigateur et transférez-les vers vos fichiers journaux. Ce stratagème est plus sûr et n'importunera pas vos utilisateurs finaux avec des messages qu'ils ne peuvent pas comprendre.

 
 -- René

Comme je l'ai indiqué précédemment, l'utilitaire strace est très pratique pour savoir ce qui se produit au niveau du processus. Avant d'employer strace pour corriger le problème, je donnerai une brève explication de la façon dont Apache prend en charge une requête entrante.

4. Détour par Apache

Apache commence par créer un processus parent avec des privilèges root. C'est le processus principal, il est responsable de la création en parallèle (ou forking) de processus fils et de leur maintien. Le processus Apache principal ne répond à aucune requête, les requêtes sont prises en charge par des processus fils. Le nombre de processus fils inactifs à un moment donné est déterminé par les directives MinSpareServers et MaxSpareServers dans httpd.conf. Lorsqu'une nouvelle requête arrive, elle est prise en charge par un des processus fils inactifs. S'il n'y a aucun processus fils inactif, alors un nouveau processus fils est créé en parallèle par le parent, pour prendre en charge une requête entrante. D'après le résultat de la commande ps affiché ci-dessous, il est évident que le processus portant le PID 1861 (Process IDentifier, identifiant de processus) est le processus Apache parent. Il fonctionne avec des privilèges root, tous les processus fils fonctionnent en tant qu'utilisateur apache.

[root@haqmail ~]# ps aux | grep httpd
USER       PID %CPU      %MEM           VSZ  RSS TTY      STAT        START   TIME     COMMAND
root      1861  0.0      0.4           25680 1992 ?        Ss         Sep24   0:02     /usr/sbin/httpd
apache    2295  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2296  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2297  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2298  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2299  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2300  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2301  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd
apache    2302  0.0      0.4           25852 2024 ?        S          Sep24   0:00     /usr/sbin/httpd

Une meilleur aperçu des relations parent-fils est disponible à partir du résultat de pstree.

[root@haqmail ~]# pstree -p | grep httpd
        +-httpd(1861)---httpd(2295)
        |             +-httpd(2296)
        |             +-httpd(2297)
        |             +-httpd(2298)
        |             +-httpd(2299)
        |             +-httpd(2300)
        |             +-httpd(2301)
        |             +-httpd(2302)

5. Utilisation de strace

À présent, nous savons que notre requête pour mail.php est prise en charge par un des processus Apache filset que strace peut être employé pour obtenir des précisions sur la façon dont les requêtes sont servies, mais il y a encore un problème : quel processus fils prend en charge la requête pour mail.php ? Soit nous connaissons l'ID de processus du processus fils exact, soit nous traçons tous les processus fils et trions ensuite le résultat de strace. Comme il n'y a aucun moyen de savoir à l'avance quel processus fils prendra en charge la requête mail.php, nous devons tracer le processus Apache parent et tous ses processus fils. L'option -f de strace trace les processus fils à mesure qu'ils sont créés par des processus actuellement tracés à la suite de l'appel système fork.

Commençons... Arrêtons d'abord Apache puis redémarrons-le avec strace :

[root@dns1 root]# strace -f -o trace.txt /etc/rc.d/init.d/httpd start

L'option -o enregistre le résultat dans un fichier trace.txt. Accédez maintenant à la page PHP de test dans le navigateur. Arrêtez strace et redémarrez Apache comme d'habitude. Il peut être nécessaire d'envoyer au processus de strace un signal SIGKILL, parce qu'il enregistre certains signaux qu'il obtient à partir du terminal de session.

Poursuivons à présent et examinons le résultat de strace dans le fichier trace.txt.

[root@dns1 root]# grep mail.php trace.txt
21837 read(7, "GET /mail.php HTTP/1.1\r\nUser-Age"..., 4096) = 472
21837 stat64("/var/www/virtdomain/mail.php", {st_mode=S_IFREG|0644, st_size=587, ...}) = 0

D'après le résultat de la commande grep ci-dessus, il est évident que le processus Apache fils prenant en charge notre requête pour mail.php est celui portant le PID 21837. Faisons maintenant un grep trace.txt pour 21837. Le résultat pertinent est collé ci-dessous.

21837 chdir("/var/www/virtdomaint") = 0
21837 open("/var/www/virtdomain/mail.php", O_RDONLY) = 8
.
.
.
.
21837 fork() = 21844

La dernière ligne prouve que le processus Apache fils crée en parallèle un autre processus portant le PID de 21844. Faisons un grep pour 21844 dans trace.txt et observons ce qui se passe.

21844 execve("/bin/sh", ["sh", "-c", "/usr/sbin/sendmail -t -i"], [/* 21 vars */]) = -1 EACCES (Permission denied)

Donc, le processus est utilisé pour envoyer du courrier à l'aide de /usr/sbin/sendmail, mais des droits d'accès incorrects empêchent de le faire. Les permissions de Sendmail sont définies correctement, mais une vérification de /bin/sh révèle qu'elles sont fixées à 770 avec une appartenance root.root. Puisque le processus Apache fils fonctionne en tant qu'utilisateur apache, il n'a pas les droits de lecture et d'exécution sur /bin/sh, d'où le problème. Un changement des permissions en 755 pour /bin/sh l'a résolu.

 

À ce stade, je serai enclin à me demander pourquoi les droits d'accès sur le fichier étaient incorrects. Il se pourrait que quelqu'un « avance à tâtons » dans votre système de fichiers.

 
 -- Steve Brown

6. Conclusion

Avec une compréhension élémentaire d'Apache et en utilisant strace, nous avons pu trouver la cause originelle du problème et donc le résoudre. strace est un utilitaire poylvalent et on peut s'en servir pour dépanner n'importe quel programme. Strace et GDB (GNU Debugger, débogueur de GNU) sont très utiles pour dépanner au niveau du système. Voici un bon article présentant les deux utilitaires.

Je suis titulaire d'un MTech. (maîtrise) en Communication Systems (systèmes de communication) de l'IIT Madras (Institut Indien de Technologie de Madras). J'ai rejoint Poornam Info Vision Pvt Ltd. en 2003 et travaille pour Poornam depuis lors.

Mes centres d'intérêt sont l'optimisation de performances, la surveillance de serveurs et la sécurité. Pendant mon temps libre, je pratique le karaté, je lis et j'écoute de la musique.

Adaptation française de la Gazette Linux

L'adaptation française de ce document a été réalisée dans le cadre du Projet de traduction de la Gazette Linux.

Vous pourrez lire d'autres articles traduits et en apprendre plus sur ce projet en visitant notre site : http://wiki.traduc.org/Gazette_Linux.

Si vous souhaitez apporter votre contribution, n'hésitez pas à nous rejoindre, nous serons heureux de vous accueillir.