How do *nix pseudo-terminals work ? What's the master/slave channel?

With respect to the master/slave part of your question, from the pty(4) man page (which is referenced from the openpty(3) man page on my system):

A pseudo terminal is a pair of character devices, a master device and a slave device. The slave device provides to a process an interface identical to that described in tty(4). However, whereas all other devices which provide the interface described in tty(4) have a hardware device of some sort behind them, the slave device has, instead, another process manipulating it through the master half of the pseudo terminal. That is, anything written on the master device is given to the slave device as input and anything written on the slave device is presented as input on the master device.

Man pages are your friends.


I just tried the examples found on this tutorial, they work very fine for me and I think they are an interesting starting point for the problem.

EDIT: The tutorial explain briefly the pseudo-terminals function. The explanation is done step by step and is followed by examples.

The following example show how to create a new pseudo-terminal, and fork the process in two parts, one writing on the master side of the pseudo-terminal, the other reading from the slave side of the 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

Tags:

Linux

C

Tty