|
CS471/571 - Operating Systems
|
Displaying ./code/Pseudo-terminals/pty.c
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/select.h>
#define K 1024
// man pty
/**
* Termios are documented in man 3 tcgetattr
*/
void noecho()
{
struct termios t;
tcgetattr(STDIN_FILENO,&t);
// Turn off:
// ICANNON - canonical mode (line buffered/processed input)
// ECHO - echoing characters as we type them
// ISIG - Send signals when special keys are typed (Ctrl-C, Ctrl-Z, etc)
// IEXTEN - Enable line editing
t.c_lflag &= ~(ICANON|ECHO|ISIG|IEXTEN);
// Set MIN minimum number of characters to block read on and TIME maximum
// amount of time to wait for input.
t.c_cc[VMIN] = 1;
t.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO,TCSANOW,&t);
}
void echo()
{
struct termios t;
tcgetattr(STDIN_FILENO, &t);
t.c_lflag |= (ICANON|ECHO|ISIG|IEXTEN);
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
int main(int argc, char *argv[])
{
struct termios tio;
struct winsize ws;
pid_t pid;
int pty, tty, r;
char *pts, buf[K];
// Add a signal handler to handle things like SIGCHLD, maybe SIGWINCH
// Get line attributes from our TTY:
if (tcgetattr(STDIN_FILENO, &tio) < 0) {
perror("tcgetattr");
exit(1);
}
// Get the window size from our TTY:
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
fprintf(stderr,"Getting winsize\n");
exit(1);
}
// Open master end of the PTY:
if ((pty = posix_openpt(O_RDWR | O_NOCTTY)) < 0) {
perror("openpt");
exit(1);
}
// The remaining can probably be done in the child process.
// Allocate a slave terminal to us:
if (grantpt(pty) < 0) {
perror("grantpt");
exit(1);
}
// Unlock and set permissions on slave terminal side:
if (unlockpt(pty) < 0) {
perror("unlockpt");
exit(1);
}
// Find out what the device name of the slave side pseudo terminal is:
if ((pts = ptsname(pty)) == NULL) {
perror("ptsname");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
// Open slave side PTY:
if ((tty = open(pts, O_RDWR)) < 0) {
perror("slave open");
exit(1);
}
dup2(tty, STDIN_FILENO);
dup2(tty, STDOUT_FILENO);
dup2(tty, STDERR_FILENO);
close(tty);
// Set line attributes (immediately):
if (tcsetattr(STDIN_FILENO, TCSANOW, &tio) < 0) {
perror("tcsetattr");
}
// Set the window size:
if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws) < 0) {
fprintf(stderr,"winsize\n");
}
execlp("arogue", "arogue", NULL);
perror("execlp");
exit(1);
}
// In the parent process:
fd_set rfds;
noecho();
// Add support for async I/O on keyboard and from the master pty:
while (1) {
FD_ZERO(&rfds);
FD_SET(STDIN_FILENO, &rfds);
FD_SET(pty, &rfds);
select(pty+1, &rfds, NULL, NULL, NULL);
// If true, then something is available on stdin:
if (FD_ISSET(STDIN_FILENO, &rfds)) {
r = read(STDIN_FILENO, buf, K);
if (r < 0) break;
write(pty, buf, r);
}
// If true, something is available on the pty to read:
if (FD_ISSET(pty, &rfds)) {
r = read(pty, buf, K);
if (r < 0) break;
write(STDOUT_FILENO, buf, r);
}
}
echo();
exit(0);
}
|