/* tty.c Shareware Copyright by Sam Lantinga 5/6/93 */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> extern void d_zero(), d_copy(); #ifdef HAVE_TERMIO_H #include <termio.h> #else #include <sys/ioctl.h> #endif /* HAVE_TERMIO_H */ #ifdef HAVE_BSDTTY_H #include <sys/bsdtty.h> #ifndef TIOCNOTTY #define TIOCNOTTY _IO('t', 113) /* HP-UX void tty definition */ #endif #endif /* HAVE_BSDTTY_H */ #ifdef NEED_COMPAT_H #include <sys/ioctl_compat.h> #endif /* NEED_COMPAT_H */ /* * Pseudo-terminal routines for Unix System V Release 3.2 and BSD4.2-3 * I actually have three sets of routines, for three different methods * of obtaining a pseudo-tty. The first is for IRIX System V UNIX, * which uses _getpty() to obtain a pty. The second is for Solaris * 2.x which uses a clone open with some funky streams stuff to get * a pseudo-tty. The last is the brute-force method that works on * every thing else I have come across, including Sun/OS 4.x, AIX, * HP-UX, BSD 4.2-3, ULTRIX, and AT&T System V.3 */ extern int errno; int master_fd; char tty_name[18]; char pty_name[12]; #ifdef IRIX /* IRIX System V for SGI machines */ extern char *_getpty(); int get_pty_master() { char *ttyptr; if ( (ttyptr=_getpty(&master_fd, O_RDWR, 0600, 0)) == 0 ) return(-1); else strcpy(tty_name, ttyptr); return(master_fd); } /* * Open the slave half of a pseudo-terminal. */ int get_pty_slave() { int slave_fd; char *slavename; slavename=tty_name; if (slavename == NULL) { close(master_fd); return(-1); } if ( (slave_fd=open(slavename, O_RDWR, 0666)) < 0 ) /* open the slave */ { close(master_fd); return(-1); } return(slave_fd); } #else /* not IRIX */ #ifdef SOLARIS /* Solaris 2.1 (UNIX System V r4) */ #include <stropts.h> #define DEV_CLONE "/dev/ptmx" extern char *ptsname(); int get_pty_master() { char *ttyptr; if ( (master_fd=open(DEV_CLONE, O_RDWR, 0666)) < 0 ) return(-1); if ( grantpt(master_fd) < 0 ) /* grant access to slave */ { close(master_fd); return(-1); } if ( unlockpt(master_fd) < 0 ) /* clear slave's lock flag */ { close(master_fd); return(-1); } if ( (ttyptr=ptsname(master_fd)) == NULL ) { close(master_fd); return(-1); } else strcpy(tty_name, ttyptr); return(master_fd); } /* * Open the slave half of a pseudo-terminal. */ int get_pty_slave() { int slave_fd; char *slavename; slavename=tty_name; if ( (slave_fd=open(slavename, O_RDWR, 0666)) < 0 ) /* open the slave */ { close(master_fd); return(-1); } if ( ioctl(slave_fd, I_PUSH, "ptem") < 0 ) { close(master_fd); close(slave_fd); return(-1); } if ( ioctl(slave_fd, I_PUSH, "ldterm") < 0 ) { close(master_fd); close(slave_fd); return(-1); } if ( ioctl(slave_fd, I_PUSH, "ttcompat") < 0 ) { close(master_fd); close(slave_fd); return(-1); } return(slave_fd); } #else /* BSD, Sun/OS, AIX, ULTRIX, HP-UX, AT&T SYSV */ #include <setjmp.h> #ifndef X_OK #define R_OK 4 /* Test for Read permission */ #define W_OK 2 /* Test for Write permission */ #define X_OK 1 /* Test for eXecute permission */ #endif #define PTY_OWNER 0 /* the uid of the owner of pty's. usually bin or root */ #define BIN_UID 2 /* Secondary pty owner */ jmp_buf next; void trynext() { longjmp(next, 2); } int get_pty_master() { int i, master_fd; char *ptr; struct stat statbuff; static char ptychar[]="pqrst"; /* X */ static char hexdigit[]="0123456789abcdef"; /* Y */ for (ptr=ptychar; *ptr != 0; ptr++) { strcpy(pty_name, "/dev/ptyXY"); pty_name[8]=(*ptr); /* X */ pty_name[9]='0'; /* Y */ if ( stat(pty_name, &statbuff) < 0 ) break; #ifdef OLDDEBUG fprintf(stderr, "statted.\n"); #endif i=(-1); /* Initialize i */ /* Set a time limit for the open */ if ( setjmp(next) == -1 ) return(-1); signal(SIGALRM, trynext); for ( ++i; hexdigit[i]; ++i) { pty_name[5]='p'; pty_name[9]=hexdigit[i]; alarm(2); /* Set an open timeout */ if ( (master_fd=open(pty_name, O_RDWR, 0666)) >= 0 ) { alarm(0); /* Reset the alarm */ pty_name[5]='t'; sprintf(tty_name, "%s", pty_name); #ifdef OLDDEBUG fprintf(stderr, "tty: %s\n", tty_name); #endif if ( access(tty_name, R_OK|W_OK) == 0 ) { signal(SIGALRM, SIG_DFL); return (master_fd); } else { pty_name[5]='p'; (void) close(master_fd); } } /* reset the alarm */ alarm(0); } } return(-1); } /* Open the slave half of a pseudo-terminal. */ int get_pty_slave() { int slave_fd; if ( (slave_fd=open(tty_name, O_RDWR, 0666)) < 0 ) { close(master_fd); return(-1); } return(slave_fd); } #endif /* is SOLARIS? */ #endif /* is IRIX? */ /* The end of the Pseudo-tty routines */ /* These are the terminal manipulation routines. :) Fun! */ #ifdef SIGWINCH /* Starting window structure */ /* I am using struct winsize here, but redefining it, because Each system seems to define it in a different place, or not at all. The actual structure never seems to change though. :) */ struct winstats { unsigned short ws_row; /* rows, in characters */ unsigned short ws_col; /* columns, in characters */ unsigned short ws_xpixel; /* horizontal size, pixels - not used */ unsigned short ws_ypixel; /* vertical size, pixels - not used */ } tty_win; #ifdef NEED_UPDATEWIN /* Not used */ /* The window size has changed, let the pty know. Used as a signal handler */ void updatewin() { #ifdef TIOCGWINSZ (void) ioctl(ttyfd, TIOCGWINSZ, &tty_win); (void) ioctl(masterfd, TIOCSWINSZ, &tty_win); #endif /* TIOCGWINSZ */ /* An interesting note... I had code here to send a SIGWINCH to the pty process, but it turns out the kernel does when the pty recieves the TIOCSWINSZ ioctl. */ } #endif /* NEED_UPDATEWIN */ #endif /* SIGWINCH */ /* Code to disassociate from my tty. Yay! :) */ void dropctty() { int fd; #ifdef _POSIX_SOURCE setsid(); /* The POSIX solution is simple. :) */ #else #ifdef TIOCNOTTY /* We want to get HP-UX, BSD, and Sun/OS here */ setpgrp(0, 0); #ifndef CIBAUD /* Sun/OS doesn't need to do TIOCNOTTY. */ if ( (fd=open("/dev/tty", O_RDWR)) > (-1) ) { if (ioctl(fd, TIOCNOTTY, 0) < 0) { perror("ioctl TIOCNOTTY error"); fprintf(stderr, "\r"); } close(fd); } #endif /* CIBAUD */ #else /* SYSV */ setpgrp(); #endif /* TIOCNOTTY */ #endif /* _POSIX_SOURCE */ } /* Just fork, and exit, leaving the child running as a daemon */ void daemonize() { switch (fork()) { case -1: perror("fork() failed"); exit(3); break; case 0: dropctty(); return; default: exit(0); } } #ifdef HAVE_TERMIO_H /* Get the modes of the contorlling tty and save them. Saves ttymodes in tty_mode and returns -1 if ioctl fails. */ struct termio tty_mode; /* Save tty mode here */ int tty_init=0; int tty_getmode(fd) int fd; { /* If not a tty, don't set tty_init, but return ok */ if ( !isatty(fd) ) return(0); d_zero((char *)&tty_mode, sizeof(struct termio)); if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0) return(-1); tty_init=1; /* Flag: we have initialized the tty_mode struct */ #if defined(SIGWINCH) && defined(TIOCGWINSZ) d_zero((char *)&tty_win, sizeof(struct winstats)); (void) ioctl(fd, TIOCGWINSZ, &tty_win); #endif /* SIGWINCH */ return(0); } /* Restore terminal's mode to whatever it was on the most recent call to the tty_getmode() function. */ int tty_reset(fd) int fd; { if ( ! tty_init ) return(-1); if ( ! isatty(fd) ) return(0); if (ioctl(fd, TCSETA, (char *) &tty_mode) < 0) return(-1); #if defined(SIGWINCH) && defined(TIOCSWINSZ) (void) ioctl(fd, TIOCSWINSZ, &tty_win); #endif /* SIGWINCH */ return(0); } /* Set a tty to a sane mode */ int tty_sane(fd) int fd; { #ifdef TIOCGWINSZ struct winstats temp_win; #endif struct termio temp_mode; if ( ! isatty(fd) ) return(0); #ifdef DEBUG fprintf(stderr, "tty_init: %d\r\n", tty_init); #endif temp_mode.c_lflag=(ISIG|ICANON|ECHO|ECHOE); temp_mode.c_iflag=(BRKINT|IGNPAR|ISTRIP|ICRNL|IXON); temp_mode.c_oflag=(OPOST|ONLCR); temp_mode.c_cflag=(CS8|PARENB|CREAD); temp_mode.c_cc[VERASE]=('H'^64); temp_mode.c_cc[VKILL]=('U'^64); temp_mode.c_cc[VQUIT]=('\\'^64); temp_mode.c_cc[VINTR]=('C'^64); temp_mode.c_cc[VEOF]=('D'^64); temp_mode.c_cc[VMIN]=1; temp_mode.c_cc[VTIME]=0; if (ioctl(fd, TCSETA, (char *) &temp_mode) < 0) return(-1); #if defined(SIGWINCH) && defined(TIOCSWINSZ) d_zero((char *)&temp_win, sizeof(struct winstats)); temp_win.ws_row=24; temp_win.ws_col=80; (void) ioctl(fd, TIOCSWINSZ, &temp_win); #endif return(0); } /* Set a terminal in raw mode */ int tty_raw(fd) int fd; /* file descriptor of tty device */ { struct termio temp_mode; if ( ! isatty(fd) ) return(0); if ( ioctl(fd, TCGETA, (char *)&temp_mode) < 0 ) return(-1); temp_mode.c_iflag=(IGNBRK | ISTRIP); /* turn off all input control */ temp_mode.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET); /* disable output post-processing */ temp_mode.c_lflag = 0; temp_mode.c_cc[VMIN]=1; /* 1 or more chars satisfy read */ temp_mode.c_cc[VTIME]=0; /* 10'ths of seconds between chars */ if (ioctl(fd, TCSETA, (char *) &temp_mode) < 0) return(-1); return(0); } /* Function to set a tty echo or no echo */ int tty_echo(fd, echo) int fd; int echo; { struct termio temp_mode; if ( ! isatty(fd) ) return(0); if ( ioctl(fd, TCGETA, &temp_mode) < 0 ) return(-1); if ( echo ) temp_mode.c_lflag|=ECHO; else temp_mode.c_lflag&=(~ECHO); if ( ioctl(fd, TCSETA, &temp_mode) < 0 ) return(-1); return(0); } #else /* no /usr/include/termio.h */ /* Get the modes of the controlling tty and save them. Saves ttymodes in tty_mode and returns 1 if ioctl fails. */ static struct sgttyb tty_mode; /* save tty mode here */ int tty_init=0; int tty_getmode(fd) int fd; { if ( ! isatty(fd) ) return(0); tty_init=1; /* Flag: we have initialized the tty_mode struct */ if (ioctl(fd, TIOCGETP, (char *) &tty_mode) < 0) return(-1); #ifdef SIGWINCH if ( ioctl(fd, TIOCGWINSZ, &tty_win) < 0 ) perror("ioctl TIOCGWINSZ error"); #endif /* SIGWINCH */ return(0); } /* * Restore a terminal's mode to whatever it was on the most * recent call to the tty_getmode() function above. */ int tty_reset(fd) int fd; /* of terminal device */ { if ( ! tty_init ) /* Have we been initialized? */ return(-1); if ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCSETP, (char *) &tty_mode) < 0) return(-1); #ifdef SIGWINCH (void) ioctl(fd, TIOCSWINSZ, &tty_win); #endif /* SIGWINCH */ return(0); } /* Set a tty to a sane mode */ int tty_sane(fd) int fd; { struct sgttyb temp_mode; if ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCGETP, (char *) &temp_mode) < 0) return(-1); temp_mode.sg_flags &= ~RAW; /* turn RAW mode off */ temp_mode.sg_flags |= ECHO; /* turn ECHO on */ if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0) return(-1); return(0); } /* * Put a terminal device into RAW mode with ECHO off. */ int tty_raw(fd) int fd; /* of terminal device */ { struct sgttyb temp_mode; if ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCGETP, (char *) &temp_mode) < 0) return(-1); temp_mode.sg_flags |= RAW; /* turn RAW mode on */ temp_mode.sg_flags &= ~ECHO; /* turn ECHO off */ if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0) return(-1); return(0); } /* Set a terminal echo or no echo, as requested. */ int tty_echo(fd, echo) int fd; int echo; { struct sgttyb temp_mode; if ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCGETP, (char *) &temp_mode) < 0) return(-1); if ( echo ) temp_mode.sg_flags |= ECHO; /* turn ECHO on */ else temp_mode.sg_flags &= ~ECHO; /* turn ECHO off */ if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0) return(-1); return(0); } #endif /* HAVE_TERMIO_H */ /* * Write "n" bytes to a descriptor. * Use in place of write() when fd is a stream socket. */ int writen(fd, ptr, nbytes) register int fd; register char *ptr; register int nbytes; { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = write(fd, ptr, nleft); if (nwritten <= 0) return(nwritten); /* error */ nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); } /* Replacement for bzero(), which isn't always available. */ void d_zero(data, size) char *data; int size; { int i; for ( i=0; (i < size); ++i, ++data ) *data='\0'; } /* Replacement for bcopy(), which isn't always available. */ void d_copy(frombuf, tobuf, size) char *frombuf; char *tobuf; int size; { int i; for ( i=0; (i < size); ++i, ++frombuf, ++tobuf ) *tobuf=(*frombuf); }