Skip to content

Linux Attitude

Le libre est un état d'esprit

Archive

Archive for December, 2007

Niveau :      
Résumé : ldd ; readelf ; ld-linux

Savez vous ce qui se passe quand vous lancez un logiciel ? Si c'est à travers votre shell, il analyse votre ligne de commande puis d'une façon ou d'une autre appelle execve qui demande au noyau de charger le fichier en question et de l'exécuter. Si ce n'est pas le shell, un autre fera à peu près le même chose.

Vous pensez probablement que le binaire est mis en mémoire à un endroit donné et que le point d'entrée correspondant est appelé et que c'est tout. Vous auriez eu raison il y a 15 ans. Mais le format de fichier aout a été remplacé par le format elf et les choses ne se passent plus tout à fait de cette façon.

Le format elf a été inventé entre autre pour permettre le chargement dynamique de bibliothèque. Pour lancer un fichier elf, le noyau lance d'abord ce qu'il appelle un interpréteur. L'interpréteur est une information donnée dans le programme lui-même. Il apparaît dans a liste des bibliothèques chargées avec un programme qu'on voit avec la commande ldd :

$ ldd /bin/ls

Vous pouvez aussi le voir avec la commande readelf :

$ readelf -l /bin/ls

Donc en pratique lorsque vous tapez ls, le noyau comprend la commande suivante :

$ /lib64/ld-linux-x86-64.so.2 /bin/ls
# vous constatez que je suis en architecture 64 bits

D'ailleurs vous pouvez aussi lancer la commande telle quelle.

Ld-linux va à son tour lire les en-têtes elf et charger toutes les bibliothèques dont dépend le programme en question. C'est lui qui appellera le point de départ du programme. Les symboles (comme par exemple les fonctions) seront eux résolus lorsqu'ils seront appelés, le premier appel d'une fonction est donc toujours légèrement plus long que les suivants.

Sachez que vous pouvez influencer le comportement de ld-linux avec des variables d'environnement, attention des restrictions s'appliquent pour les programmes setuid. Les variables les plus courantes sont les 2 premières :


continue reading...

Niveau :      
Résumé : esd -tcp -public

Au commencement était la carte perforée, Puis il y eut le clavier et l'imprimante, qu'on améliora en terminal. Et enfin il y eut le terminal X, mais la course ne s'arrête pas là. Il nous manque encore le son.

Hé bien, nous allons nous y mettre ! Je ne vous parlerai que du cas de gnome, probablement adaptable à kde. Écrivez moi si vous avez la recette.

Tout d'abord, il vous faut une machine sous gnome et un dm (gdm, kdm, xdm ...) qui accepte les connexions XDMCP (ce n'est pas indispensable, mais tellement plus pratique).

Connectez-vous d'abord sous gnome, puis lancez

$ gstreamer-properties

Modifiez le plugin de sortie pour esd, et faites un test pour vérifier que le son fonctionne.

Maintenant déconnectez-vous et mettez-vous sur une autre machine. Lancez esd, puis xorg :

$ esd -tcp -public -nobeeps -terminate &
$ Xorg -query lamachine distante

Et voilà, après vous être connecté sous gnome, vous aurez le son pour les applications gnome.

Si vous n'êtes pas en xdmcp, ou si vous voulez faire fonctionner le son pour une application seulement, il vous faudra changer une variable juste avant de lancer l'application en question

$ export ESPEAKER=machine.avec.esd:16001

Maintenant supposons que votre machine faisant l'affichage soit un windows. C'est toujours possible !

Utilisons Xming, ainsi que esd pour windows.

Il vous faudra un Xming installé et un fichier xlaunch fonctionnel : lancez xlaunch et juste avant de lancer le serveur X, cliquez sur sauvegarder. Il vous faudra aussi avoir extrait le paquet esound pour windows.

Et créons un batch pour tout ceci :

call c:\esd.bat
c:\xming\xlaunch.exe -run c:\linux.xlaunch

Et dans le esd.bat

c:\esd\esd -tcp -public -nobeeps -terminate

Bien, sûr, adaptez à vos emplacements. Notez au passage que Xming supporte très bien l'openGL et par conséquent, un certain nombre de jeux fonctionneront mieux à distance sous windows que sous linux (moins depuis l'existence de AIGLX).

Niveau :      
Résumé : sysrq ; sysrq-trigger ; sysrqd

Sysrq, nom de la touche clavier pour system request (aussi connue sous le nom de print-screen). Les magic-sysrq sont une fonctionnalité optionnelle du noyau utilisant cette touche. Lorsqu'elle est compilée dans votre noyau et activée, elle est disponible en utilisant la combinaison de touche alt-sysrq-X où X est une touche du clavier.

Ces touches sont particulièrement utiles en situation d'urgence. Les plus importantes à mémoriser sont 'sub'. Une utilisation typique ressemblera plutôt à 'rfesiub' (pensez a laisser un peu de temps entre les touches).

Les touches les plus courantes sont :

  • b : reboot pur et simple de la machine
  • e : termine proprement (SIGTERM) tous les processus
  • f : tue un processus consommateur de mémoire (au hasard)
  • h : affiche l'aide si vous êtes en console (la touche espace aussi)
  • i : tue (SIGKILL) tous les processus
  • k : tue les processus lancés dans la console en cours (dont xorg si c'est le cas), Cela permet de récupérer l'accès au clavier. Certaines mesures de sécurité exigent de l'utiliser avant de se logguer en console pour tuer d'éventuels keyloggers.
  • r : réinitialise le mode de fonctionnement du clavier (à la suite d'un crash de xorg par exemple)
  • s : synchronise les systèmes de fichier (pour ne pas avoir de problèmes avant un reboot violent)
  • u : passe les systèmes de fichier en lecture seule
  • 0-9 : change le loglevel des messages du noyau

Et d'autres plus rares :

  • c : lance un kexec
  • d : affiche les locks en cours
  • m : affiche un dump mémoire
  • n : utilisé pour le temps réel
  • o : éteint la machine (ne marche pas toujours)
  • p : affiche les registres du processeur
  • q : affiche les timers
  • t : affiche des informations sur les processus
  • w : affiche les processus bloqués

Il est possible de désactiver l'usage de ces fonctionnalités

$ echo 0 > /proc/sys/kernel/sysrq

Ou plus finement avec un nombre supérieur à 1 (voir /usr/src/linux/Documentation/sysrq.txt).

Et si vous êtes à distance vous pouvez faire appel à une de ces fonctions en ligne de commande :

$ echo s > /proc/sysrq-trigger

Et si vous êtes encore plus à distance (ssh ne fonctionne plus), il y a aussi moyen de les déclencher avec sysrqd. Sysrqd est un démon qui doit tourner sur la machine (bloquée donc bon courage). Et à ce moment :

# port 4094
$ telnet crashed.machine sysrqd

Niveau :      
Résumé : Unicode ; utf8

Qu'est-ce que l'unicode ?

L'unicode est une table de caractères. Tout comme l'ascii, l'ebd, l'iso-8859-1 ...

Cette table a pour but de prendre en compte touts les caractères disponibles dans le monde et ainsi résoudre les problèmes causés par les différentes tables. Cette table fait 1 million d'entrées (0x10FFFF pour être précis), mais rassurez-vous elle n'est pas entièrement remplie. Elle est divisée en blocs, chaque bloc correspondant à une catégorie de caractère, permettant ainsi l'évolution de la table sans devoir en changer l'ordonnancement.

Cette table est une liste de caractères et en pratique son utilisation se fait à travers une police de caractère (laquelle va représenter un ou plusieurs blocs). Bien évidemment avec une table aussi longue, il est difficile de trouver une police qui la couvrirait entièrement. Sur votre machine, il y a donc un grand nombre de polices parmi lesquelles on va chercher pour trouver le caractère qui nous intéresse.

C'est le consortium unicode qui développe cette table ainsi que les règles qui vont avec. Nous en sommes à la 5e version d'unicode. Pour un peu plus d'explications, suivez le guide.

Qu'est-ce que l'utf8 ?

L'utf8 est un encodage, tout comme l'est le 8 bits pour l'ascii. Il est juste un peu plus évolué. On pourrait donc imaginer une utilisation de l'utf8 pour encoder une table autre que l'unicode. Le but de l'utf8 est d'encoder la table unicode tout en évitant de casser un certain nombre d'applications existantes. Il profite du fait que l'ascii se retrouve directement sur les 127 premiers caractères de l'unicode. Tous les caractères de ce bloc restent inchangés et sont codés sur 8 bits. Par contre les autres caractères sont codés sur un nombre variables de bits (multiples de 8).

Il existe d'autres encodages que l'UTF-8 pour l'unicode. L'UCS-2, l'UTF-16, l'UCS-4, l'UTF-32 sont en général plus efficaces en taille que l'utf8 pour les caractères non latins. Par contre ils cassent les applications ascii.

Notez que tous ces encodages sont dépendants de la boutitude (endianness) de votre processeur. C'est pourquoi ils autorisent l'utilisation de caractères spéciaux au début des fichiers texte pour la détecter et permettre le transfert d'un tel fichier d'une architecture à une autre.

Ce qui explique que parfois vous voyiez quelques caractères bizarres au début de vos fichiers texte s'ils sont lus avec des applications qui ne le supportent pas. C'est particulièrement embêtant pour le php qui ne les reconnaît pas et les affiche en début de page (et empêche l'utilisation de header).

Pourquoi l'utiliser (ou pas) ?

L'utf8 est un encodage qui permet une transition relativement simple vers un encodage qui vous permettra de communiquer avec le monde entier. Nombreux sont ceux qui l'utilisent, sa compatibilité avec l'ascii fait qu'il est le seul à pouvoir fonctionner directement avec des protocoles ascii comme l'irc.

Par contre ces encodages créent une différence entre caractère et octets et pour éviter la confusion les développeurs doivent utiliser des fonctions qui calculent cette différence. Dans le cas de l'utf8, ce calcul est compliqué car les caractères sont tous potentiellement de taille différente. Au contraire, pour l'UTF-32 il s'agit d'une simple division par 4.

Un application utilisant l'utf-8 a donc le choix entre faire la conversion (consommateur en mémoire) ou travailler sur des chaînes UTF-8 (très consommateur en temps). Les opposants à l'utf8 vous diront donc que la migration va ralentir la plupart de vos applications. Ce qui est vrai. Peut-être que la voie à suivre serait celle de l'utf-32, ou l'utf-16 comme java ...

Niveau :      
Résumé : inetd ; xinetd

Écrire un service c'est facile. La preuve en ligne avec un serveur qui dit bonjour en shell en 5mn.

Tout d'abord codons un script qui vous dit bonjour.

#!/bin/sh
echo "Quel est votre nom ?"
read nom
echo "Bonjour $nom"

Appelons-le /srv/tests/serveur.sh et testons le :

$ chmod +x /srv/tests/serveur.sh
$ /srv/tests/serveur.sh

Et maintenant transformons tout ça en service. Pour cela il nous faut un inetd fonctionnel, soit le vieux netkit-inetd, soit xinetd, soit le plus récent openbsd-inetd.

Ajoutons une configuration inetd (netkit ou openbsd):

1234    stream  tcp     nowait  root    /srv/tests/serveur.sh

Qui nous donne pour xinetd :

service 1234
{
        type            = UNLISTED
        socket_type     = stream
        protocol        = tcp
        user            = root
        wait            = no
        server          = /srv/tests/serveur.sh
        port            = 1234
}

Testez :

$ telnet localhost 1234

C'est prêt !

Niveau :      

Résumé : mod_setenvif ; mod_header

Savez-vous que la lecture et l'écriture de variables d'environnement sont particulièrement lentes ?

Je suis tombé récemment sur un problème de performances sur apache. Après analyse, il s'est avéré que le coupable est mod_setenvif. Nous utilisions ce dernier pour modifier des header avec cette méthode :

SetEnvIf Cookie (.*) value
SetEnvIf value (.*) value debut$1fin
RequestHeader Cookie value

Or à partir d'apache 2.2.4, il est possible d'utiliser une expression régulière directement avec RequestHeader :

RequestHeader edit Cookie (.*) debut$1fin

Le temps de réponse par requête a été divisé par 5. Bien sûr on peut accuser les expressions régulières, mais vous constaterez que ce sont les 2 mêmes.

De plus, un test a montre que setenv était environ dix fois plus lent qu'un simple malloc. Même si on ne peut pas vraiment comparer, l'explication me convient.

Niveau :      
Résumé : ssh-agent ; ssh-add

Comme je l'ai déjà expliqué il y a quelque temps, il est possible grâce à ssh-agent de réutiliser sa clé ssh. C'est bien, mais en pratique, comment fonctionne cet agent ?

L'agent est un processus qui gère vos clés. Il communique à travers une socket. Ssh-add lit une clé sur votre compte utilisateur et demande de l'ajouter à travers la socket. Le client ssh lui va demander les clés disponibles à la socket pour se connecter quelque part.

Lorsque vous vous connectez en ssh avec l'option -A (en général présente par défaut), ssh tunnelle simplement une socket sur la machine de destination vers la machine de départ. Ce qui veut dire que l'agent que vous utiliserez sur cette machine distante est celui de la machine d'origine. Autrement dit vous disposez des clés de la machine de départ, mais ssh-add ajoutera des clés de la machine de destination sur la machine de départ. Testez, vous verrez c'est amusant.

Maintenant puisque la communication se fait à travers une socket, n'importe quel processus peut communiquer avec elle. C'est en partie vrai, car les socket respectent les droits établis par le système de fichier. Donc en gros seul root et vous peuvent communiquer avec cette socket. Ainsi, si vous vous connectez à une machine sur laquelle vous avez déjà un agent qui tourne vous pouvez le réutiliser.

Pour avoir la liste des agents disponibles :

$ ls -ld /tmp/ssh-* | grep `whoami`

Mais attention, cela contient aussi les socket ouvertes pas les ssh qui se sont connectés avec le forwarding d'agent. Pour les différencier, vous devrez utiliser la date. Vous pouvez aussi avoir quelques info supplémentaire avec netstat :

$  netstat -lnp --unix | grep ssh-

Maintenant, pour la réutiliser, il suffit simplement d'affecter la variable adaptée :

$ export SSH_AUTH_SOCK=/tmp/ssh-lwksuR6740/agent.6740
$ ssh nouvelle.machine
# et voila !

C'est bien pratique, mais n'oubliez pas une chose, cela signifie que root a accès à vos clés sans passphrase. Cela signifie aussi que vous ne devez jamais forwarder votre agent lorsque que vous vous connectez sur une machine avec un compte partagé. Cela signifierait que vos associés auraient accès à toute les clés dont vous disposez sur votre machine de départ. Si vous ne pouvez pas vous en passer, essayez au moins d'utiliser l'option -t de ssh-add qui limite en temps la validité de la clé.

$ ssh-add -t 600