@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
ReadKey
y ReadMode
.
Preste antención a los modos cbreak
y normal
.
¿Como cambia la conducta?
telnet
o una
ssh
.
Casiano Rodríguez León