Wie tun * nichts Pseudo-Terminals arbeiten? Was ist der Master / Slave-Kanal?
Frage
Ich mag einen einfachen, stumm, X-Terminal-Emulator in C auf einem Linux-System schreiben.
Zuerst dachte ich, ich würde eine Schale popen haben und seine Ausgabe an. Ich habe xterm und rxvt Code, und es sieht ein wenig komplizierter.
Zuerst muss ich mit openpty ein Pseudo-Terminal öffnen. So sehe ich den Mann an Seite und sehen, dass openpty füllt 2 Filedeskriptoren, den Master und den Slave. Sowohl xterm und rxvt Code ist chaotisch wegen der systemabhängigen Heit dieser Angebote Dateien.
Ich verstehe das termios Zeug: es ist nur ein Bündel von Informationen über den Escape-Code des Terminals. Was ich wirklich nicht bekommen, ist: was ich mit dem Master / Slave-Dateideskriptor zu tun bin nehme
Ein Beispiel-Programm, das ein Terminal zu öffnen, anmeldet, führt einen „ls“ auf der Schale wäre großartig.
(Englisch ist nicht meine Muttersprache, entschuldigen Sie meine eventuellen Fehler)
Edit: Hier ist der Beispielcode kam ich mit:
#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;
}
Lösung
Im Hinblick auf den Master / Slave-Teil Ihrer Frage, von der pty (4) Manpage (die aus dem openpty (3) man-Seite auf meinem System verwiesen wird):
Ein Pseudo-Terminal ist ein Paar von Zeichengeräte, ein Master-Gerät und ein Slave-Gerät. Die Slave-Vorrichtung liefert ein Verfahren eine Schnittstelle identisch mit dem in tty beschrieben (4). Während jedoch alle anderen Geräte die bilden die Schnittstelle beschrieben in TTY (4) ein Hardware-Gerät von eine Art hinter ihnen, der Sklave Gerät hat stattdessen einen anderen Prozess Manipulieren es durch den Master Hälfte des Pseudo-Terminal. Das heißt, alles auf dem Master-Gerät geschrieben wird an die Slave-Vorrichtung als Eingabe gegeben und alles, was auf dem Slave geschrieben Gerät wird als Eingang auf dem präsentierten Master-Gerät.
Man-Seiten sind deine Freunde.
Andere Tipps
Ich habe gerade versucht, die auf diesem Tutorial , sie arbeiten sehr fein für mich und ich denke, dass sie ein interessanter Ausgangspunkt für das Problem sind.
EDIT: Das Tutorial erklärt kurz die Pseudo-Terminals Funktion. Die Erklärung wird Schritt für Schritt durchgeführt und wird durch Beispiele gefolgt.
Das folgende Beispiel zeigt, wie eine neuen Pseudo-Terminal und Gabel der Prozess in zwei Teilen zu schaffen, eine Schrift auf dem Master Seite der Pseudo-Terminal, die andere Lesung aus dem Slave Seite der 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