2009-01-24 18 views
23

Chcę napisać prosty, głupi emulator terminala X w C na systemie Linux.Jak działają * pseudo-terminale * nix? Co to jest kanał nadrzędny/podrzędny?

Najpierw pomyślałem, że będę musiała wysunąć powłokę i wyświetlić jej wynik. Sprawdziłem kod xterm i rxvt i wygląda to trochę bardziej skomplikowanie.

Najpierw muszę otworzyć pseudo-terminal z openpty. Spojrzałem więc na stronę man i widzę, że openpty wypełnia 2 deskryptory plików, master i slave. Zarówno kod xterm, jak i rxvt są niechlujne ze względu na zależne od systemu pliki specjalne.

Rozumiem termios stuff: to tylko garść informacji o kodzie ucieczki terminalu. Czego naprawdę nie rozumiem, to co mam zrobić z deskryptorem pliku master/slave?

Przykładowy program, który otwiera terminal, loguje się, wykonuje "ls" na powłoce byłby niesamowity.

(angielski nie jest mój język ojczysty, przepraszam mój ewentualny błąd)

Edit: Oto przykładowy kod wymyśliłem:

#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; 
}  
+0

Program wiersza poleceń o nazwie "ekran" wykorzystuje to, jak sądzę. Dzięki temu możesz mieć zalogowaną konsolę na hoście, a jeśli zostaniesz wyrzucony, możesz zalogować się ponownie i połączyć się ponownie z tą sesją i kontynuować. To jest esencja pty. Ma kanał, który współdziała z systemem hosta i "kanał zwrotny", który ty, na zewnątrz, mówisz mu, co ma robić (i widzisz wyniki). Ja też, nie mam żadnego rzeczywistego doświadczenia z jego realizacją; Czytałem o nich w "Linux Application Development". Pod X, myślę, że jest więcej opatrunków okiennych, ale podstawową zasadą powinno być – gbarry

Odpowiedz

19

W odniesieniu do master/części niewolnikami twojego pytania, ze strony podręcznika użytkownika pty(4) (która jest wymieniona na stronie podręcznika openpty (3) w moim systemie):

Pseudo terminal to para urządzeń znakowych , urządzenie nadrzędne i urządzenie podrzędne. Urządzenie podrzędne zapewnia procesowi interfejs identyczny z opisanym w tty (4). Jednakże, podczas gdy wszystkie inne urządzenia który zapewnia interfejs opisany w tty (4) posiadać urządzenie sprzętowej jakiś za nimi, urządzenie slave ma natomiast inny proces manipulowania go przez mistrza połowie lat pseudo terminal. Oznacza to, że wszystko zapisane na urządzeniu nadrzędnym jest przekazywane urządzeniu podrzędnemu jako dane wejściowe , a wszystko, co zapisano w urządzeniu podrzędnym , jest przedstawiane jako dane wejściowe w urządzeniu master z .

Strony man są Twoimi przyjaciółmi.

1

Po prostu próbowałem przykładów znalezionych na this tutorial, działają one bardzo dobrze dla mnie i myślę, że są one interesującym punktem wyjścia dla problemu.

EDYTOWANIE: Samouczek krótko wyjaśnić funkcję pseudo-terminali. Wyjaśnienie jest wykonywane krok po kroku i następuje po przykładach.

Poniższy przykład pokazuje, jak utworzyć nowy Pseudoterminal i widelca procesu w dwóch części, napis na głównego stronie Pseudoterminal, drugi odczyt z podrzędnego strony pseudo-terminala.

#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