/* Miscellaneous system dependent routines for splitsh */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <signal.h> #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_INET_H /*#define STTY_HACK*/ #endif /* * Initialize a pty, fork a command running under it, and then * return the master file descriptor */ extern int WU_lines, WL_lines, W_columns; /* From vt100.c */ #define UPPER 0 /* Upper window definition */ #define LOWER 1 /* Lower window definition */ int pty_open(argv, childpid, win) char *argv[]; int *childpid; int win; /* 0 for upper, 1 for lower */ { void dropctty(), pty_setwin(); int get_master_pty(), get_slave_pty(); char LINES[12], COLUMNS[12]; int returnfd, slave_fd; time_t now; /* Get the master pty file descriptor */ if ( (returnfd=get_master_pty()) < 0 ) return(-1); /* Fork and go! */ if ( ((*childpid)=fork()) < 0 ) return(-1); else if ( (*childpid) == 0 ) { dropctty(); /* Lose controlling tty */ if ( (slave_fd=get_slave_pty()) < 0 ) { perror("Can't open slave tty"); exit(128); } close(0); close(1); close(2); dup(slave_fd); dup(slave_fd); dup(slave_fd); close(slave_fd); close(returnfd); #ifdef TIOCGWINSZ /* We don't want to set the environment if possible */ pty_setwin(0, win); #else if ( win == UPPER ) sprintf(LINES, "LINES=%d", WU_lines); else sprintf(LINES, "LINES=%d", WL_lines); putenv(LINES); sprintf(COLUMNS, "COLUMNS=%d", W_columns); putenv(COLUMNS); #endif /* TIOCGWINSZ */ putenv("TERM=vt100"); /* Put the new TERM in the env. */ #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif #ifdef STTY_HACK system("stty sane echo echoe intr '^C' erase '^H'"); #else (void) tty_reset(0); #endif /* Set our uid to our real uid if necessary */ (void) setuid(getuid()); /* Run the requested program */ execvp(argv[0], argv); perror(""); exit(255); } return(returnfd); } /* * Pseudo-terminal routines for Unix System V Release 3.2 and BSD4.2-3 */ extern int errno; int master_fd; char tty_name[64]; char pty_name[64]; #ifdef IRIX /* IRIX System V for SGI machines */ extern char *_getpty(); int get_master_pty() { 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_slave_pty() { int slave_fd; char *slavename; slavename=tty_name; if (slavename == NULL) { close(master_fd); return(-1); } if ( (slave_fd=open(slavename, O_RDWR)) < 0 ) /* open the slave */ { close(master_fd); return(-1); } return(slave_fd); } #else /* ! IRIX */ #ifdef SOLARIS /* System V.4 pty routines from W. Richard Stevens */ #include <stropts.h> #define DEV_CLONE "/dev/ptmx" extern char *ptsname(); int get_master_pty() { char *ttyptr; if ( (master_fd=open(DEV_CLONE, O_RDWR)) < 0 ) return(-1); if ( grantpt(master_fd) < 0 ) /* grant access to slave */ { close(master_fd); #ifdef DEBUG perror("grantpt()"); #endif 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_slave_pty() { int slave_fd; char *slavename; slavename=tty_name; if ( (slave_fd=open(slavename, O_RDWR)) < 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 /* X_OK */ #define PTY_OWNER 0 /* the uid of the owner of pty's. usually bin or root */ jmp_buf next; void trynext() { longjmp(next, 2); } int get_master_pty() { int i, master_fd; char *ptr; struct stat statbuff; static char ptychar[]="pqrs"; /* X */ /* 'r' is also a valid letter. */ 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 ( access(pty_name, R_OK|W_OK) != 0 ) break; #ifdef OLDDEBUG fprintf(stderr, "statted.\n"); #endif for ( i=(-1); ; ) { /* Set a time limit for the open */ if ( setjmp(next) == -1 ) return(-1); else { if ( ++i >= 16 ) break; #ifdef OLDDEBUG perror(pty_name); #endif } signal(SIGALRM, trynext); alarm(2); pty_name[5]='p'; pty_name[9]=hexdigit[i]; if ( (master_fd=open(pty_name, O_RDWR)) >= 0 ) { 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 && stat(tty_name, &statbuff) >= 0 ) { if ( statbuff.st_uid == PTY_OWNER ) { #ifdef OLDDEBUG fprintf(stderr," Slave pty: %s\n", tty_name); #endif /* Reset the alarm */ alarm(0); 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_slave_pty() { int slave_fd; errno=0; /* Set a time limit for the open */ alarm(3); if ( (slave_fd=open(tty_name, O_RDWR)) < 0 ) { close(master_fd); return(-1); } /* reset the alarm */ alarm(0); return(slave_fd); } #endif /* if SOLARIS */ #endif /* if IRIX */ /* These are the binary data functions that I am using instead of bcopy() and bzero(), written by Richard A. O'Keefe. Thanks! */ void d_copy(src, dst, len) register char *src, *dst; register int len; { while (--len >= 0) *dst++ = *src++; } void d_zero(dst, len) register char *dst; register int len; { while (--len >= 0) *dst++ = 0; } /* Here are the Terminal manipulation routines... */ /* Code to disassociate from my tty. Yay! :) */ void dropctty() { int fd; #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 */ } #ifdef HAVE_TERMIO_H /* Get the modes of the controlling tty and save them. Saves ttymodes in tty_mode and returns -1 if ioctl fails. */ struct termio tty_mode; /* Save tty mode here */ static int tty_init=0; int tty_getmode(fd) int fd; { d_zero((char *)&tty_mode, sizeof(struct termio)); tty_init=1; /* Flag: we have initialized the tty_mode struct */ if ( ! isatty(fd) ) return(0); #ifdef OLDDEBUG fprintf(stderr, "Getting tty modes for tty_mode.\r\n"); #endif if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0) { #ifdef DEBUG perror("tty_getmode(): ioctl error"); #endif return(-1); } return(0); } /* Set a tty to a sane mode */ int tty_sane(fd) int fd; { struct termio temp_mode; if ( ! isatty(fd) ) return(0); #ifdef OLDDEBUG fprintf(stderr, "tty_init: %d\nfd: %d\n", tty_init, fd); #endif if ( ! tty_init ) { if (ioctl(fd, TCGETA, (char *) &tty_mode) < 0) return(-1); } /* temp_mode.c_lflag=(tty_mode.c_lflag|(ISIG|ICANON|ECHO|ECHOE|ECHOK)); temp_mode.c_iflag=(tty_mode.c_iflag|(BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)); temp_mode.c_oflag=(tty_mode.c_oflag|(OPOST|ONLCR)); temp_mode.c_cflag=(tty_mode.c_cflag|(CS7|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); */ if ( ioctl(fd, TCSETA, (char *)&temp_mode) < 0 ) { #ifdef DEBUG perror("Can't set tty modes"); #endif return(-1); } return(0); } /* Set a terminal in raw mode */ int tty_raw(fd) int fd; /* of tty device */ { struct termio temp_mode; if ( ! tty_init ) return(-1); 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); } /* 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); return(0); } #else /* no /usr/include/termio.h */ #ifdef NEED_COMPAT_H /* FreeBSD needs this */ #include <sys/ioctl_compat.h> #endif /* NEED_COMPAT_H */ /* 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|=ECHO; if (ioctl(fd, TIOCSETP, (char *) &temp_mode) < 0) return(-1); return(0); } /* 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_getmode(fd) int fd; { if ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCGETP, (char *) &tty_mode) < 0) return(-1); return(0); } /* * Put a terminal device into RAW mode with ECHO off. * Before doing so we first save the terminal's current mode, * assuming the caller will call the tty_reset() function * (also in this file) when it's done with raw mode. */ int tty_raw(fd) int fd; /* of terminal device */ { struct sgttyb temp_mode; if ( ! isatty(fd) ) return(0); temp_mode = tty_mode; 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); } /* * 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 ( ! isatty(fd) ) return(0); if (ioctl(fd, TIOCSETP, (char *) &tty_mode) < 0) return(-1); return(0); } #endif /* HAVE_TERMIO_H */ /* Set the pty window size to the size of the virtual window */ #ifdef TIOCSWINSZ static struct /* winsize */ { unsigned short ws_row; /* rows, in characters */ unsigned short ws_col; /* columns, in characters */ unsigned short ws_xpixel; /* horizontal size - not used */ unsigned short ws_ypixel; /* vertical size - not used */ } mywinz; void pty_setwin(fd, win) int fd; /* The pty file descriptor */ int win; /* 0 for upper, 1 for lower window */ { if ( win == UPPER ) mywinz.ws_row=WU_lines; else mywinz.ws_row=WL_lines; mywinz.ws_col=W_columns; mywinz.ws_xpixel=0; mywinz.ws_ypixel=0; (void) ioctl(fd, TIOCSWINSZ, &mywinz); } #else void pty_setwin(fd, win) int fd; int win; { /* Bogus routine */ } #endif /* TIOCSWINSZ */ /* * 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); }