---
created_at: '2017-06-10T15:26:16.000Z'
title: Using Pseudo-Terminals to Control Interactive Programs (2010)
url: http://rachid.koucha.free.fr/tech_corner/pty_pdip.html
author: robertelder
points: 49
story_text: 
comment_text: 
num_comments: 10
story_id: 
story_title: 
story_url: 
parent_id: 
created_at_i: 1497108376
_tags:
- story
- author_robertelder
- story_14527671
objectID: '14527671'

---
[Source](http://rachid.koucha.free.fr/tech_corner/pty_pdip.html "Permalink to Using pseudo-terminals to control interactive programs, pty, pdip")

# Using pseudo-terminals to control interactive programs, pty, pdip

Author: R. Koucha  
Last update: 16-Apr-2014  
  
  
[Back to home page][1]  
[Back to previous page][2]   

# Using pseudo-terminals (pty) to control interactive programs

| ----- |
|    
 | 

  
  
  

| ----- |
|    

 |    
 |  
|    
 |    
 |  
|    
 |  ![s][3] | 

  
[Foreword][4]  
[Introduction][5]  
[Redirection of the standard input and outputs of a process][6]  
[Problems while automating interactive programs][7]  
[Introduction to pseudo-terminals][8]  

[API of the pseudo-terminals][9]  

[Usage of the pseudo-terminals][10]  

[Inter-process communication through a pseudo-terminal][11]  
[Limitation of grantpt()][12]  
[Taking control over an interactive process][13]  

[Presentation of pdip][14]  

[Using pdip][15]  

[Resources][16]  
[About the author][17]  

## Foreword

  
A french version of this article has been published in ![glmf_logo][18] special issue 34.  

## Introduction

Although less and less popular, the interactive applications in command line mode interacting with an operator through a terminal on a serial port, are still widely used. Especially in the embedded world where the graphical resources are superfluous or too expensive. Among those applications, we can quote as examples:  

* bash: the default Linux shell
* bc: the calculator
* ftp: file transfer utility
* telnet: remote terminal
It is possible to use those tools in shell scripts in order to automate some tasks like tests or system maintenance and administration. For example, we could use telnet in a script to connect to a remote machine to trigger some operations. Unfortunately, this is not that simple because an interactive program needs a human to operate.  
  
This article focuses on a solution based on pseudo-terminals to automate interactive programs.  
  
  

## Redirection of the standard input and outputs of a process

Once a program is loaded into memory to be executed, it becomes a process attached to the current terminal. By default, the standard input (stdin) comes from the  keyboard and the standard outputs (stdout and stderr) are redirected to the screen (cf. [figure 1][19]).  

  

###### Figure 1: Standard input and outputs of a Linux process

![figure 1][20]

  
Linux provides the ability to redirect the standard input and outputs in order to get data from another source than the keyboard and display data to another destination than the screen. This feature is very powerful: a process reads its standard input and writes to its standard outputs without knowing any details about the devices. In other words, a program can run without any modifications to read from the keyboard or a file or the output of another process (pipe mechanism). It is the same for the outputs.  
Let's consider the following program called mylogin which gets a login name and a password:  
  

| ----- |
|  #include <stdio.h>   
  
int main(void)   
{   
char login_name[150];   
char password[150];   
  
// By default stdin, stdout and stderr are open  
  
fprintf(stdout, "Login : ");   
if (NULL == fgets(login_name, sizeof(login_name), stdin))   
{   
fprintf(stderr, "No login namen");   
return 1;   
}  
  
fprintf(stdout, "Password : ");   
if (NULL == fgets(password, sizeof(password), stdin))   
{   
fprintf(stderr, "No passwordn");  
return 1;  
}  
  
fprintf(stdout, "Result :n%s%sn", login_name, password);  
  
return 0;  
}  | 

  
Under a shell like bash, multiple solutions are available to make redirections. If we launch the program as it is, the input is the keyboard and the outputs are the screen.  
  

| ----- |
|  $ ./mylogin   
Login : bar   
Password : foo   
Result :  
bar  
foo   
$  | 

  
The preceding program can be launched as follow to redirect the output to the file output.txt.  

| ----- |
|  $ ./mylogin > output.txt   
bar   
foo   
$ cat output.txt   
Login : Password : Result :   
bar  
foo   
$  | 

  
We can see that without any modifications in the program mylogin, it has been possible to launch it to have the standard output redirected to the screen and then to the file output.txt.  
  

## Problems while automating interactive programs

A program as simple as mylogin can be automated. That is to say that the human operator can be replaced by a program like a shell script. Let's consider the file input.txt into which we have stored the answers expected by mylogin.  
  

| ----- |
|  $ cat input.txt   
bar   
foo   
$  | 

  
We can launch mylogin with input.txt as standard input:  
  

| ----- |
|  $ ./mylogin < input.txt   
Login : Password : Result :   
bar  
foo  
$   
 | 

  
So, we have replaced the human operator by a file containing the expected entries. Unfortunately, it is not possible to apply this method to all interactive programs. Some of them are very elaborated. A program reading a password typically flushes its standard input right after having displayed the password prompt to get rid of any characters entered between the login name and the password prompt (the character echoing is also deactivated during the password entry). We can illustrate this by making mylogin flush its standard input just before the password entry (call to fseek()).  
  

| ----- |
|  #include <stdio.h>   
  
int main(void)   
{   
char login_name[150];   
char password[150];   
  
// By default stdin, stdout and stderr are open  
  
fprintf(stdout, "Login : ");   
if (NULL == fgets(login_name, sizeof(login_name), stdin))   
{   
fprintf(stderr, "No login namen");   
return 1;   
}  
  
// Flush standard input  
fseek(stdin, 0, SEEK_END);   
  
fprintf(stdout, "Password : ");   
if (NULL == fgets(password, sizeof(password), stdin))   
{   
fprintf(stderr, "No passwordn");  
return 1;  
}  
  
fprintf(stdout, "Result :n%s%sn", login_name, password);  
  
return 0;  
} | 

  
When interacting with the operator, the program behaves the same (the keyboard is the standard input).  
  

| ----- |
|  $ ./mylogin  
login : bar  
Password : foo  
La saisie est :  
bar  
foo  | 

  
On the other side, when the standard input is a file, the error message "No password" is displayed. Actually, the second call to fread() gets an end of file which means that there are no more data on input.  
  

| ----- |
|  $ ./mylogin < input.txt   
No password  
Login : Password :  
$  | 

  
When the operator interacts, he waits for the display of the password prompt before entering the password. When the entry is the file input.txt, the entired file is entered at the beginning of the program. So, the first line is read with the first call to fread() and the second line is flushed by the call to fseek(). That's why the second fread() encounters an end of file. This is a typical case where the program is desynchronized with its standard input.  
From this example, we encountered one of the numerous problem we can have while attempting to automate an interactive program. These kind of programs also suppose that their standard input and output are terminals. So, they can trigger some terminal specific operations like "echo off", "canonical mode", "line mode"... If the input and output are not terminals but files for example, these operations will fail and trigger errors in the program.  
The pseudo-terminal concept is a solution to those problems.  
  

## Introduction to pseudo-terminals

A pseudo-terminal is a pair of character mode devices also called [pty][21]. One is master and the other is slave and they are connected with a bidirectional channel. Any data written on the slave side is forwarded to the output of the master side. Conversely, any data written on the master side is forwarded to the output of the slave side as depicted in [figure 2][22].  
  

###### Figure 2: Overview of a pseudo-terminal

![figure 2][23]

  
The slave side behaves exactly as a standard terminal as any process can open it to make it its standard input and outputs. So, all the operations like disabling the echo, setting the line mode or canonical mode are available.  
The master side is not a terminal. It is just a device which permits to send/receive data to/from the slave side.  
In the Unix world, there are multiple implementation of the pseudo-terminals. There are the BSD and the System V versions. The Linux world recommends the system V implementation also called "Unix 98 pty". This is the one we are going to study below.  
  

### API of the pseudo-terminals

The API is quite simple:  

* posix_openpt()  
This call creates the master side of the pty. It opens the device /dev/ptmx to get the file descriptor belonging to the master side.
* grantpt()  
The file descriptor got from posix_openpt() is passed to grantpt() to change the access rights on the slave side: the user identifier of the device is set to the user identifier of the calling process. The group is set to an unspecified value (e.g. "tty") and the access rights are set to crx--w----.
* unlockpt()  
After grantpt(), the file descriptor is passed to unlockpt() to unlock the slave side.
* ptsname()  
In order to be able to open the slave side, we need to get its file name through ptsname(). 
To this API, we can mention the standard API of the terminals: tcgetattr(), cfmakeraw()...  
  
The following program called mypty uses the API to create a pseudo-terminal.  

| ----- |
|  #define _XOPEN_SOURCE 600   
#include <stdlib.h>   
#include <stdio.h>   
#include <fcntl.h>   
#include <errno.h>   
  
int main(void)   
{   
int fdm;   
int rc;   
  
// Display /dev/pts   
system("ls -l /dev/pts");   
  
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;   
}   
  
// Display the changes in /dev/pts   
system("ls -l /dev/pts");   
  
printf("The slave side is named : %sn", ptsname(fdm));   
  
return 0;   
} // main  | 

  
The programs lists the content of the directory /dev/pts at the beginning and at the end to show the creation of the slave side. In the following example, this is the slave number 4 which is created:  
  

| ----- |
|  $ ./mypty   
total 0   
crw--w---- 1 koucha tty 136, 0 2007-09-25 13:56 0   
crw--w---- 1 koucha tty 136, 1 2007-09-25 13:32 1   
crw--w---- 1 koucha tty 136, 2 2007-09-25 12:58 2   
crw--w---- 1 koucha tty 136, 3 2007-09-25 07:32 3   
total 0   
crw--w---- 1 koucha tty 136, 0 2007-09-25 13:56 0   
crw--w---- 1 koucha tty 136, 1 2007-09-25 13:32 1   
crw--w---- 1 koucha tty 136, 2 2007-09-25 12:58 2   
crw--w---- 1 koucha tty 136, 3 2007-09-25 07:32 3   
crw--w---- 1 koucha tty 136, 4 2007-09-25 13:56 4   
The slave side is named : /dev/pts/4   
$  | 

  

## Usage of the pseudo-terminals

A pseudo-terminal is mainly used to make a process believe that it interacts with a terminal although it actually interacts with one or more processes.  
  

### Inter-process communication through a pseudo-terminal

To point out the pseudo-terminal functions, we can modify mypty into mypty2.  
  

| ----- |
|  #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);   
  
// 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] = ' 

[1]: http://rachid.koucha.free.fr/index.html
[2]: http://rachid.koucha.free.fr/technical_documents.html
[3]: http://rachid.koucha.free.fr/send_to_a_friend.gif
[4]: http://rachid.koucha.free.fr#Foreword
[5]: http://rachid.koucha.free.fr#Introduction
[6]: http://rachid.koucha.free.fr#Redirection_of_the_standard_input_and
[7]: http://rachid.koucha.free.fr#Problems_while_automating_interactive
[8]: http://rachid.koucha.free.fr#Introduction_to_pseudo-terminals
[9]: http://rachid.koucha.free.fr#API_of_the_pseudo-terminals
[10]: http://rachid.koucha.free.fr#Usage_of_the_pseudo-terminals
[11]: http://rachid.koucha.free.fr#Inter-process_communication_through_a
[12]: http://rachid.koucha.free.fr#Limitation_of_grantpt
[13]: http://rachid.koucha.free.fr#Taking_control_over_an_interactive
[14]: http://rachid.koucha.free.fr#Presentation_of_pdip
[15]: http://rachid.koucha.free.fr#Using_pdip
[16]: http://rachid.koucha.free.fr#Resources
[17]: http://rachid.koucha.free.fr#About_the_author
[18]: http://rachid.koucha.free.fr/glmf_logo.jpg
[19]: http://rachid.koucha.free.fr#figure_1
[20]: http://rachid.koucha.free.fr/pty_pdip_figure_1.jpg
[21]: http://rachid.koucha.free.fr#1_man_7_pty_
[22]: http://rachid.koucha.free.fr#Figure_2%3A_Overview_of_a_pseudo-terminal
[23]: http://rachid.koucha.free.fr/pty_pdip_figure_2.jpg

