![]() |
Linux
Application Development |
Michael K. Johnson Erik W. Troan |
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
#include "ptypair.h"
volatile int propagate_sigwinch = 0;
/* sigwinch_handler
* propagate window size changes from input file descriptor to
* master side of pty.
*/
void sigwinch_handler(int signal) {
propagate_sigwinch = 1;
}
/* ptytest tries to open a pty pair with a shell running
* underneath the slave pty.
*/
int main (void) {
int master;
int pid;
char *name;
fd_set ready;
int i;
#define BUFSIZE 1024
char buf[1024];
struct termios ot, t;
struct winsize ws;
int done = 0;
struct sigaction act;
if ((master = get_master_pty(&name)) < 0) {
perror("ptypair: could not open master pty");
exit(1);
}
/* set up SIGWINCH handler */
act.sa_handler = sigwinch_handler;
sigemptyset(&(act.sa_mask));
act.sa_flags = 0;
if (sigaction(SIGWINCH, &act, NULL) < 0) {
perror("ptypair: could not handle SIGWINCH ");
exit(1);
}
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
perror("ptypair: could not get window size");
exit(1);
}
if ((pid = fork()) < 0) {
perror("ptypair");
exit(1);
}
if (pid == 0) {
int slave; /* file descriptor for slave pty */
/* We are in the child process */
close(master);
if ((slave = get_slave_pty(name)) < 0) {
perror("ptypair: could not open slave pty");
exit(1);
}
free(name);
/* We need to make this process a session group leader, because
* it is on a new PTY, and things like job control simply will
* not work correctly unless there is a session group leader
* and process group leader (which a session group leader
* automatically is). This also disassociates us from our old
* controlling tty.
*/
if (setsid() < 0) {
perror("could not set session leader");
}
/* Tie us to our new controlling tty. */
if (ioctl(slave, TIOCSCTTY, NULL)) {
perror("could not set new controlling tty");
}
/* make slave pty be standard in, out, and error */
dup2(slave, STDIN_FILENO);
dup2(slave, STDOUT_FILENO);
dup2(slave, STDERR_FILENO);
/* at this point the slave pty should be standard input */
if (slave > 2) {
close(slave);
}
/* Try to restore window size; failure isn't critical */
if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0) {
perror("could not restore window size");
}
/* now start the shell */
execl("/bin/sh", "/bin/sh", 0);
/* should never be reached */
exit(1);
}
/* parent */
free(name);
/* Note that we only set termios settings for standard input;
* the master side of a pty is NOT a tty.
*/
tcgetattr(STDIN_FILENO, &ot);
t = ot;
t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
ECHOK | ECHOKE | ECHONL | ECHOPRT );
t.c_iflag |= IGNBRK;
t.c_cc[VMIN] = 1;
t.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &t);
/* This code comes nearly verbatim from robin.c
* If the child exits, reading master will return -1 and we exit.
*/
do {
FD_ZERO(&ready);
FD_SET(STDIN_FILENO, &ready);
FD_SET(master, &ready);
select(master+1, &ready, NULL, NULL, NULL);
if (propagate_sigwinch) {
/* signal handler has asked for SIGWINCH propagation */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
perror("ptypair: could not get window size");
}
if (ioctl(master, TIOCSWINSZ, &ws) < 0) {
perror("could not restore window size");
}
/* now do not do this again until next SIGWINCH */
propagate_sigwinch = 0;
/* select may have been interrupted by SIGWINCH,
* so try again. */
continue;
}
if (FD_ISSET(master, &ready)) {
i = read(master, buf, BUFSIZE);
if (i >= 1) {
write(STDOUT_FILENO, buf, i);
} else {
done = 1;
}
}
if (FD_ISSET(STDIN_FILENO, &ready)) {
i = read(STDIN_FILENO, buf, BUFSIZE);
if (i >= 1) {
write(master, buf, i);
} else {
done = 1;
}
}
} while (!done);
/* this really doesn't matter because each time a master pty is
* opened, the corresponding slave pty has its termios settings
* reset
*/
tcsetattr(STDIN_FILENO, TCSANOW, &ot);
exit(0);
}