Écriture d'une fonction d'encapsulation (banalisée)
-
Système Ubuntu Intrepid Ibex -

1 - But de la fonction :

         Un professeur de mathématiques m'a demandé s'il était possible d'améliorer le dialogue avec certains programmes qui n'utilisent que le clavier et l'écran, programmes de type console donc. Ces programmes relativement éphémères dont la durée de vie est l'année scolaire en général, n'incitent pas au développement d'une interface élaborée, surtout graphique. Il faudrait donc quelque chose de facile à mettre en oeuvre.
        J'ai donc répondu que j'allais essayer de regarder comment faire et j'ai pensé à la technique d'encapsulation de programme comme le réalisent les commandes "ddd", "cgdb", "xcbx" en particulier qui encapsulent la commande  de debugguing "gdb". On a l'habitude d'utiliser aujourd'hui le vocable de "Front end" pour nommer ce genre de programme.

        J'ai alors essayé de m'appuyer sur les sources des commandes "ddd", "cgdb" et "xdbx" !.... Pas aisé je dois dire car les commentaires sont trés limités, surtout dans "cgdb", et je n'ai pas trouvé de documentation explicative qui s'y rapporte.

        Pour faire ce qui est demandé, il faut limiter la programmation au strict nécessaire. J'ai donc essayé d'écrire une fonction qui ferait le travail d'encapsulation et que j'ai appelée "encaps.c". L'utilisateur aurait alors à programmer le traitement des sorties et des entrées du programme encapsulé.

         Durant mon activité dans l'aéronautique, au tout début des années 80, j'ai déjà été confronté à ce besoin sur des stations Apollo, tournant sous système AEGIS, SYS V et BSD 4.2, pour tenter de connecter en mode online une table de dessin Kongsberg, une machine à commandes numériques, fonctionnant normalement en off-line. Je n'ai pas le souvenir d'avoir galéré comme aujourd'hui. Peut-être l'âge !... Il me semble que j'utilisais les IPC's (Inter Process Communications et "mmap") avec un pilote-caractère qu'Apollo m'avait aidé à installer. Je n'ai pas le souvenir des speudo-terminaux. Je vais tenter néanmoins de chercher dans mes vieilles archives une éventuelle note qui traînerait par là.

2 - Le programme "mon_programme" à encapsuler :

         Ce programme simple simule le fonctionnement-type d'un programme-console comme "gdb". En entrée, le programme affiche un prompt "mpgm=>" pour la saisie du nombre de lignes à afficher et en sortie le programme affiche le nombre de lignes demandées en les numérotant :

Prototype de programme-console...
mpgm=>2
ligne 0
ligne 1
mpgm=>5
ligne 0
ligne 1
ligne 2
ligne 3
ligne 4
mpgm=>quit

      Il s'agit donc d'obtenir le même résultat mais affiché par le programme encapsuleur ce qui permet d'envisager de traiter les flux d'entrée et de sortie du programme encapsulé.

2 - Le programme encapsuleur "ma_commande" :

      C'est le programme qui doit être lancé. Il se compose des données et des points d'entrée suivants pour remplir sa fonction :

A) - Les données :

"*prompt_maitre"   :   affiché par le programme encapsuleur pour demander à l'utilisateur d'entrer une commande au clavier.
"*quit_maitre"  :  commande entrée au clavier par l'utilisateur pour demander au programme encapsuleur de sortir.
"*prompt_esclave"  :  prompt habituel du programme encapsulé qui permet au programme encapsuleur de savoir s'il faut saisir une commande. Par exemple avec gdb lc prompt est "(gcb)".
"*pgm_encaps"   nom du programme à encapsuler.

B) - Les points d'entrée à écrire par l'utilisateur :

"encaps_init"   :   routine d'initialisation avant le fork()
"encaps_recu"  :  routine de traitement des lignes reçues
"encaps_emis"  :  routine de traitement des lignes à envoyer
"encaps_ctlC"  :  routine de traitement du signal CTRL/C éventuellement entrée par l'utilisateur sur son clavier
"main"   :   point d'entrée principal du programme encapsuleur.

3 - Console de la compilation :

$ make

  1. cc -g -c -o mon_programme.o mon_programme.c
  2. cc -o mon_programme mon_programme.o
  3. cc -o encaps.o -c encaps.c -g
  4. encaps.c: In function 'encaps':
    encaps.c:101: attention : assignment makes pointer from integer without a cast
  5. encaps.c:186: attention : le format n'est pas une chaîne littérale et pas d'argument de format
  6. cc -o ma_commande.o -c ma_commande.c -g
  7. cc -o ma_commande ma_commande.o encaps.o

4 - Console de l'exécution :

$ ma_commande

  1. chemin d'accès au pseudo-terminal maître : /dev/ptys0
  2. chemin d'accès au speudo-terminal esclave : /dev/ttys0
  3. les paramètres lancés par l'utilisateur :
  4. p[0] = 'ma_commande'
  5. les paramètres lancés avant le fork() :
  6. param[0] = 'ma_commande'
  7. param[1] = '(null)'
  8. exec fork
  9. pid du fils = 1706
  10. exec fork
  11. (reçu de mpgm) :////Prototype de programme-console...
  12. ////
  13. (reçu de mpgm) :////mpgm=>////
  14. ??:2
  15. (à émettre de mpgm) :////2////
  16. Erreur read() : peut pas recevoir de l'esclave

5 - Ce que je constate :

     Le programme affiche :

      Si on essaie de relancer "ma_commande", le fonctionnement du programme s'arrête parfois au niveau de la ligne 10.

      Un test de dernière minute (si cela peut éclairer) :

      Voilà où j'en suis. J'ai l'impression qu'il ne manque pas grand chose mais quoi ?

      Merci si vous pouvez m'aider à trouver l'erreur de logique à moins qu'il faille s'y prendre autrement mais peut-être pas comme dans les sources de "cgdb" e "xdbx"que j'analyse et que je trouve un peu brouillon.

      J'ai, pour ma part, quelques utilisations personnelles d'un tel outil.

      Dans l'espoir de trouver parmi vous au moins une personne intéressée par ce genre de service ou par la technique pour l'assurer, encore merci d'avance.

Jean-Pierre Rousset