Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Archive for January, 2008

Niveau :      
Résumé : écrire, réécrire

Factoring

Supposons que vous développiez un projet personnel. Vous êtes le seul à spécifier les besoins, le seul à spécifier l'architecture, le langage ... Malgré cela vous constatez la plupart du temps que le résultat est moyen, voire médiocre (ou acceptable si vous êtes très doués). Pourquoi ? Simplement parce qu'un programme doit toujours être développé au moins deux fois.

Vous devez être conscient que la première version d'un programme sera toujours un prototype. Sans exception. Mais cela va me coûter cher me direz-vous ! Hé bien pas tant que ça. Puisque vous êtes conscients que la première version sera un prototype, vous y mettrez beaucoup moins d'efforts. D'autre part la deuxième version sera plus courte à écrire puisque vous avez acquis toute l'expérience nécessaire. Et enfin les bugs de conceptions, les plus durs à résoudre, pourront être évités et donc vous gagnerez du temps.

Un prototype c'est un programme qui a toutes les fonctionnalités du produit fini (ou presque, les demandes évoluant souvent en cours de route). Mais il n'intègre pas nécessairement tous les détails, toutes les options ou une interface graphique soignée. Un prototype ne doit pas être la base de la version suivante du programme. La version finale doit être repensée en partant de 0 (on pourrait même dire que le langage doit être changé). Par contre, rien n'interdit de faire du copier/coller depuis le prototype vers la version finale, c'est même recommandé car un grand nombre de fonctions ont déjà été développées correctement.

Vous aurez compris que ce que je viens de vous décrire est le refactoring. Rien de bien nouveau, seulement vous devez être conscient que pour faire un logiciel dans lequel vous ne perdrez pas votre temps, vous devrez refactorer au moins une fois.

Refactoring

Maintenant , si vous développez pour une entreprise, le processus de développement est quasiment toujours le même. Un client a fait un cahier des charges plus ou moins complet, assisté d'un commercial qui en a profité pour lui faire de nombreuses promesses. Ensuite ce cahier des charges a été traduit par un analyste (peut-être vous même) en un schéma d'architecture ou un schéma UML avec des use case pour les plus méticuleux. Très bien, on pourrait aller jusqu'à dire que c'est fait dans les règles de l'art. C'est ici que le boulot de développeur (et les ennuis) commence. Il s'agit de prendre l'analyse et d'en faire, presque mécaniquement, le logiciel désiré.

Et vous en avez tous fait l'expérience, ça ne marche jamais comme il faut. Il y a de nombreux endroits où le bât blesse. Tout d'abord le client ne sait pas ce qu'il veut, normal il s'agit d'un produit qu'il n'a jamais vu ni utilisé. Ensuite le commercial en a profité pour lui vendre quelque chose de génial, normal c'est son boulot, mais pas nécessairement réalisable. Ensuite l'analyste a fait ce qu'il a pu pour déduire de ce cahier des charges écrit en langage courant un schéma qui peut être informatisé. Et enfin le développeur fait ce qu'il peut en fonction des contraintes du langage utilisé.

C'est le même cas que précédemment, en légèrement plus compliqué surtout s'il y a beaucoup d'intervenants. L'extreme programming pallie ce problème en prônant un refactoring permanent et un bouclage fréquent avec le client.

Ne perdez plus votre temps, mettez des bouts d'extreme programming dans votre activité.

Niveau :      
Résumé : xargs -l ; gdb close ; alias shutdown

Pour avoir les arguments de xargs n'importe où sur la ligne de commande et pas seulement en dernier :

$ ls | xargs -n 1 -I @ echo debut @ fin

Mieux, ça marche avec les chaînes de caractère

$ ls | xargs -n 1 -I @ echo "debut @ fin"

Fermer un file descriptor ou une socket ouverte par un autre programme (ça ne paraît pas, mais ça peut être utile pour débloquer une situation sans tuer un processus) :

$ gdb -p $pid
> call (void)close($fd)

Un petit alias à mettre sur toutes vos machines ultra importantes, ça peut vous sauver la vie :

$ alias shutdown="echo Tu serais pas un peu malade"

Faites pareil pour halt et reboot et le jour où vous en avez vraiment besoin, utilisez directement /sbin/shutdown. Attention, si vous l'utilisez partout et pas seulement sur les machines importantes, ça ne servira plus à rien.

Niveau :      
Résumé : MTU vs MSS ; ip link show ; ip link set mtu

La MTU : Maximum transmission unit est la taille maximum des paquets pouvant être transférés sur un réseau. La MTU dépend de la technologie réseau sur laquelle on se trouve. Pour l'ethernet, elle vaut 1500 (le gigabit ajoute les jumbo frame et elle peut alors valoir 9000). Internet ne garantit officiellement qu'une MTU de 576 (en ipv4), ce qui est la valeur que vous avez avec un modem rtc. En ipv6, la valeur minimale garantie a été augmentée à 1280. Pour information, si votre accès internet se fait en PPPoE vous avez une MTU à 1492, 8 octets servant à la partie PPP.

La MSS : Maximum segment size est la taille maximum d'un segment TCP (et n'est donc pas applicable aux paquets non tcp), c'est la taille utile du paquet. L'en-tête TCP/IP variant rarement, elle vaut en général MTU - 40.

Une MTU étant dépendante du chemin emprunté, elle peut varier au cours d'une communication. Le noyau commence donc par attribuer la valeur correspondant à l'interface sur laquelle il se trouve comme vu plus haut.

# Pour voir la MTU
$ ip link show
# ou (vieille version)
$ ifconfig
# et pour la changer
$ ip link set eth0 mtu 1450

Ensuite, en général, il utilise un algorithme appelé PMTU discovery. C'est du même style que pour un traceroute, il envoie ses paquets avec la taille correspondant à son interface et les réduit lorsqu'il reçoit un paquet icmp lui disant que son paquet est trop gros. Vous pouvez découvrir la MTU de la même façon à la main avec ping :

$ ping -c 1 -M do www.google.com -s 1500

Comme vous savez ce qu'est la dichotomie, vous trouverez rapidement. Attention, ping ajoute une en-tête de 28 octets (icmp/ip) au paquet (mais n'affiche que 8 octets (icmp) de plus).

Il se peut que le noyau réduise la MTU temporairement pour une destination donnée, par exemple vers un modem rtc. Mais il arrive que des administrateurs peu consciencieux empêchent tout paquet ICMP de passer, y compris ceux qui indiquent qu'il faut fragmenter le paquet. Dans ce cas vous êtes bien embêtés et vos connexions ont des problèmes. Typiquement, une connexion ftp ou http se passe bien et certains transferts de fichiers aussi, mais dès qu'un gros fichier est transféré vous perdez la connexion.

À vous alors de découvrir la MTU à la main (ping), vous pouvez alors la forcer. Et comme vous ne voulez pas toucher à celle des autres destinations, vous la changez seulement pour une route.

$ ip route add 10.0.0.0/24 via 192.168.2.1 mtu 1300

Note : C'est la MTU garantie de 576 qui limite la taille des paquets dns et par conséquent le nombre de root server.

Note 2 : si quelqu'un connaît le sexe des mtu et mss ca m'intéresse, vous dites le mtu ou la mtu ?

Niveau :      
Résumé : apachetop ; mytop

Vous connaissiez xrestop, iftop, top, htop, ntop voici maintenant deux nouveaux, apachetop et mytop.

Apachetop

Comme vous vous en doutez, apachetop permet de monitorer l'activité d'apache. Il le fait en lisant ses fichiers de log, donc attention, si vous logguez des choses dans des fichiers inhabituels, c'est à vous de préciser quels fichiers lire. Apachetop vous affiche :

  • les statistiques depuis le début de la session, en requête, en octets, ...
  • la proportion de chacun des codes de retour (200,300,...) correspondante
  • les statistiques depuis les 30 dernières secondes, en requête, en octets, ...
  • la proportion de chacun des codes de retour (200,300,...) correspondante
  • la liste des url les plus appelées
$ apachetop -l -f /var/log/apache2/virtual1.access.log -f /var/log/apache2/virtual2.access.log

Mytop

Comme vous vous en doutez, mytop permet de monitorer l'activité de mysql. Il le fait en appelant régulièrement show processlist et en vous formatant la sortie.

  • Attention, parfois ce processus consomme beaucoup.
  • Attention, il ne lit pas le fichier .my.cnf donc les valeurs par défaut ne sont pas bonnes.
  • Attention, s'il y a une erreur de config mytop affiche sa config, y compris le mot de passe, ce qui est pas top.
  • Mytop demande une base de données valide, mais ne s'en sert pas vraiment, -> --db mysql
  • La connexion se fait par défaut en tcp -> option -S pour spécifier la socket
  • Après un changement de mode d'affichage, on est obligé de quitter (touche q)
$ mytop --db mysql --user toto --prompt

Il vous affichera les connexions avec leur thread id, leur machine, leur user et leur commande. Notez que le manuel vous indiquera quelques touches permettant d'afficher diverses informations.

Niveau :      
Résumé : lsof | grep delete ; cat /proc/$PID/fd/$FD

Suite à un article récent, vous avez constaté que certains de vos processus utilisent des fichiers qui ont été supprimés. Que se passe-t-il si vous voulez les récupérer ? (un logrotate apache qui s'est mal passé par exemple).

Pour cela, il suffit de regarder votre ligne lsof

$ lsof | grep delete

Regardez les valeurs qui en sortent :

COMMAND     PID       USER   FD      TYPE     DEVICE     SIZE       NODE NAME

Je vous conseille le man de lsof à la section OUTPUT pour les champs que vous ne comprenez pas.

Notez que FD ne contient pas toujours le file descriptor, mais parfois la méthode d'ouverture du fichier. En effet, un certain nombre de fichier n'est pas ouvert avec la commande open (exemple mmap ou le binaire lui-même). Ce qui nous intéresse est donc un nombre suivi de r, w ou u.

Remarquez que nous avons l'inode du fichier ce qui nous permettra d'imaginer une autre technique de récupération.

Maintenant récupérons :

$ cp /proc/$PID/fd/$FD /tmp/sauvegarde

Et voilà

La même chose en plus dur

Donc, je disais une autre méthode (pour laquelle il faut être root et avoir un système de fichier ext2/ext3). On va d'abord s'assurer que la technique fonctionne en lisant le contenu du fichier :

# remplacez par le /dev/kivabien (déduit de $DEVICE)
$ echo "cat <$INODE>" | debugfs /dev/sda3

Cool ça marche, maintenant nous allons pouvoir recréer l'entrée du fichier inline (pratique pour les gros fichiers quand on n'a plus de place). Attention, méthode barbare :

# pour forcer le noyau a relire les entrées après
$ sync
# on recrée l'entrée sans passer par le noyal
# attention, $NAME est relatif à la partition
$ echo "ln <$NODE> $NAME" | debugfs -w /dev/sda3

Et voila pas besoin de recopier le fichier. Par contre on a fait ça sans passer par le système, ce qui veut dire que :

  • le noyau voit toujours le fichier dans lsof | grep delete
  • le répertoire parent ne doit pas être utilisé entre le sync et le debugfs (en particulier, vous ne devez pas être dedans) sinon le noyau ne va rien comprendre et votre fichier ne sera pas restauré correctement)

C'est une méthode barbare, mais il existe aussi une méthode propre offline (qui marche, euh ... souvent) :

$ umount /dev/sda3
$ echo "ln <$NODE> $NAME" | debugfs -w /dev/sda3
$ mount /dev/sda3

Niveau :      
Résumé : lsof | grep delete

Parfois, on tombe devant le cas suivant : un logiciel quelconque vous indique une erreur de type "not enough space on disk". On regarde alors l'espace libre :

$ df -h

Et bizarrement, on constate qu'il reste encore beaucoup de place sur le disque.

Sous unix, toutes les opérations sur les fichiers sont permises même si le fichier est déjà ouvert. Grâce à cela, une application ne peut pas en bloquer d'autres en lisant le mauvais fichier. Ainsi il est possible de supprimer un fichier alors qu'il est en cours d'utilisation par une application. Mais l'application elle même ne s'en rend pas compte car seule la référence au fichier a été supprimée.

Donc une application qui détiendrait de très gros fichiers ouverts peut remplir le système de fichier sans que l'utilisateur ne s'en rende compte. Lisons tous les fichiers dans ce cas avec la commande lsof :

$ lsof | grep delete


Et au passage vous avez l'application fautive, vous pouvez la tuer si besoin (le PID est le 2e champ).

Note : Créer un fichier et l'effacer tout en le gardant ouvert est une technique parfois utilisée pour protéger ses fichiers temporaires de certaines attaques, ou pour garantir que ceux-ci seront automatiquement effacés même si l'application plante.

Niveau :      
Résumé : logs apache en php

Un petit article pour tous ceux qui ont un site sur sur un hébergement mutualisé et qui n'ont pas accès aux logs apache. C'est assez gênant, on est limité dans le choix des outils de statistiques. On a quelques outils purement php peu efficaces, ou des outils web externes qui stockent les données pour vous (mais à qui appartiennent-elles ?).

Pour permettre d'utiliser les outils courants (awstats, awffull, webalizer ...), j'ai écrit un petit script php qui génère des logs au format apache. Il y a plusieurs inconvénients, tout d'abord, on ne loggue que les appels au php, pas les images, ni les binaires ... D'autre part, le code s'exécute nécessairement avant la fin du code lui-même. Donc il y a certaines informations dont on ne dispose pas, comme la taille totale générée, le code de retour ou des éléments des en-têtes renvoyés.

Ce script s'utilise très simplement, il suffit d'inclure le fichier et éventuellement de préciser le format à utiliser et le nom du fichier de log. En pratique l'entrée de log se fera à la fin de l'exécution du php, ce qui permet de mesurer le temps d'exécution. Le logformat s'écrit exactement sous la même forme que celui d'apache, les informations inconnues seront simplement remplacées par un '-' . Notez aussi que le %r (ligne contenant la requête intégrale) est généré à partir des informations disponibles.

A mettre dans votre code php :

$alog_logFormat = "%h %l %u %t \"%r\" %>s %b"; // optionnel
$alog_logFile   = "/tmp/access.log"; // optionnel
include_once("ApacheLog.php");

Attention, le nom du fichier est relatif au script appelant.

Vous trouverez le fichier en pièce attachée à cet article.