Retrouvez cet article dans : Linux Magazine Hors série 34
Bien que de moins en moins utilisées, les applications interactives en mode ligne de commandes interagissant avec un opérateur via un terminal sur port série sont encore légions, notamment dans le monde Linux embarqué où les ressources graphiques sont superfl ues ou d’un coût trop élevé. Parmi ces applications,
on peut citer les plus connues :
- bash : le shell par défaut de Linux ;
- bc : la calculatrice ;
- ftp : utilitaire de transfert de fi chier ;
- telnet : terminal distant.
Il est possible de tirer bénéfi ce de ces utilitaires dans des scripts shell afi n d’automatiser certaines tâches comme les tests ou les opérations de maintenance et d’administration système. Par exemple, on pourrait lancer un script qui crée une session telnet sur ne machine distante afi n de déclencher certaines opérations. Mais, cela n’est pas aussi simple, car un processus interactif, nécessitant l’intervention d’un opérateur pour fonctionner, se prête a priori mal à une automatisation de son déclenchement.
Cet article se propose donc de présenter une solution à l’automatisation des programmes interactifs à travers la notion de " pseudoterminal ".
1. Redirection de l’entrée et des sorties standards d’un processus
Un programme Linux, lorsqu’il est chargé en mémoire pour être exécuté, devient un processus qui est attaché au terminal courant. Par défaut, l’entrée standard (stdin) rovient du clavier, tandis que les sorties normale et en erreur standards (stdout et stderr) sont redirigées sur l’écran (Cf. Figure 1).

Figure 1 : Entrée et sorties standards d’un processus Linux
Linux off re la notion de " redirection " des entrées et sorties de sorte à permettre à un processus de lire ses données d’entrée à partir d’une autre source que le clavier du terminal courant et d’affi cher ses données de sortie sur une autre destination que l’écran du terminal courant. La puissance de ce mécanisme réside dans le fait que les redirections sont complètement transparentes : le processus lit son entrée standard et affiche sur ses sorties standards sans connaître
la nature des périphériques qui se cachent derrière.
En d’autres termes, un programme peut être lancé sans modifi cation pour lire tantôt le clavier, tantôt le contenu d’un fi chier, tantôt la sortie d’un autre programme (via le mécanisme de pipe). Il en va de même pour ses sorties.
Considérons le programme simple suivant appelé mylogin, qui saisit un nom de login et un mot de passe :
#include <stdio.h>
int main(void)
{
char nom_de_login[150];
char mot_de_passe[150];
// Par défaut stdin, stdout et stderr sont ouverts
fprintf(stdout, "login : ");
if (NULL == fgets(nom_de_login, sizeof(nom_de_login), stdin))
{
fprintf(stderr, "Pas de nom de login\n");
return 1;
}
fprintf(stdout, "Mot de passe : ");
if (NULL == fgets(mot_de_passe, sizeof(mot_de_passe), stdin))
{
fprintf(stderr, "Pas de mot de passe\n");
return 1;
}
fprintf(stdout, "La saisie est :\n%s%s\n", nom_de_login, mot_de_passe);
return 0;
}
Sous un shell tel que bash, plusieurs solutions sont à disposition pour eff ectuer les opérations de redirection.
Si on lance le programme simplement, son entrée et ses sorties standards sont respectivement le clavier et l’écran du terminal courant :
$ ./mylogin login : toto Mot de passe : foo La saisie est : toto foo $
Le programme précédent peut être lancé de la manière suivante pour rediriger ce qui est saisi au clavier dans le fichier output.txt :
$ ./mylogin > output.txt toto foo $ cat output.txt login : Mot de passe : La saisie est : toto foo $
On voit que sans aucune modifi cation du programme mylogin, on a pu le lancer la première fois avec l’entrée standard sur le clavier et la sortie standard sur l’écran et la seconde fois avec l’entrée standard sur le clavier et la sortie standard sur le fi chier output.txt.
2. Problèmes d’automatisation d’un programme interactif
Un programme interactif aussi simple que mylogin peut être automatisé. Nous entendons, par automatisation, le remplacement d’un opérateur humain par un programme, tel qu’un script shell. Considérons, par exemple, le fichier input.txt dans lequel on a mis le nom de login et le mot de passe attendus par mylogin :
$ cat input.txt toto foo $
On peut lancer le programme mylogin en injectant le fichier input.txt sur son entrée standard :
$ ./mylogin < input.txt login : Mot de passe : La saisie est : toto foo $
On a donc remplacé l’opérateur humain par un fichier contenant les entrées attendues par mylogin. Mais, il n’est malheureusement pas possible de généraliser cette méthode à tout programme interactif. En effet, certains sont très élaborés. Typiquement, un programme de saisie de login et mot de passe effectue systématiquement un nettoyage de son entrée standard pour ne pas tenir compte des caractères saisis entre la demande du nom de login et la demande du mot de passe (l’écho des caractères est aussi désactivé pendant la saisie du mot de passe). Pour étayer ces dernières remarques, on peut simuler le nettoyage de l’entrée standard en modifiant mylogin.c de sorte à insérer un appel à fseek() juste avant la saisie du mot de passe :
#include <stdio.h>
int main(void)
{
char nom_de_login[150];
char mot_de_passe[150];
// Par défaut stdin, stdout et stderr sont ouverts
fprintf(stdout, "login : ");
if (NULL == fgets(nom_de_login, sizeof(nom_de_login), stdin))
{
fprintf(stderr, "Pas de nom de login\n");
return 1;
}
// Nettoyage de l’entrée standard
fseek(stdin, 0, SEEK_END);
fprintf(stdout, “Mot de passe : “);
if (NULL == fgets(mot_de_passe, sizeof(mot_de_passe), stdin))
{
fprintf(stderr, “Pas de mot de passe\n”);
return 1;
}
fprintf(stdout, “La saisie est :\n%s%s\n”, nom_de_login, mot_de_passe);
return 0;
}
Le programme continue à se comporter comme souhaité quand il interagit avec un opérateur (l’entrée standard est le clavier) :
$ ./mylogin login : toto Mot de passe : foo La saisie est : toto foo
Par contre, lorsque l’entrée standard est un fi chier, le message d’erreur " Pas de mot de passe " s’affiche pour indiquer qu’aucune donnée n’a été saisie pour le mot de passe. En fait, le deuxième appel à fread() rencontre GNU_LMHS_34_1.indd 59 03/12/2007 17:40:54 60 Hors-Série N° 34 _ Janvier / Février 2008 une fi n de fi chier qui indique qu’il n’y a plus de données en entrée :
$ ./mylogin < input.txtPas de mot de passelogin : Mot de passe : $
Quand les données du programme sont fournies par l’opérateur, ce dernier attend l’affi chage de la chaîne " Mot de passe " avant de saisir le mot de passe. En mode automatique, le fi chier input.txt est injecté d’une traite et, par conséquent, sa deuxième ligne se retrouve " nettoyée " par l’appel à fseek(). C’est un cas typique de désynchronisation de l’entrée standard avec le programme.
À travers cet exemple simple, est mis en avant un des nombreux problèmes que l’on peut rencontrer lors du lancement automatique des programmes interactifs. En eff et, en plus de la désynchronisation qu’il peut y avoir entre les données en entrée et le programme, ce dernier peut aussi eff ectuer des opérations de reconfi guration du terminal pour se mettre en mode ligne ou canonique ou tout simplement pour désactiver l’écho des caractères. Si l’entrée ou les sorties standards ne sont pas des terminaux, mais des fi chiers par exemple, alors ces opérations vont échouer et déclencher des erreurs dans le programme.
La notion de " pseudo-terminal " est une solution à ce problème comme nous allons le voir dans la suite.
3. Présentation des pseudoterminaux
Un pseudo-terminal (communément appelé pty [1]) est une paire de périphériques virtuels en mode caractère : l’un est esclave et l’autre est maître. Un canal bidirectionnel relie ces deux entités. Toute donnée écrite du côté maître se retrouve en sortie du côté esclave. Inversement, toute donnée écrite du côté esclave, se retrouve en sortie du côté maître comme indiqué en figure 2.

Figure 2 : Vue générale d’un pseudo-terminal
La partie esclave se comporte exactement comme un terminal classique dans le sens où tout processus peut l’ouvrir pour en faire son entrée et sa sortie standards.
Certains traitements tels que l’écho, le remplacement des carriage returns par des line feeds ou autre peuvent être réalisés sur les données entrant ou sortant sur le pty esclave.
La partie maître n’est, quant à elle, pas un terminal, mais permet de faire des opérations de lecture de données provenant de la partie esclave et d’écriture de données à destination de la partie esclave.
Dans le monde Unix en général, il existe plusieurs implémentations des pseudo-terminaux. Il y a la version BSD et la version System V. Le monde Linux a retenu la version System V sous l’appellation " Unix 98 pty ".
C’est cette dernière qui est recommandée désormais et qui fera donc l’objet de la suite de cet article.
3.1 API des pseudo-terminaux
La mise en oeuvre des pseudo-terminaux se fait à l’aide d’une API assez simple :
posix_openpt
Cette fonction permet de créer la partie maître d’un pseudo-terminal. Elle ouvre le périphérique /dev/ptmx pour obtenir le descripteur de fi chier associé à la partie maître du pseudo-terminal.
grantpt
Après l’appel à posix_openpt(), le descripteur de fichier est passé à grantpt() pour changer les droits d’accès sur la partie esclave du pseudo-terminal : l’identifi ant d’utilisateur du périphérique esclave est positionné avec l’identifiant d’utilisateur du processus appelant. Le groupe est positionné à une valeur non spécifiée (par exemple tty) et les droits d’accès sont positionnés à crw--w----.
unlockpt
Après l’appel à grantpt(), le descripteur de fichier est passé à unlockpt() pour déverrouiller le périphérique esclave.
ptsname
Après les opérations précédentes, la partie esclave du pseudo-terminal peut être ouverte à l’aide de l’appel système open(). Mais, avant cela, il faut obtenir le nom du périphérique esclave via l’appel à ptsname().
À ces API, il faut ajouter les opérations classiques sur les terminaux telles que tcgetattr(), cfmakeraw()...
Le petit programme suivant appelé mypty met en oeuvre l’API pour créer un pseudo-terminal :
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int main(void)
{
int fdm;
int rc;
// Affichage de /dev/pts
system("ls -l /dev/pts");
fdm = posix_openpt(O_RDWR);
if (fdm < 0)
{
fprintf(stderr, “Erreur %d sur posix_openpt()\n”, errno);
return 1;
}
rc = grantpt(fdm);
if (rc != 0)
{
fprintf(stderr, “Erreur %d sur grantpt()\n”, errno);
return 1;
}
rc = unlockpt(fdm);
if (rc != 0)
{
fprintf(stderr, “Erreur %d sur unlockpt()\n”, errno);
return 1;
}
// Affichage des changements dans /dev/pts
system("ls -l /dev/pts");
printf(„Le pseudo-terminal esclave a pour nom : %s\n“, ptsname(fdm));
return 0;
} // main
Le programme affi che le contenu du répertoire /dev/pts au début et à la fi n de son exécution pour montrer que la création d’un pseudo-terminal ajoute une nouvelle entrée dans le répertoire /dev/pts. Dans l’exemple d’exécution suivant, c’est le pseudo-terminal esclave numéro 4 qui est créé :
$ ./mypty total 0 crw--w---- 1 koucha tty 136, 0 2007-09-25 13:56 0 crw--w---- 1 koucha tty 136, 1 2007-09-25 13:32 1 crw--w---- 1 koucha tty 136, 2 2007-09-25 12:58 2 crw--w---- 1 koucha tty 136, 3 2007-09-25 07:32 3 total 0 crw--w---- 1 koucha tty 136, 0 2007-09-25 13:56 0 crw--w---- 1 koucha tty 136, 1 2007-09-25 13:32 1 crw--w---- 1 koucha tty 136, 2 2007-09-25 12:58 2 crw--w---- 1 koucha tty 136, 3 2007-09-25 07:32 3 crw--w---- 1 koucha tty 136, 4 2007-09-25 13:56 4 Le pseudo-terminal esclave a pour nom : /dev/pts/4 $
4. Application des pseudoterminaux
Les pseudo-terminaux sont essentiellement utilisés pour faire croire à un processus qu’il est en interface avec un terminal classique alors qu’il est en communication avec un ou plusieurs processus.
4.1 Communication interprocessus via un pseudo-terminal
Pour mettre en évidence les fonctionnalités d’un pseudo-terminal, on peut modifier le programme mypty en mypty2 comme suit :
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#define __USE_BSD
#include <termios.h>
int main(void)
{
int fdm, fds, rc;
char input[150];
fdm = posix_openpt(O_RDWR);
if (fdm < 0)
{
fprintf(stderr, “Erreur %d sur posix_openpt()\n”, errno);
return 1;
}
rc = grantpt(fdm);
if (rc != 0)
{
fprintf(stderr, “Erreur %d sur grantpt()\n”, errno);
return 1;
}
rc = unlockpt(fdm);
if (rc != 0)
{
fprintf(stderr, “Erreur %d sur unlockpt()\n”, errno);
return 1;
}
// Ouverture du PTY esclave
fds = open(ptsname(fdm), O_RDWR);
// Création d’un processus fils
if (fork())
{
// Code du processus pere
// Fermeture de la partie esclave du PTY
close(fds);
while (1)
{
// Saisie operateur (entree standard = terminal)
write(1, "Entree : ", sizeof("Entree : "));
rc = read(0, input, sizeof(input));
if (rc > 0)
{
// Envoie de la saisie aux processus fils via le PTY
write(fdm, input, rc);
// Lecture de la reponse du fils dans le PTY
rc = read(fdm, input, sizeof(input) - 1);
if (rc > 0)
{
// Ajout d’une fin de chaine en fin de buffer
input[rc] = ‘\0’;
fprintf(stderr, "%s", input);
}
else
{
break;
}
}
else
{
break;
}
} // End while
}
else
{
struct termios slave_orig_term_settings; // Saved terminal settings
struct termios new_term_settings; // Current terminal settings
// Code du processus fils
// Fermeture de la partie maitre du PTY
close(fdm);
// Sauvegarde des parametre par defaut du PTY esclave
rc = tcgetattr(fds, &slave_orig_term_settings);
// Positionnement du PTY esclave en mode RAW
new_term_settings = slave_orig_term_settings;
cfmakeraw (&new_term_settings);
tcsetattr (fds, TCSANOW, &new_term_settings);
// Le cote esclave du PTY devient l’entree et les sorties standards du fils
close(0); // Fermeture de l’entrée standard (terminal courant)
close(1); // Fermeture de la sortie standard (terminal courant)
close(2); // Fermeture de la sortie erreur standard (terminal courant)
dup(fds); // Le PTY devient l’entree standard (0)
dup(fds); // Le PTY devient la sortie standard (1)
dup(fds); // Le PTY devient la sortie erreur standard (2)
while (1)
{
rc = read(fds, input, sizeof(input) - 1);
if (rc > 0)
{
// Remplacement du retour a la ligne par une fin de chaine
input[rc - 1] = ‘\0’;
printf(“Le fils a recu : ‘%s’\n”, input);
}
else
{
break;
}
} // End while
}
return 0;
} // main
Le programme consiste en deux processus. Le premier (le père) lit une ligne de caractères saisie au clavier et l’envoie sur la partie maître du pty. Le second (le fils) a fait du pty esclave son entrée et ses sorties standards et renvoie, sur sa sortie standard, toute chaîne lue, préfi xée par " Le fi ls a reçu : ". Voici un exemple de lancement :
$ ./mypty2 Entree : azerty Le fils a recu : ‘azerty’ Entree : qwerty Le fils a recu : ‘qwerty’ Entree : pwd Le fils a recu : ‘pwd’
La figure 3 explique le fonctionnement de mypty2 lorsque l’opérateur saisit la chaîne de caractères qwerty :

Figure 3 : Fonctionnement de mypty2
Côté processus fils, on remarquera la confi guration du pty esclave via les appels à cfmakeraw() et tcsetattr() de sorte à passer en mode raw (brut en français) pour désactiver les opérations telles que l’écho.
On peut rendre plus générique la modifi cation faite dans mypty2 pour donner la possibilité d’exécuter n’importe quel programme derrière le pty. Dans mypty3, le processus père envoie tout ce qui vient de son entrée standard sur le pty maître et tout ce qui vient du pty maître sur sa sortie standard. Il se conduit simplement comme un relayeur de données. Le processus fils effectue les mêmes opérations que précédemment, mais se généralise pour exécuter un programme interactif quelconque passé en argument. Nous noterons l’appel à setsid() pour faire en sorte que le pty esclave soit le terminal de contrôle du programme exécuté.
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#define __USE_BSD
#include <termios.h>
#include <sys/select.h>
int main(int ac, char *av[])
{
int fdm, fds;
int rc;
char input[150];
// Contrôle des arguments
if (ac <= 1)
{
fprintf(stderr, "Usage: %s nom_de_programme\n", av[0]);
exit(1);
}
fdm =posix_openpt(O_RDWR); if (fdm < 0) { fprintf(stderr, "Erreur %d sur posix_openpt()\n", errno); return 1; } rc =grantpt(fdm); if (rc != 0) { fprintf(stderr, “Erreur %d sur grantpt()\n”, errno); return 1; } rc =unlockpt(fdm); if (rc != 0) { fprintf(stderr, “Erreur %d sur unlockpt()\n”, errno); return 1; } // Ouverture du PTY esclave fds = open(ptsname(fdm), O_RDWR); // Création d’un processus fils if (fork()) { fd_set fd_in; // Code du processus pere // Fermeture de la partie esclave du PTY close(fds); while (1) { // Attente de données de l’entrée standard et du PTY maître FD_ZERO(&fd_in); FD_SET(0, &fd_in); FD_SET(fdm, &fd_in); rc = select(fdm + 1, &fd_in, NULL, NULL, NULL); switch(rc) { case -1 : fprintf(stderr, "Erreur %d sur select()\n", errno); exit(1); default : { // S’il y a des donnees sur l’entree standard if (FD_ISSET(0, &fd_in)) { rc = read(0, input, sizeof(input)); if (rc > 0) { // Envoie des données sur le PTY maitre write(fdm, input, rc); } else { if (rc < 0) { fprintf(stderr, “Erreur %d sur read entree standard\n”, errno); exit(1); } } }
// S’il y a des donnees sur le PTY maitre
if (FD_ISSET(fdm, &fd_in))
{
rc = read(fdm, input, sizeof(input));
if (rc > 0)
{
// Envoie des données sur la sortie standard
write(1, input, rc);
}
else
{
if (rc < 0)
{
fprintf(stderr, “Erreur %d sur read PTY maitre\n”, errno);
exit(1);
}
}
}
}
} // End switch
} // End while
}
else
{
struct termios slave_orig_term_settings; // Saved terminal settings
struct termios new_term_settings; // Current terminal settings
// Code du processus fils
// Fermeture de la partie maitre du PTY
close(fdm);
// Sauvegarde des parametre par defaut du PTY esclave
rc = tcgetattr(fds, &slave_orig_term_settings);
// Positionnement du PTY esclave en mode RAW
new_term_settings = slave_orig_term_settings;
cfmakeraw (&new_term_settings);
tcsetattr (fds, TCSANOW, &new_term_settings);
// Le cote esclave du PTY devient l’entree et les sorties standards du fils
// Fermeture de l’entrée standard (terminal courant)
close(0);
// Fermeture de la sortie standard (terminal courant)
close(1);
// Fermeture de la sortie erreur standard (terminal courant)
close(2);
// Le PTY devient l’entree standard (0)
dup(fds);
// Le PTY devient la sortie standard (1)
dup(fds);
// Le PTY devient la sortie erreur standard (2)
dup(fds);
// Nouvelle session pour le process courant de sorte
// a permettre le controle de processus dans le PTY esclave
// (utile pour le shell)
setsid();
// Execution du programme
execvp(av[1], NULL);
}
return 0;
} // main
Ci-après, on lance mypty3 avec la calculatrice bc à travers le pty :
$ ./mypty3 Usage: ./mypty3 program_name $ $ ./mypty3 bc bc 1.06 Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty’. 3+6 9 quit Erreur 5 sur read PTY maitre $
Il est possible de lancer un shell ou tout autre programme interactif à la place de bc. Cette technique s’applique à nombre de programmes célèbres tels que xterm, telnet, ssh, rlogin, rsh... Par exemple, la figure 4 décrit l’architecture de telnet.

Figure 4 : Description d’une session telnet
Pour faire le parallèle avec les programmes d’exemples précédents, le démon telnetd est le processus père dont l’entrée standard n’est pas un simple terminal, mais un terminal déporté à travers un réseau où s’exécute le client telnet. Le processus fils est un shell bash. Tout ce qui provient du client telnet à travers le réseau est transmis par telnetd sur le pseudoterminal maître. Tout ce qui provient du shell bash à travers le pseudo-terminal est transmis au client telnet par le réseau.
4.2 Prise de contrôle d’un processus interactif
mypty3 peut être modifi é pour rendre le processus père plus intelligent de sorte à lui faire interpréter un scénario avec un ensemble de commandes qui lui permettent de se synchroniser avec le processus fils. En d’autres termes, on pourrait remplacer l’opérateur humain par un script de commandes. C’est tout simplement ce qui est fait par le programme pdip [3] que nous voyons dans la suite.
5. Présentation de PDIP
pdip est l’acronyme de " Programmed Dialogue with Interactive Programs ". En français, cela donne " Dialogue Programmé avec des Programmes Interactifs ". Ce nom provient des premières lignes du manuel du programme expect [4] dont pdip se veut être une version extrêmement simplifiée. C’est un utilitaire qui accepte un scénario en entrée pour dialoguer avec un programme interactif. pdip est disponible en source libre sur Sourceforge.
Comme expect, pdip accepte un langage de commandes pour envoyer et recevoir des chaînes de caractères à un programme. Mais, contrairement à expect, le langage de commandes n’est pas évolué au point d’accepter des structures de contrôle de haut niveau ou des branchements. Il ne permet pas non plus de dialoguer avec plusieurs programmes en même temps ou de rendre la main à l’opérateur en cours de session.
Comme indiqué en figure 5, pdip reçoit en paramètre le chemin d’accès du programme interactif à exécuter et, pour interagir avec, il accepte un langage de commandes simples sur son entrée standard ou sous forme d’un script passé en paramètre.

Figure 5 : Vue générale du fonctionnement de PDIP
La liste des commandes disponibles pouvant être interprétées par PDIP est :
#
Début de commentaire.
timeout x
Positionne à x secondes le temps maximum à attendre après chacune des commandes qui suivent (la valeur 0 annule le temporisateur. C’est la comportement par défaut).
recv "w1 w2..."
Attend une chaîne de caractères venant du programme se conformant au modèle w1 w2... Le modèle est une expression régulière conforme à regex [2].
send "w1 w2..."
Envoie la chaîne de caractères w1 w2... au programme.
La chaîne peut contenir les caractères de contrôle suivants :
\a Cloche
\b Retour arrière
\t Tabulation horizontale
\n Retour à la ligne
\v Tabulation verticale
\f Saut de page
\r Retour chariot
\" Guillemet
\\ Barre oblique inversée
sleep x
Arrête toute activité pendant x secondes.
exit
Fin de session.
6. Utilisation de PDIP
Utiliser pdip est d’une simplicité extrême comme on le voit ici avec le pilotage d’un client telnet qui se connecte à une machine appelée remote avec le nom de login foo et le mot de passe bar. Ensuite, la commande ls est exécutée avant de terminer la session :
$ pdip --cmd=’telnet remote’ recv "login" # Attente du prompt " login: " send "foo\n" # Envoi du nom de login ’foo’ recv "Password"# Attente du prompt " Password " send "bar\n" # Envoi du mot de passe ’bar’ recv "\$ " # Attente du prompt du shell " $ " send "ls\n" # Lancement de la commande ’ls’ recv "\$ " # Attente du prompt du shell " $ " send "exit\n" # Sortie du shell exit # Sortie de PDIP $
Le script est largement commenté. On précisera toutefois qu’étant donné que la commande recv reçoit une expression régulière en paramètre, il convient de ne pas oublier d’inhiber les caractères spéciaux tels que $ à l’aide du caractère \. D’où la commande recv "\$" pour attendre le prompt du shell. Voici un exemple de résultat de ce script pdip :
$ pdip --cmd=’telnet remote’ recv "login " Trying 192.0.1.12... Connected to remote. Escape character is ‘^]’. Linux 2.6.22-14-generic (remote) (pts/10) remote loginsend “foo\n” recv “Password” : foo Passwordsend “bar\n” recv “\$ “ : Last login: Tue Nov 6 20:06:51 CET 2007 on :0 Linux remote 2.6.22-14-generic #1 SMP Sun Oct 14 23:05:12 GMT 2007 i686 The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. foo@remote:~$ send "ls\n" recv "\$ " ls DIR2 DOCUMENTS PERSO TODO Applications PHOTOS VIDEOS foo@remote:~$ send "exit\n" exit
Conclusion
La notion de pseudo-terminal est très largement utilisée dans le monde Unix en général à travers les utilitaires les plus célèbres tels que telnet ou xterm. Cet article a présenté l’une de ses applications à travers pdip qui a pour but de piloter des programmes interactifs.
NOTES
- [1] man 7 pty
- [2] man 7 regex
LIENS
- [3] Site de PDIP : http://pdip.sourceforge.net
- [4] Site d’EXPECT : http://expect.nist.gov
Retrouvez cet article dans : Linux Magazine Hors série 34


