posix serial communication
¿Cómo funcionan los pseudo-terminales*nix? ¿Cuál es el canal maestro/esclavo? (2)
Acabo de probar los ejemplos que se encuentran en este tutorial , funcionan muy bien para mí y creo que son un punto de partida interesante para el problema.
EDITAR: El tutorial explica brevemente la función de pseudo-terminales. La explicación se hace paso a paso y es seguida por ejemplos.
El siguiente ejemplo muestra cómo crear un nuevo pseudo-terminal y divide el proceso en dos partes, una escribiendo en el lado maestro del pseudo-terminal y la otra leyendo desde el lado esclavo del pseudo-terminal.
#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, "Error %d on posix_openpt()/n", errno);
return 1;
}
rc = grantpt(fdm);
if (rc != 0)
{
fprintf(stderr, "Error %d on grantpt()/n", errno);
return 1;
}
rc = unlockpt(fdm);
if (rc != 0)
{
fprintf(stderr, "Error %d on unlockpt()/n", errno);
return 1;
}
// Open the slave PTY
fds = open(ptsname(fdm), O_RDWR);
printf("Virtual interface configured/n");
printf("The master side is named : %s/n", ptsname(fdm));
// Creation of a child process
if (fork())
{
// Father
// Close the slave side of the PTY
close(fds);
while (1)
{
// Operator''s entry (standard input = terminal)
write(1, "Input : ", sizeof("Input : "));
rc = read(0, input, sizeof(input));
if (rc > 0)
{
// Send the input to the child process through the PTY
write(fdm, input, rc);
// Get the child''s answer through the PTY
rc = read(fdm, input, sizeof(input) - 1);
if (rc > 0)
{
// Make the answer NUL terminated to display it as a string
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
// Child
// Close the master side of the PTY
close(fdm);
// Save the default parameters of the slave side of the PTY
rc = tcgetattr(fds, &slave_orig_term_settings);
// Set raw mode on the slave side of the PTY
new_term_settings = slave_orig_term_settings;
cfmakeraw (&new_term_settings);
tcsetattr (fds, TCSANOW, &new_term_settings);
// The slave side of the PTY becomes the standard input and outputs of the child process
close(0); // Close standard input (current terminal)
close(1); // Close standard output (current terminal)
close(2); // Close standard error (current terminal)
dup(fds); // PTY becomes standard input (0)
dup(fds); // PTY becomes standard output (1)
dup(fds); // PTY becomes standard error (2)
while (1)
{
rc = read(fds, input, sizeof(input) - 1);
if (rc > 0)
{
// Replace the terminating /n by a NUL to display it as a string
input[rc - 1] = ''/0'';
printf("Child received : ''%s''/n", input);
}
else
{
break;
}
} // End while
}
return 0;
} // main
Quiero escribir un emulador de terminal X simple y tonto en C en un sistema Linux.
Al principio, pensé que tendría que abrir un caparazón y mostrar su salida. Comprobé el código xterm y rxvt, y parece un poco más complicado.
Primero, tengo que abrir un pseudo-terminal con openpty. Entonces miro la página man y veo que openpty llena 2 descriptores de archivo, el maestro y el esclavo. Tanto el código xterm como el rxvt son complicados debido a la dependencia del sistema de esos archivos especiales.
Entiendo las cosas de termios: es solo un montón de información sobre el código de escape de la terminal. Lo que realmente no entiendo es: ¿qué se supone que debo hacer con el descriptor de archivo maestro / esclavo?
Un programa de ejemplo que abra un terminal, inicie sesión y ejecute un "ls" en el shell sería increíble.
(El inglés no es mi lengua materna, excusa mi error final)
Editar: Aquí está el código de muestra que se me ocurrió:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pty.h>
#include <utmp.h>
#include <ctype.h>
void
safe_print (char* s)
{
while(*s) {
if(*s == ''/n'')
putchar("/n");
else if(iscntrl(*s))
printf("//e(%d)", *s);
else
putchar(*s);
s++;
}
}
int
main (int argc, char** argv)
{
char buf[BUFSIZ] = {0};
int master;
int ret = forkpty(&master, NULL, NULL, NULL);
if(ret == -1)
puts("no fork"), exit(0);
if(!ret) {
execl("/bin/sh", "sh", NULL);
exit(0);
}
sleep(1); /* let the shell run */
if(argc >= 2) {
write(master, argv[1], strlen(argv[1]));
write(master, "/n", 1);
} else {
write(master, "date/n", sizeof "date/n");
}
while(1) {
switch(ret = read(master, buf, BUFSIZ)) {
case -1:
puts("error!");
exit(1);
break;
case 0:
puts("nothing.."), sleep(1);
break;
default:
buf[ret] = ''/0'';
safe_print(buf);
}
}
close(master);
return 0;
}
Con respecto a la parte maestro / esclavo de su pregunta, desde la página de manual pty (4) (a la que se hace referencia desde la página de manual de openpty (3) en mi sistema):
Un pseudo terminal es un par de dispositivos de caracteres, un dispositivo maestro y un dispositivo esclavo. El dispositivo esclavo proporciona a un proceso una interfaz idéntica a la descrita en tty (4). Sin embargo, mientras que todos los demás dispositivos que proporcionan la interfaz descrita en tty (4) tienen un dispositivo de hardware de algún tipo detrás de ellos, el dispositivo esclavo tiene, en cambio, otro proceso que lo manipula a través de la mitad maestra del pseudo-terminal. Es decir, cualquier cosa escrita en el dispositivo maestro se entrega al dispositivo esclavo como entrada y cualquier cosa escrita en el dispositivo esclavo se presenta como entrada en el dispositivo maestro.
Las páginas man son tus amigos.