Archive for septembre 2011

[LECTURE] The Practice of Programming

Livres | Publié par cpb
sept 26 2011

Entre un album de la B.D. The Walking Dead et le dernier roman d’Umberto Ecco (le cimetière de Prague), j’ai relu récemment avec grand plaisir un petit livre très intéressant :

The Practice of Programming de Brian W. Kernighan et Rob Pike.

Je ne peux qu’encourager chaudement les programmeurs à lire avec attention cet ouvrage, plein d’excellents conseils, tant sur la forme du code (style, conventions, etc.) que sur le fond (efficacité des algorithmes, qualité des interfaces, etc.). L’accent est mis sur les deux points essentiels, à mon avis, représentant la qualité d’un programme : la lisibilité du code et la robustesse du logiciel.

J’ajouterai prochainement sur ce blog une page de bibliographie, avec les ouvrages qui me semblent essentiels pour le développeur.

Bonne lecture…

Efficacité des IPC : les files de messages Posix

Linux, Temps-réel | Publié par cpb
sept 17 2011

Un client m’a demandé récemment de le conseiller sur le choix d’un mécanisme de communication entre processus pour transférer rapidement des données entre deux applications. Il existe plusieurs systèmes d’IPC (Inter Process Communication), chacun avec ses avantages et inconvénients, et j’ai eu envie de les comparer pour déterminer le plus rapide, en fonction du type de données à transférer. Ce premier article est consacré aux message queues, les files de messages.

Posix message queues

Dans l’API standard Unix, il existe deux implémentations pour les files de message : les message queue Système V et les message queues Posix. Les premières sont apparues dans les systèmes Unix des années 80. Elles étaient efficaces mais disposaient d’une interface de programmation un peu bancale : il fallait réserver dans le bloc de données à envoyer quatre octets pour stocker la priorité du message. Ceci n’est pas très dérangeant, mais pouvait nécessiter quand même une recopie systématique du message s’il était produit par une bibliothèque indépendante par exemple. En outre, comme l’ensemble des IPC Système V, ces message queues ne s’intégraient pas dans le concept général des descripteurs de fichiers Unix.

Dans les années 90, avec la volonté d’uniformiser les interfaces de programmation des systèmes Unix par l’intermédiaire de la norme Posix, sont apparues de nouvelles files de message, dont l’interface est plus simple, et dont les descripteurs sont plus proches de ceux des fichiers. Les message queues Posix n’ont été intégrées qu’assez tardivement dans Linux (dans la version 2.6.10 si ma mémoire est bonne).

Les appels-système qui nous concernent sont :

#include <mqueue.h>

mqd_t   mq_open    (const char * nom, int flags, mode_t mode, struct mq_attr * attr);
int     mq_send    (mqd_t mq, const char * msg, size_t lg, unsigned int prio);
ssize_t mq_receive (mqd_t mq, char * msg, size_t lg, unsigned int * prio);
int     mq_close   (mqd_t mq);
int     mq_unlink  (const char * nom);

On peut se reporter aux pages de manuels respectives de ces fonctions pour avoir plus de détails.

Transfert à faible débit

Voici un petit programme qui va nous permettre de mesurer le temps de transfert d’un message d’un processus à un autre, pour des messages relativement courts (8 octets), avec un faible débit.

Nous créeons deux processus, un émetteur qui envoie toutes les secondes dans la file un message contenant l’heure actuelle (secondes + microsecondes) et un récepteur qui compare la valeur reçue avec l’heure qu’il vient de lire lui-même. Notez que sur la plupart des machines actuelles, la lecture de l’heure à l’aide de gettimeofday() dure environ une micro-seconde. Le débit étant faible (un message par seconde), nous pouvons afficher directement la différence (en micro-secondes).

Pour que le fonctionnement soit optimal, il conviendra de placer l’émetteur et le récepteur sur deux cœurs ou deux processeurs différents.

Voici le premier émetteur qui envoie régulièrement l’heure dans la file de messages. Les fichiers-source ainsi que le fichier Makefile se trouvent dans cette archive.

emetteur-01.c:
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

int main(int argc,char * argv[])
{
    mqd_t mq;
    struct timeval heure;

    if (argc != 2) {
        fprintf(stderr, "usage: %s nom_file_message\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    mq = mq_open(argv[1], O_WRONLY | O_CREAT, 0600, NULL);
    if (mq == (mqd_t) -1) {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    while (1) {
        gettimeofday(& heure, NULL);
        mq_send(mq, (char *) & heure, sizeof(heure), 1);
        sleep(1);
    }
    return EXIT_SUCCESS;
}

Et voici le premier récepteur. On notera que dans l’API des message queues Posix, la fonction mq_receive() réclame un buffer capable de contenir (au moins) le plus grand message susceptible d’être véhiculée par la file. Cette valeur est obtenue avec mq_getattr().

recepteur-01.c:
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

int main(int argc,char * argv[])
{
    mqd_t mq;
    int taille;
    char * buffer;
    long int duree;
    struct mq_attr attr;
    struct timeval heure;
    struct timeval * recue;

    if (argc != 2) {
        fprintf(stderr, "usage: %s nom_file_message\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    mq = mq_open(argv[1], O_RDONLY | O_CREAT, 0600, NULL);
    if (mq == (mqd_t) -1) {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }

    if (mq_getattr(mq, & attr) != 0) {
        perror("mq_getattr");
        exit(EXIT_FAILURE);
    }
    taille = attr.mq_msgsize;
    buffer = malloc(taille);

    if (buffer == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    recue = (struct timeval *) buffer;
    while (1) {
        mq_receive(mq, buffer, taille, NULL);
        gettimeofday(& heure, NULL);
        duree  = heure.tv_sec - recue->tv_sec;
        duree *= 1000000;
        duree += heure.tv_usec - recue->tv_usec;
        fprintf(stdout, "%ld usec\n", duree);
    }
    return EXIT_SUCCESS;
}

Pour l’exécution, je lance sur un premier terminal :

$ taskset -c 0 ./recepteur-01 /essai

Notez que le nom de la file de message (essai) doit obligatoirement être précédé d’un slash ‘/‘.

Puis sur un second terminal :

$ taskset -c 1 ./emetteur-01 /essai

Les résultats s’affichent dans le premier terminal :

$ taskset -c 0 ./recepteur-01 /essai
16 usec
14 usec
14 usec
15 usec
13 usec
12 usec
13 usec
15 usec
14 usec
(Contrôle-C)
$

Sur ce système – Intel Core 2 Quad avec Linux 2.6.38 générique Ubuntu – le temps de passage d’un message sporadique est donc d’une quinzaine de micro-secondes. Essayons à présent avec un débit plus élevé.

Transfert d’un message à haut débit

Nous allons à présent envoyer des messages aussi vite que possible. Pour cela, nous retirons simplement le sommeil d’une seconde de l’exemple précédent :

emetteur-02.c:
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

int main(int argc,char * argv[])
{
    mqd_t mq;
    struct timeval heure;

    if (argc != 2) {
        fprintf(stderr, "usage: %s nom_file_message\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    mq = mq_open(argv[1], O_WRONLY | O_CREAT, 0600, NULL);
    if (mq == (mqd_t) -1) {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    while (1) {
        gettimeofday(& heure, NULL);
        mq_send(mq, (char *) & heure, sizeof(heure), 1);
    }
    return EXIT_SUCCESS;
}

Pour la réception, le problème est plus compliqué. Nous ne pouvons pas nous permettre de faire un fprintf() à chaque message, car cela perturberait l’exécution du programme. J’ai donc décidé d’afficher régulièrement les durées minimale, maximale et moyenne sur une période donnée, de l’ordre d’une seconde (calculée avec les résultats précédents).

recepteur-02.c:
#include <fcntl.h>
#include <mqueue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

int main(int argc,char * argv[])
{
    mqd_t mq;
    int taille;
    char * buffer;
    struct mq_attr attr;
    struct timeval heure;
    struct timeval * recue;

    int nb_messages;
    long int duree;
    long int duree_max;
    long int duree_min;
    long int somme_durees;

    if (argc != 2) {
        fprintf(stderr, "usage: %s nom_file_message\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    mq = mq_open(argv[1], O_RDONLY | O_CREAT, 0600, NULL);
    if (mq == (mqd_t) -1) {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }

    if (mq_getattr(mq, & attr) != 0) {
        perror("mq_getattr");
        exit(EXIT_FAILURE);
    }

    taille = attr.mq_msgsize;
    buffer = malloc(taille);
    if (buffer == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    recue = (struct timeval *) buffer;
    while (1) {
        nb_messages = 0;
        duree_max = 0;
        duree_min = -1;
        somme_durees = 0;
        do {
            mq_receive(mq, buffer, taille, NULL);
            gettimeofday(& heure, NULL);
            duree  = heure.tv_sec - recue->tv_sec;
            duree *= 1000000;
            duree += heure.tv_usec - recue->tv_usec;
            if (nb_messages > 0) { // Ignorer le premier message (retarde)
                if (duree_max < duree)
                    duree_max = duree;
                if ((duree_min == -1) || (duree_min > duree))
                    duree_min = duree;
                somme_durees += duree;
            }
            nb_messages ++;
        } while (nb_messages < 100000); // arbitraire, de l'ordre de la seconde
        fprintf(stdout, "min =%3ld   max =%3ld moy=%5.1f\n",
            duree_min, duree_max, ((float) somme_durees) / (nb_messages - 1));
    }
    return EXIT_SUCCESS;
}

Comme précédemment, je lance les deux processus sur deux cœurs distincts. Voici les résultats :

$ taskset -c 0 ./recepteur-02 /essai
min =  1   max = 78 moy=  7.8
min =  1   max =102 moy=  6.9
min =  1   max = 65 moy=  7.5
min =  1   max = 61 moy=  7.4
min =  1   max = 70 moy=  5.5
min =  1   max = 64 moy=  6.6
min =  1   max = 63 moy=  5.7
min =  1   max = 75 moy=  5.5
min =  1   max = 84 moy=  5.6
min =  1   max = 71 moy=  6.8
min =  1   max = 68 moy=  6.3
min =  1   max =688 moy=  6.8
min =  1   max = 69 moy=  5.7
min =  1   max = 69 moy=  7.7
min =  1   max = 68 moy=  6.7
min =  2   max = 69 moy=  7.8
min =  2   max =102 moy=  6.7
min =  1   max = 70 moy=  5.6
min =  1   max = 73 moy=  6.3
min =  1   max = 63 moy=  6.2
min =  1   max = 71 moy=  4.8
min =  1   max = 87 moy=  5.5
min =  3   max = 74 moy=  6.4
min =  1   max = 71 moy=  4.1
min =  1   max = 71 moy=  8.1
min =  1   max = 80 moy=  5.8
min =  1   max = 76 moy=  7.2
min =  1   max =177 moy=  7.9
min =  1   max = 55 moy=  5.4
min =  2   max = 74 moy=  8.5
min =  4   max = 74 moy=  8.2
min =  3   max = 77 moy=  6.4
^C
$

En fait, le temps moyen lorsqu’on envoie des messages fréquents est plutôt de l’ordre de 8 micro-secondes alors qu’avec des messages rares, il était de 15 micro-secondes environ. Il y a naturellement de temps à autres des temps de transferts plus long à cause de l’activité du système. Si l’on ordonnance en temps-réel les deux tâches, les durées maximales sont plus stables. Il faut toutefois obtenir les droits root.

# chrt -f 40 taskset -c 1 ./emetteur-02 /essai

et sur l’autre terminal

#  chrt -f 40 taskset -c 0 ./recepteur-02 /essai
min =  1   max = 80 moy=  6.1
min =  1   max = 67 moy=  6.3
min =  1   max = 64 moy=  5.3
min =  1   max = 70 moy=  8.2
min =  3   max = 67 moy=  8.8
min =  2   max = 62 moy=  5.7
min =  2   max = 62 moy=  7.8
min =  1   max = 59 moy=  7.5
min =  1   max = 63 moy=  6.6
min =  2   max = 63 moy=  6.4
min =  2   max = 66 moy=  7.2
min =  1   max = 58 moy=  5.9
min =  1   max = 55 moy=  6.4
min =  2   max = 65 moy=  8.5
min =  2   max = 71 moy=  9.0
min =  2   max = 81 moy=  8.5
min =  1   max = 63 moy=  7.5
min =  1   max = 66 moy=  8.1
min =  3   max = 69 moy=  8.7
min =  2   max = 64 moy=  7.2
min =  1   max = 62 moy=  5.7
min =  1   max = 63 moy=  7.2
min =  1   max = 65 moy=  8.2
min =  2   max = 64 moy=  7.2
min =  1   max = 68 moy=  6.0
min =  1   max = 69 moy=  6.3
min =  1   max = 60 moy=  6.5
min =  1   max = 67 moy=  7.3
min =  1   max = 62 moy=  7.1
min =  2   max = 62 moy=  8.6
min =  1   max = 57 moy=  6.7
min =  2   max = 72 moy=  8.7
min =  1   max = 85 moy=  7.9
min =  1   max = 72 moy=  7.1
min =  1   max = 58 moy=  5.0
min =  1   max = 64 moy=  5.8
min =  2   max = 73 moy=  7.4
min =  1   max = 55 moy=  7.1
min =  1   max = 62 moy=  7.3
min =  1   max = 66 moy=  8.3
min =  1   max = 58 moy=  5.2
min =  5   max = 75 moy=  9.3
min =  3   max = 68 moy=  9.1

Nos messages sont très courts, deux entiers de 32 bits, soient 8 octets en tout. J’ai fait quelques expériences en augmentant leur taille (jusqu’à 64 ko), sans constater de différence sensible de temps de passage d’un message.

Conclusion

Nous pouvons donc estimer que sur ce système le temps de transfert d’un message d’un processus à l’autre par les files Posix est d’environ 8 micro-secondes en moyenne, avec des pics de quelques dizaines de micro-secondes. Nous comparerons ces valeurs avec d’autres mécanismes IPC dans les prochains articles.

[ACTU] kernel.org down…

Actualité, Linux | Publié par cpb
sept 09 2011

Depuis une semaine, un sujet de discussion s’est répandu sur la plupart des sites consacrés à Linux : la sécurité du site http://www.kernel.org/ a été compromise durant la fin août. Ce serveur est le point central de distribution des sources des noyaux Linux.

L’intrus a apparemment obtenu des accréditations SSH par l’intermédiaire d’une machine appartenant à Hans Peter Anvin. Les traces découvertes le 28 août suggèrent que le système SSH et des scripts de démarrage ont été compromis, entre autres. Naturellement les administrateurs ne divulguent pas encore le détail de l’attaque.

Le sujet d’inquiétude principal porte sur les sources des noyaux Linux. L’intrus a-t-il pu insérer dans le code source du noyau une backdoor, rendant faillibles les machines qui utiliseront ce kernel ? Aujourd’hui rien ne permet de l’affirmer, mais rien ne permet non plus d’affirmer à coup sûr le contraire.

La probabilité est très faible en ce qui concerne le dépôt GIT des sources, car chaque fichier est surveillé indépendamment, et le code de hachage d’une version du noyau tient compte de ceux des versions précédentes. N’oublions pas que des centaines de développeurs possèdent un miroir à jour du noyau ce qui rend impossible l’introduction invisible d’une faille de sécurité. Personnellement je n’ai pas vraiment d’inquiétude pour le dépôt GIT des sources en développement, mais plutôt pour les archives .tar.bz2 plus anciennes. Notamment les versions longterm du kernel, qui peuvent être téléchargées automatiquement par des outils de développement embarqué (de type Buildroot par exemple).

L’attaque a eu lieu en utilisant un rootkit connu nommé Phalanx, qui n’a pas été particulièrement adapté pour sa cible. Ceci laisse supposer que les intrus n’avaient pas les compétences techniques pour forcer une somme de contrôle SHA-1 comme on en utilise pour signer les archives du noyau.

Aujourd’hui le serveur kernel.org est inaccessible.

Linus Torvalds utilise pour le moment son compte sur Github pour centraliser le développement. Si vous souhaitez obtenir une copie des sources de Linux, vous devrez utiliser le dépôt suivant :

[tmp]$ git clone https://github.com/torvalds/linux.git linux-3
Cloning into linux-3...
remote: Counting objects: 2135359, done.
remote: Compressing objects: 100% (336683/336683), done.
remote: Total 2135359 (delta 1781604), reused 2132480 (delta 1778804)
Receiving objects: 100% (2135359/2135359), 424.17 MiB | 1.10 MiB/s, done.
Resolving deltas: 100% (1781604/1781604), done.
[tmp]$

Espérons que kernel.org retrouve rapidement son intégrité, et sa disponibilité…

 

Création d’un système Gnu/Linux live personnalisé

Formations, Linux | Publié par cpb
sept 02 2011

Je vais être amené à présenter très prochainement une session de formation professionnelle sur Linux temps-réel et embarqué dans une entreprise où l’administrateur système ne veut pas installer Gnu/Linux sur les postes de formation ! N’ayant pas les compétences pour installer une partition Linux, il ne tolère que le boot sur systèmes live (CD ou Clé USB).

Il existe de nombreuses distributions qui proposent des systèmes live préconfigurés et opérationnels, toutefois plusieurs problèmes se posent dans ce cas particulier

  • J’ai besoin de la toolchain Gnu complète (gcc, gdb, gprof, gcov, g++, etc.) rarement présente sur les systèmes live.
  • Une partie importante de ma présentation est orientée sur l’environnement Eclipse, qui est absent des systèmes préconfigurés.
  • Je fournis aux participants de nombreux exemples et fichiers de configuration qui doivent être rajoutés dans les répertoires de travail. Habituellement je fournis ces éléments sur CD-Rom, mais si nous démarrons sur un CD live, le lecteur sera déjà occupé !
  • En outre, nous devons télécharger et compiler de nombreux packages (Buildroot, Busybox, le kernel Linux lui-même, Xenomai, Dropbear, etc.) L’utilisation du réseau et l’accès Internet depuis les salles de formation étant souvent problématique – d’autant plus depuis un système live – je voudrais pré-télécharger les archives.

Au vu de ces points, il apparaît qu’il faut pouvoir construire un système Linux live personnalisé. A nouveau, il existe plusieurs projets – plus ou moins aboutis – qui permettent d’ajuster le système live. Après plusieurs expérimentations, j’ai choisi d’utiliser le projet « Debian Live Build » et son utilitaire lb (Live Build) pour lequel il existe de très bonnes documentations sur le web, et je récapitulerai simplement ici les opérations nécessaires dans mon cas, dans l’espoir que cela puisse être utile à d’autres.

Principe

Le principe est relativement simple. Je pars d’une distribution Debian ou Ubuntu fraichement installée avec les options par défaut.

Je ne fais preuve d’aucun sectarisme entre les principales distributions Linux, j’utilise régulièrement des Debian, Fedora, Ubuntu, Centos, RHEL et autres Gentoo. J’ai peut-être une meilleure connaissance des Fedora, mais j’apprécie les avantages de chacune des autres. Pour le propos de cet article, le système Live Build de Debian est supérieur aux autres environnements du même genre.

Certaines opérations nécessitent obligatoirement les privilèges root, d’autres peuvent sûrement être effectuées avec les droits d’un utilisateur normal, mais j’ai réalisé l’ensemble des manipulations avec un shell root (en utilisant sudo -i).

[~]# apt-get install -y live-build

On crée un répertoire de travail (livekey dans mon cas) puis on y exécute la configuration de Live Build :

[~] # mkdir livekey 
[~] # cd livekey 
[livekey]# lb config options...

Les options serons détaillées plus bas et :

[livekey]# lb build

En sortie, nous obtenons une image prête à être gravée sur CD/DVD ou transférée sur une clé USB.

La complexité va venir des options à passer à lb config pour ajuster le système live et préinstaller les fichiers que l’on veut fournir aux futurs utilisateurs.

Script de création

Dès que je dois répéter à plusieurs reprises les mêmes opérations – pour ajuster mon système en l’occurrence – je les regroupe toujours dans un script shell. C’est ce que je ferai ici.

La première version (script-livekey-1.sh) est très simple

#! /bin/sh
rm -rf config/
lb clean

lb config \
   --distribution   "squeeze"           \
   --archive-areas  "main contrib"      \
   --packages-lists "gnome-desktop"     \
   --packages "gcc gdb gedit g++ make"  \
   --clean                              \
   --memtest none                       \
   --syslinux-timeout 20                \
   --interactive true                   \

lb build

Les répertoires config, cache et chroot sont créés pendant la phase lb build. J’efface systématiquement le répertoire config au démarrage du script pour éviter les incohérences entre plusieurs exécutions successives. De même la commande lb clean sert-elle à « faire le ménage » dans les fichiers générés précédemment.

Voici la signification des options ajoutées à « lb config« 

  • --distribution squeeze : il s’agit de la distribution Debian stable actuelle, ce que l’on peut naturellement modifier au besoin ;
  • --archive-areas "main contrib" : les sources de paquets, en fonction de leur licences,
  • --packages-lists "gnome-desktop" : il s’agit d’une liste préconfigurée de packages permettant de configurer une station graphique avec environnement Gnome. Il est évidemment possible de préférer kde-desktop ou xfce-desktop.
  • --packages "..." : la liste des packages supplémentaires que je souhaite installer. Nous en reparlerons plus bas, j’ai simplement indiqué ici les éléments essentiels de la toolchain Gnu.
  • --clean : effacer les sous-répertoires inutiles dans config/ et chroot/,
  • --memtest none : ne pas proposer d’option de vérification de la mémoire dans le menu de démarrage de Grub ;
  • --syslinux-timeout 20 : Démarrer sur l’image par défaut après 20 secondes d’affichage du menu Grub ;
  • --interactive true : pour la mise au point, ceci nous permet d’accéder temporairement au futur environnement live (par le biais d’un chroot) pour vérifier son bon état. Cette option est supprimée après la mise au point.

 

 

Lançons notre script :

[livekey]# chmod 755 script-livekey-1.sh 
[livekey]# ./script-livekey-1.sh 
P: Cleaning chroot
P: Considering defaults defined in /etc/live/build.conf
P: Creating config tree
P: Setting up cleanup function
P: Begin caching bootstrap stage...
P: Begin bootstrapping system...
P: If the following stage fails, the most likely cause of the problem is with your mirror configuration or a caching proxy.
P: Running debootstrap (download-only)...
I: Retrieving Release
[...]

Après quelques (dizaines de) minutes de téléchargement et préparation des packages, la construction s’interrompt avec un shell chrooté (c’est-à-dire qui s’exécute dans une sous-arborescence du système de fichiers de l’hôte). Nous pouvons alors avoir une idée des commandes disponibles sur le futur système live. Par exemple, je vais vérifier le fonctionnement de GCC.

update-initramfs: Generating /boot/initrd.img-2.6.32-5-686
update-initramfs: Generating /boot/initrd.img-2.6.32-5-486
P: Begin interactive build...
P: Pausing build: starting interactive shell...
(live)root@SRVR:/# gcc --version
gcc (Debian 4.4.5-8) 4.4.5
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
(live)root@SRVR:/#

C’est le moment adéquat pour vérifier s’il manque des commandes pour le futur système, et modifier en conséquence le script de construction. En ce qui me concerne, j’ai modifié la ligne packages du script (script-livekey-2.sh) ainsi :

    --packages "bison eclipse exuberant-ctags flex gcc gdb gedit gettext g++ qemu libncurses5-dev make minicom vim"  \

Nous pouvons quitter ce shell « chrooté » pour continuer la construction de l’image.

(live)root@SRVR:/# exit
logout
P: Deconfiguring file /etc/kernel-img.conf
P: Deconfiguring file /etc/apt/sources.list
[...]
Building dependency tree
Reading state information... Done
P: Deconfiguring file /etc/hostname
P: Deconfiguring file /bin/hostname
P: Deconfiguring file /etc/resolv.conf
P: Deconfiguring file /etc/hosts
P: Begin unmounting filesystems...
[livekey]# ls -l
total 1105980
drwxr-xr-x  6 root root       4096 2011-09-02 12:17 binary
-rw-r--r--  1 root root 1132462080 2011-09-02 12:18 binary-hybrid.iso
-rw-r--r--  1 root root       2162 2011-09-02 12:17 binary.list
-rw-r--r--  1 root root      31490 2011-09-02 12:17 binary.packages
drwxr-xr-x  6 root root       4096 2011-09-02 12:17 cache
drwxr-xr-x 20 root root       4096 2011-09-02 12:18 chroot
drwxr-xr-x  2 root root       4096 2011-09-02 11:14 config
-rwxr-xr-x  1 root root        396 2011-09-02 11:13 script-livekey-1.sh
[livekey]#

Une fois la construction terminée, nous obtenons un fichier image binary-hybrid.iso que l’on peut soit graver un DVD, soit transférer sur une clé USB. C’est cette dernière option que je choisis pendant la mise au point. J’insère donc une clé USB de 8Go, dont la partition /dev/sdg1 se trouve montée automatiquement. Avant d’écrire sur la clé, il faut donc démonter cette partition, puis nous effectuons un transfert direct sur le périphérique bloc représentant la clé (/dev/sdg chez moi). Attention à bien vérifier le nom du périphérique sur votre système.

[livekey]# umount /dev/sdg1
[livekey]# dd if=binary-hybrid.iso of=/dev/sdg bs=4M
270+0 enregistrements lus
270+0 enregistrements écrits
1132462080 octets (1,1 GB) copiés, 236,551 s, 4,8 MB/s
[livekey]#

On peut alors insérer la clé sur une machine de test, et démarrer dessus. Plusieurs points posent problème, que je vais aborder l’un après l’autre :

  • Par défaut la localisation est américaine avec un clavier Qwerty, je désire disposer d’une disposition Azerty du clavier.
  • Il faut que je place quelque part sur mon système live les archives pré-téléchargées des packages à compiler durant la session de formation.
  • L’environnement Eclipse fonctionne, mais il n’y a pas le plug-in CDT (C/C++ Development Tool) pour la programmation en C. Le package eclipse-cdt qui implémente ce plug-in n’existe pas dans la version Debian Squeeze mais seulement dans Sid.
  • Les modifications apportées par l’utilisateur à son environnement ou aux fichiers de son répertoire personnel ne perdurent pas d’une exécution à l’autre.

 

Localisation et clavier

Pour gérer la localisation et la configuration du clavier, il suffit d’indiquer les valeurs voulues sur la ligne de commande du kernel, avec les options locales et keyboard-layouts.

Ceci s’obtient facilement en ajoutant la ligne suivante dans les arguments de lb config :

   --bootappend-live "locales=fr_FR.UTF-8 keyboard-layouts=fr"  \

Après construction et redémarrage, l’interface utilisateur de Gnome est bien en français et le clavier est configuré en disposition Azerty.

Fichiers supplémentaires dans le système

Je souhaite ajouter quelques archives pré-téléchargées dans le répertoire /usr/local/src du système live. Pour cela il faut les copier dans le répertoire :

~/livekey/config/chroot_local-includes/usr/local/src/

Ceci sera réalisé dans la première partie de mon script, avant la commande lb config.

Packages Debian supplémentaires

Pour ajouter un package ne se trouvant dans la distribution Squeeze, il faut le télécharger au préalable et le placer dans le répertoire

~/livekey/config/chroot_local-packages/

C’est ce que j’ai fait avec le package eclipse-cdt_6.0.2-1_i386.deb téléchargé depuis le dépot Sid.

Après reconstruction et démarrage, l’environnement Eclipse de mon système live est bien doté du plug-in CDT.

Persistance des données utilisateur

Pour que la configuration et les données de l’utilisateur soient conservées d’une session à l’autre, il faut les sauvegarder sur la clé USB que nous avons utilisé pour démarrer. Plusieurs solutions s’offrent à nous, mais je trouve que la plus simple est de disposer d’une partition dédiée sur la même clé, qui sera montée en guise de répertoire /home. L’avantage est que cette méthode est prévue d’avance et bien intégrée dans Live Build.

Le principe consiste à créer une seconde partition derrière la première, qui sera formattée avec un nom de volume home-rw. D’autre part, l’argument persistant sera ajouté sur la ligne de commande du noyau. Lors du démarrage, la partition sera identifiée par son nom, et montée automatiquement dans le répertoire /home.

Nous modifions donc la ligne boot-append-live de lb config pour ajouter le mot-clé persistent ainsi :

   --bootappend-live "locales=fr_FR.UTF-8 keyboard-layouts=fr persistent" \

Un problème se pose toutefois : les images ISO hybrides (susceptibles d’être gravées sur CD/DVD ou transférées sur clé USB) ne contiennent pas de table de partition valide (elle sont indiquées comme récursives). Aussi n’est-il pas possible de rajouter correctement de seconde partition. Pour que notre méthode fonctionne, il faut modifier le format de l’image, qui ne sera plus ISO (donc pas gravable sur DVD) mais compatible avec une image de disque dur (USB-HDD). On ajoute encore une ligne d’option à lb config :

   --binary-images "usb-hdd"      \

Au final, mon script (script-livekey-3.sh) devient :

#! /bin/sh

rm -rf config

# Copier les archives pre-telechargees
mkdir -p config/chroot_local-includes/usr/local/src/
cp  linux-3.0.tar.bz2  config/chroot_local-includes/usr/local/src/
# ...

# Copier les packages supplementaires
mkdir -p config/chroot_local-packages/
cp   eclipse-cdt_6.0.2-1_i386.deb   config/chroot_local-packages/
# ...

lb clean

lb config  \
   --binary-images "usb-hdd"            \
   --distribution "squeeze"             \
   --archive-areas "main contrib"       \
   --bootappend-live "locales=fr_FR.UTF-8 keyboard-layouts=fr persistent" \
   --packages-lists "gnome-desktop"       \
   --packages "bison eclipse exuberant-ctags flex gcc gdb gedit gettext g++ qemu libncurses5-dev make minicom vim"    \
   --clean                              \
   --memtest "none"                     \
   --syslinux-timeout 20                \
   --interactive "false"                \

lb build

On notera au passage que l’option --interactive est maintenant à false, car une fois la configuration au point, il n’est plus besoin de faire de pause dans un shell chrooté.

 

Exécutons le script, à nouveau :

[livekey]# ./script-livekey-3.sh
P: Cleaning chroot
P: Considering defaults defined in /etc/live/build.conf
P: Updating config tree
P: Setting up cleanup function
P: Begin caching bootstrap stage...
P: Begin bootstrapping system...
W: skipping bootstrap
P: Begin caching bootstrap stage...
P: Begin unmounting filesystems...
P: Setting up cleanup function
P: Begin caching chroot stage...
P: Begin mounting /dev/pts...
[...]
P: Deconfiguring file /bin/hostname
P: Deconfiguring file /etc/resolv.conf
P: Deconfiguring file /etc/hosts
P: Begin unmounting filesystems...
[livekey]# ls -l 
total 1777476
drwxr-xr-x  6 root root       4096 2011-09-02 13:50 binary
-rw-r--r--  1 root root 1875902464 2011-09-02 13:53 binary.img
-rw-r--r--  1 root root       2162 2011-09-02 13:50 binary.list
-rw-r--r--  1 root root      33566 2011-09-02 13:49 binary.packages
drwxr-xr-x  6 root root       4096 2011-09-02 13:16 cache
[...]
[livekey]#

Cette fois, l’image est dans le fichier binary.img. Transférons-la sur la clé (attention à nouveau au nom du périphérique bloc !).

[livekey]# dd if=binary.img of=/dev/sdg bs=4M
447+1 enregistrements lus
447+1 enregistrements écrits
1875902464 octets (1,9 GB) copiés, 421,45 s, 4,5 MB/s

Puis ajoutons une partition à la suite. J’utilise pour cela fdisk, mais un résultat similaire serait obtenu avec parted ou gparted.

[livekey]# fdisk /dev/sdg
GNU Fdisk 1.2.4
Copyright (C) 1998 - 2006 Free Software Foundation, Inc.
This program is free software, covered by the GNU General Public License.
[...]
Using /dev/sdg
Command (m for help): p

Disk /dev/sdg: 7 GB, 7929169920 bytes
255 heads, 63 sectors/track, 964 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/sdg1   *           1         229     1839411   83  Linux
Warning: Partition 1 does not end on cylinder boundary.
Command (m for help): n
Partition type
   e   extended
   p   primary partition (1-4)
p
First cylinder  (default 0cyl): 230
Last cylinder or +size or +sizeMB or +sizeKB  (default 963cyl): (Entrée)
Command (m for help): p                                     

Disk /dev/sdg: 7 GB, 7929169920 bytes
255 heads, 63 sectors/track, 964 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/sdg1   *           1         229     1839411   83  Linux
Warning: Partition 1 does not end on cylinder boundary.
/dev/sdg2             230         964     5895855   83  Linux
Command (m for help): w
Information: Don't forget to update /etc/fstab, if necessary.
Writing all changes to /dev/sdg.
[livekey]#

Enfin formattons notre nouvelle partition. Le système de fichier ext2 est très bien adapté pour les clés USB ; il faut penser à nommer la partition formattée home-rw.

[livekey]# mkfs.ext2 /dev/sdg2 -L home-rw
mke2fs 1.41.14 (22-Dec-2010)
Étiquette de système de fichiers=home-rw
Type de système d'exploitation : Linux
Taille de bloc=4096 (log=2)
[...]
Le système de fichiers sera automatiquement vérifié tous les 30 montages ou
après 180 jours, selon la première éventualité. Utiliser tune2fs -c ou -i
pour écraser la valeur.
[livekey]#

Après redémarrage, on peut modifier certains paramètres de configuration du système (fond d’écran, etc.) et d’ajouter des fichiers dans notre répertoire personnel. Ces éléments seront persistants d’une exécution à l’autre.

Conclusion

Il est facile de disposer avec Debian Live Build d’un environnement de travail ajusté et persistant sans toucher aux disques durs des systèmes sur lesquels on l’exécute. Ceci permet bien sûr de disposer d’un poste de travail mobile et autonome, mais permet également de palier les contraintes d’administration lorsqu’on intervient dans une entreprise extérieure sans apporter de matériel (session de formation, réunion improvisée, démonstration chez un client, etc.)