@ARGV.
En las líneas 11-13 se crea un canal pipe que será usado
para que el padre obtenga el estatus de retorno del hijo (línea 70).
Si el exec de la línea 33 fracasa el hijo envía
el código de error. Si no fracasa, el exec produce el cierre
del pipe y el padre recibe el end-of-file.
wait como es habitual para obtener
el estatus de retorno?En la línea 18 el proceso hijo establece el manejador de ficheros del lado esclavo como la terminal de control y hace que el proceso hijo se convierta en el lider de sesión.
En la línea 21 mediante la llamada
$slave->clone_winsize_from(\*STDIN)
se obtiene el tamaño de la seudoterminal asociada con el
manejador de ficheros STDIN y se transfiere al pty.
Nótese que debe ser llamado a través del lado esclavo (líneas 21 y 40).
Después de hacer que STDIN, STDOUT y STDERR estén asociados
con el lado esclavo de la terminal en las líneas 24-29
se procede a ejecutar el comando. Las llaves alrededor del
exec
evitan Perl envíe un mensaje de warning.
El proceso padre dispone el manejador para la señal de cambio de tamaño
de la ventana WINCH (línea 77). Abre un fichero de
log en el que se volcará la sesión (línea 78).
Después el padre permanece en un bucle a la escucha
de los manejadores asociados con la entrada estandar (STDIN)
y con la seudoterminal ($pty). Cada entrada
es volcada mediante la subrutina redirect
en el fichero de log y en el manejador de salida complementario
del de la entrada que se acaba de producir.
Se vuelca en $pty si la entrada viene de $pty
y en STDOUT
si la entrada viene de STDIN. Para ello se usa el objeto
$rs
de la clase IO::Select. Si los dos manejadores de
entrada $pty y STDIN están listos (@ready >1)
se le da siempre prioridad a la entrada que viene de $pty (línea 86).
El eco de la entrada desde STDIN también se retrasa
si la terminal no esta lista para escritura (!$ws->can_write(TIMEOUT),
véase la línea 91).
Para ello se usa el otro objeto $ws de la clase IO::Select
construido en la línea 80.
lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n try7
1 #!/usr/bin/perl -w
2 use strict;
3 use IO::Pty;
4 use IO::Select;
5 use IO::Handle;
6 use constant TIMEOUT=>3600;
7
8 my $pty = new IO::Pty;
9 my $pid;
10
11 pipe(STAT_RDR, STAT_WTR)
12 or die "Cannot open pipe: $!";
13 STAT_WTR->autoflush(1);
14 $pid = fork();
15 die "Cannot fork" if not defined $pid;
16 unless ($pid) { # Child
17 close STAT_RDR;
18 $pty->make_slave_controlling_terminal();
19 my $slave = $pty->slave();
20 close $pty;
21 $slave->clone_winsize_from(\*STDIN);
22 $slave->set_raw();
23
24 STDIN->fdopen($slave, "<")
25 or die "Can't reopen STDIN for reading, $!\n";
26 STDOUT->fdopen($slave, ">")
27 or die "Can't reopen STDOUT for writing, $!\n";
28 STDERR->fdopen($slave, ">")
29 or die "Can't reopen STDERR for writing, $!\n";
30
31 close $slave;
32
33 { exec(@ARGV) }; # exec produce un close de los ficheros abiertos
34 print STAT_WTR $!+0; # Fuerza contexto numérico
35 die "Cannot exec(@ARGV): $!";
36 }
37 &parent();
38
39 sub winch {
40 $pty->slave->clone_winsize_from(\*STDIN);
41 kill WINCH => $pid if $pid; # Si soy el padre envio la señal
42 print "STDIN terminal size changed.\n";
43 $SIG{WINCH} = \&winch; # En ciertos S.O.
44 }
45
46 sub redirect {
47 my ($src,$dst) = @_;
48 my $buf = '';
49 my $read = sysread($src, $buf, 1);
50 if (defined $read && $read) {
51 syswrite($dst,$buf,$read);
52 syswrite(LOG,$buf,$read);
53 return 0;
54 }
55 else { # EOF
56 print STDERR "Nothing from $src";
57 print "$read\n" if defined($read);
58 return 1
59 }
60 }
61
62 sub parent {
63 $pty->autoflush(1);
64 close STAT_WTR;
65 $pty->close_slave();
66 $pty->set_raw();
67 # Esperamos por el comienzo de la ejecución del exec
68 # eof debido al exec o error
69 my $errno;
70 my $errstatus = sysread(STAT_RDR, $errno, 256);
71 die "Cannot sync with child: $!" if not defined $errstatus;
72 close STAT_RDR;
73 if ($errstatus) {
74 $! = $errno+0;
75 die "Cannot exec(@ARGV): $!";
76 }
77 $SIG{WINCH} = \&winch;
78 open(LOG,">log") || die;
79 STDOUT->autoflush(1);
80 my ($rs, $ws) = (IO::Select->new(), IO::Select->new());
81 $rs->add(\*STDIN, $pty);
82 $ws->add($pty);
83 { # infinite loop
84 my @ready = $rs->can_read(TIMEOUT);
85 if (@ready) {
86 @ready = reverse @ready if (@ready >1) and ($ready[0] != $pty);
87 if ($ready[0] == $pty) {
88 my $eof = redirect($pty, \*STDOUT);
89 last if $eof;
90 }
91 elsif ($ws->can_write(TIMEOUT)) { # Algo en STDIN
92 my $eof = redirect(\*STDIN, $pty);
93 last if $eof;
94 }
95 }
96 redo;
97 }
98 close(LOG);
99 }
xterm con argument bash:
try7 bash
stty size para los diferentes
tamaños. ¿Que ocurre?
ls para diversos
tamaños. ¿Que observa? ¿Se adapta la salida de ls
al tamaño de la ventana?
GetTerminalSize en Term::ReadKey
o bien repase la sección 7.1.3.
raw.
Observe que ocurre en el fichero de log. ¿Cómo cambia?
WINCH, el padre, el hijo o ambos?
$pty->make_slave_controlling_terminal()?
¿Que significa el mensaje que se produce?
xterm.
vi tutu (el editor). ¿Que ocurre?
try7 vi tutu pero antes de hacerlo
ponga la xterm en modo cbreak. ¿Como cambia la conducta?
try7 vi pero antes de hacerlo
ponga la xterm en modos cbreak y -echo. ¿Como cambia?
¿Funciona?
try7
para que las entradas desde teclado se hagan sin eco
en STDOUT y sin
esperas. Ejecute la nueva versión try8 con
el comando try8 vi tutu.
Puede usar Term::ReadKey.
Lea la documentación de Term::ReadKey relativa a las funciones
ReadKeyy ReadMode.
Preste antención a los modos cbreak y normal.
¿Como cambia la conducta?
telnet o una
ssh.
Casiano Rodríguez León
