2 * UNIX Tty and Process interface
4 * (C) 1992 Joseph H. Allen
6 * This file is part of JOE (Joe's Own Editor)
11 __RCSID("$MirOS: contrib/code/jupp/tty.c,v 1.30 2017/12/02 02:07:33 tg Exp $");
13 #ifdef HAVE_SYS_STAT_H
16 #ifdef HAVE_SYS_IOCTL_H
17 #include <sys/ioctl.h>
22 #ifdef HAVE_SYS_WAIT_H
25 #ifdef HAVE_SYS_PARAM_H
26 #include <sys/param.h>
40 /* We use the defines in sys/ioctl to determine what type
41 * tty interface the system uses and what type of system
44 #ifdef HAVE_POSIX_TERMIOS
46 # ifdef HAVE_SYS_TERMIOS_H
47 # include <sys/termios.h>
50 # ifdef HAVE_SYSV_TERMIO
52 # include <sys/termio.h>
69 /* Straight from the GNU autoconf texinfo documentation */
70 #ifdef TIME_WITH_SYS_TIME
71 # include <sys/time.h>
74 # ifdef HAVE_SYS_TIME_H
75 # include <sys/time.h>
81 /* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think
85 #include <sys/stream.h>
103 /* JOE include files */
112 /** Aliased defines **/
114 /* O_NDELAY, O_NONBLOCK, and FNDELAY are all synonyms for placing a descriptor
115 * in non-blocking mode; we make whichever one we have look like O_NDELAY
119 #define O_NDELAY O_NONBLOCK
122 #define O_NDELAY FNDELAY
126 /* Some systems define this, some don't */
128 #define sigmask(x) (1<<((x)-1))
131 /* Some BSDs don't have TILDE */
136 /* Global configuration variables */
138 int noxon = 0; /* Set if ^S/^Q processing should be disabled */
139 int Baud = 0; /* Baud rate from joerc, cmd line or environment */
144 FILE *termout = NULL;
146 /* Original state of tty */
148 #ifdef HAVE_POSIX_TERMIOS
149 struct termios oldterm;
150 #else /* HAVE_POSIX_TERMIOS */
151 #ifdef HAVE_SYSV_TERMIO
152 static struct termio oldterm;
153 #else /* HAVE_SYSV_TERMIO */
154 static struct sgttyb oarg;
155 static struct tchars otarg;
156 static struct ltchars oltarg;
157 #endif /* HAVE_SYSV_TERMIO */
158 #endif /* HAVE_POSIX_TERMIOS */
160 /* Output buffer, index and size */
162 unsigned char *obuf = NULL;
168 unsigned baud; /* Bits per second */
169 unsigned long upc; /* Microseconds per character */
171 /* TTY Speed code to baud-rate conversion table (this is dumb- is it really
172 * too much to ask for them to just use an integer for the baud-rate?)
175 static int speeds[] = {
176 B50, 50, B75, 75, B110, 110, B134, 134, B150, 150, B200, 200,
177 B300, 300, B600, 600,
178 B1200, 1200, B1800, 1800, B2400, 2400, B4800, 4800, B9600, 9600
195 int have = 0; /* Set if we have pending input */
196 static unsigned char havec; /* Character read in during pending input check */
197 int leave = 0; /* When set, typeahead checking is disabled */
199 /* TTY mode flag. 1 for open, 0 for closed */
200 static int ttymode = 0;
202 /* Signal state flag. 1 for joe, 0 for normal */
203 static int ttysig = 0;
206 /* Stuff for shell windows */
208 static pid_t kbdpid; /* PID of kbd client */
209 static int ackkbd = -1; /* Editor acks keyboard client to this */
211 static int mpxfd; /* Editor reads packets from this fd */
212 static int mpxsfd; /* Clients send packets to this fd */
215 static int tty_accept = NO_MORE_DATA; /* =-1 if we have last packet */
221 unsigned char data[1024];
227 /* Set signals for JOE */
233 joe_set_signal(SIGHUP, ttsig);
234 joe_set_signal(SIGTERM, ttsig);
235 joe_set_signal(SIGINT, SIG_IGN);
236 joe_set_signal(SIGPIPE, SIG_IGN);
239 /* Restore signals for exiting */
240 void signrm(int inchild)
246 joe_set_signal(SIGHUP, SIG_DFL);
247 joe_set_signal(SIGTERM, SIG_DFL);
248 joe_set_signal(SIGINT, SIG_DFL);
249 joe_set_signal(SIGPIPE, SIG_DFL);
252 /* Open terminal and set signals */
260 /* Close terminal and restore signals */
268 static volatile sig_atomic_t winched = 0;
270 /* Window size interrupt handler */
271 static RETSIGTYPE winchd(int unused)
274 REINSTALL_SIGHANDLER(SIGWINCH, winchd);
280 static volatile sig_atomic_t ticked = 0;
282 static RETSIGTYPE dotick(int unused)
295 joe_set_signal(SIGALRM, dotick);
301 static void baud_reset(int);
307 #ifdef HAVE_POSIX_TERMIOS
308 struct termios newterm;
310 #ifdef HAVE_SYSV_TERMIO
311 struct termio newterm;
315 struct ltchars ltarg;
320 if (idleout ? (!(termin = stdin) || !(termout = stdout)) : (!(termin = fopen("/dev/tty", "r")) || !(termout = fopen("/dev/tty", "w")))) {
321 fprintf(stderr, "Couldn\'t open /dev/tty\n");
325 joe_set_signal(SIGWINCH, winchd);
335 #ifdef HAVE_POSIX_TERMIOS
336 tcgetattr(fileno(termin), &oldterm);
340 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
342 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
344 newterm.c_cc[VMIN] = 1;
345 newterm.c_cc[VTIME] = 0;
346 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
347 bbaud = cfgetospeed(&newterm);
349 #ifdef HAVE_SYSV_TERMIO
350 ioctl(fileno(termin), TCGETA, &oldterm);
354 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
356 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
358 newterm.c_cc[VMIN] = 1;
359 newterm.c_cc[VTIME] = 0;
360 ioctl(fileno(termin), TCSETAW, &newterm);
361 bbaud = (newterm.c_cflag & CBAUD);
363 ioctl(fileno(termin), TIOCGETP, &arg);
364 ioctl(fileno(termin), TIOCGETC, &targ);
365 ioctl(fileno(termin), TIOCGLTC, <arg);
369 arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK);
384 ioctl(fileno(termin), TIOCSETN, &arg);
385 ioctl(fileno(termin), TIOCSETC, &targ);
386 ioctl(fileno(termin), TIOCSLTC, <arg);
387 bbaud = arg.sg_ospeed;
394 baud_reset(int bbaud)
400 while (x < NELEM(speeds))
401 if (bbaud == speeds[x]) {
402 baud = speeds[x + 1];
410 upc = DIVIDEND / baud;
413 if ((TIMES * upc) == 0)
416 obufsiz = 1000000 / (TIMES * upc);
422 obuf = (unsigned char *) joe_malloc(obufsiz);
441 #ifdef HAVE_POSIX_TERMIOS
442 tcsetattr(fileno(termin), TCSADRAIN, &oldterm);
444 #ifdef HAVE_SYSV_TERMIO
445 ioctl(fileno(termin), TCSETAW, &oldterm);
447 ioctl(fileno(termin), TIOCSETN, &oarg);
448 ioctl(fileno(termin), TIOCSETC, &otarg);
449 ioctl(fileno(termin), TIOCSLTC, &oltarg);
456 /* Timer interrupt handler */
458 static volatile sig_atomic_t yep;
459 static RETSIGTYPE dosig(int unused)
464 /* FLush output and check for typeahead */
466 #ifdef HAVE_SETITIMER
468 static void maskit(void)
473 sigaddset(&set, SIGALRM);
474 sigprocmask(SIG_SETMASK, &set, NULL);
477 static void unmaskit(void)
482 sigprocmask(SIG_SETMASK, &set, NULL);
485 static void pauseit(void)
494 static void maskit(void)
496 sigsetmask(sigmask(SIGALRM));
499 static void unmaskit(void)
504 static void pauseit(void)
516 unsigned long usec = obufp * upc; /* No. usecs this write should take */
518 #ifdef HAVE_SETITIMER
519 if (usec >= 50000 && baud < 9600) {
520 struct itimerval a, b;
522 a.it_value.tv_sec = usec / 1000000;
523 a.it_value.tv_usec = usec % 1000000;
524 a.it_interval.tv_usec = 0;
525 a.it_interval.tv_sec = 0;
527 joe_set_signal(SIGALRM, dosig);
530 setitimer(ITIMER_REAL, &a, &b);
531 joe_write(fileno(termout), obuf, obufp);
536 joe_write(fileno(termout), obuf, obufp);
540 joe_write(fileno(termout), obuf, obufp);
543 if (baud < 9600 && usec / 1000)
553 /* Ack previous packet */
554 if (ackkbd != -1 && tty_accept != NO_MORE_DATA && !have) {
557 if (pack.who && pack.who->func)
558 joe_write(pack.who->ackfd, &c, 1);
560 joe_write(ackkbd, &c, 1);
561 tty_accept = NO_MORE_DATA;
565 /* Check for typeahead or next packet */
567 if (!have && !leave) {
570 fcntl(mpxfd, F_SETFL, O_NDELAY);
571 if (read(mpxfd, &pack, sizeof(struct packet) - 1024) > 0) {
572 fcntl(mpxfd, F_SETFL, 0);
573 joe_read(mpxfd, pack.data, pack.size);
575 tty_accept = pack.ch;
577 fcntl(mpxfd, F_SETFL, 0);
581 /* Set terminal input to non-blocking */
582 fcntl(fileno(termin), F_SETFL, O_NDELAY);
585 if (read(fileno(termin), &havec, 1) == 1)
588 /* Set terminal back to blocking */
589 fcntl(fileno(termin), F_SETFL, 0);
595 /* Read next character from input */
598 static void mpxdied(MPX *m);
601 static time_t last_time;
613 new_time = time(NULL);
614 if (new_time != last_time) {
615 last_time = new_time;
632 if (!have) { /* Wait for input */
633 stat_ = read(mpxfd, &pack, sizeof(struct packet) - 1024);
635 if (pack.size && stat_ > 0) {
636 joe_read(mpxfd, pack.data, pack.size);
637 } else if (stat_ < 1) {
638 if (winched || ticked)
643 tty_accept = pack.ch;
646 if (pack.who) { /* Got bknd input */
647 if (tty_accept != NO_MORE_DATA) {
648 if (pack.who->func) {
649 pack.who->func(pack.who->object, pack.data, pack.size);
656 if (tty_accept != NO_MORE_DATA) {
671 if (read(fileno(termin), &havec, 1) < 1) {
672 if (winched || ticked)
682 /* Write string to output */
684 void ttputs(unsigned char *s)
687 obuf[obufp++] = *s++;
688 if (obufp == obufsiz)
693 /* Get window size */
695 void ttgtsz(int *x, int *y)
698 struct ttysize getit;
701 struct winsize getit;
709 if (ioctl(fileno(termout), TIOCGSIZE, &getit) != -1) {
715 if (ioctl(fileno(termout), TIOCGWINSZ, &getit) != -1) {
724 /* void ttshell(char *s); Run a shell command or if 's' is zero, run a
727 static void ttshell PARAMS((unsigned char *cmd));
728 static const char shmsg[] =
729 "You are at the command shell. Type 'exit' to return\n";
732 #define v_or_fork() fork()
734 #define v_or_fork() vfork()
737 static void ttshell(unsigned char *cmd)
739 int x, omode = ttymode;
744 if (!(x = v_or_fork())) {
747 execl(sh, sh, "-c", cmd, NULL);
749 write(2, shmsg, sizeof(shmsg) - 1);
762 /* Create keyboard task */
764 static int mpxresume(void)
772 tty_accept = NO_MORE_DATA;
774 if (!(kbdpid = fork())) {
781 sta = joe_read(fileno(termin), &c, 1);
783 pack.ch = NO_MORE_DATA;
787 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
788 } while (joe_read(fds[0], &pack, 1) == 1);
796 /* Kill keyboard task */
798 static void mpxsusp(void)
802 while (wait(NULL) < 0 && errno == EINTR)
809 /* We used to leave the keyboard copy task around during suspend, but
810 Cygwin gets confused when two processes are waiting for input and you
811 change the tty from raw to cooked (on the call to ttopnn()): the keyboard
812 process was stuck in cooked until he got a carriage return- then he
813 switched back to raw (he's supposed to switch to raw without waiting for
814 the end of line). Probably this should be done for ttshell() as well. */
826 fprintf(stderr, "You have suspended the program. Type 'fg' to return\n");
840 /* Stuff for asynchronous I/O multiplexing. We do not use streams or
841 select() because joe needs to work on versions of UNIX which predate
842 these calls. Instead, when there is multiple async sources, we use
843 helper processes which packetize data from the sources. A header on each
844 packet indicates the source. There is no guarentee that packets getting
845 written to the same pipe don't get interleaved, but you can reasonable
846 rely on it with small packets. */
848 /* This code will explode if pipe, fork, etc. fail. --mirabilos */
850 static int mpxstart(void)
861 return (mpxresume());
864 static void mpxend(void)
874 /* Get a pty/tty pair. Returns open pty in 'ptyfd' and returns tty name
875 * string in static buffer or NULL if couldn't get a pair.
888 /* Newer sgi machines can do it the __svr4__ way, but old ones can't */
890 extern char *_getpty(int *fildes, int oflag, mode_t mode, int nofork);
892 static unsigned char *getpty(int *ptyfd)
894 return (unsigned char *)_getpty(ptyfd, O_RDWR, 0600, 0);
900 /* Strange streams way */
902 extern char *ptsname(int);
904 static unsigned char *getpty(int *ptyfd)
908 *ptyfd = fdm = open("/dev/ptmx", O_RDWR);
911 return (unsigned char *)ptsname(fdm);
917 /* BSD function, present in libc5 and glibc2 and (duh) the BSDs */
919 static unsigned char *getpty(int *ptyfd)
921 static unsigned char name[32];
924 if (openpty(ptyfd, &ttyfd, name, NULL, NULL) == 0)
931 /* The normal way: for each possible pty/tty pair, try to open the pty and
932 * then the corresponding tty. If both could be opened, close them both and
933 * then re-open the pty. If that succeeded, return with the opened pty and the
936 * Logically you should only have to succeed in opening the pty- but the
937 * permissions may be set wrong on the tty, so we have to try that too.
938 * We close them both and re-open the pty because we want the forked process
939 * to open the tty- that way it gets to be the controlling tty for that
940 * process and the process gets to be the session leader.
943 static unsigned char *getpty(int *ptyfd)
946 unsigned char *orgpwd = pwd();
947 static unsigned char **ptys = NULL;
948 static unsigned char *ttydir;
949 static unsigned char *ptydir;
950 static unsigned char ttyname[32];
953 ttydir = US "/dev/pty/";
954 ptydir = US "/dev/ptym/"; /* HPUX systems */
955 if (chpwd(ptydir) || !(ptys = rexpnd(US "pty*")))
957 ttydir = ptydir = US "/dev/"; /* Everyone else */
959 ptys = rexpnd(US "pty*");
965 for (fd = 0; ptys[fd]; ++fd) {
966 strlcpy((char *)ttyname, (char *)ptydir, 32);
967 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
968 if ((*ptyfd = open((char *)ttyname, O_RDWR)) >= 0) {
970 strlcpy((char *)ttyname, (char *)ttydir, 32);
971 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
973 x = open((char *)ttyname, O_RDWR);
977 strlcpy((char *)ttyname, (char *)ptydir, 32);
978 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
979 *ptyfd = open((char *)ttyname, O_RDWR);
981 strlcpy((char *)ttyname, (char *)ttydir, 32);
982 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
996 /* Shell dies signal handler. Puts pty in non-block mode so
997 * that read returns with <1 when all data from process has
999 static volatile sig_atomic_t dead = 0;
1001 static RETSIGTYPE death(int unused)
1003 fcntl(death_fd,F_SETFL,O_NDELAY);
1009 #define SIGCHLD SIGCLD
1012 /* Build a new environment, but replace one variable */
1014 extern unsigned char **mainenv;
1016 static unsigned char **newenv(unsigned char **old, unsigned char *s)
1018 unsigned char **new;
1021 for (x = 0; old[x]; ++x) ;
1022 new = (unsigned char **) joe_malloc((x + 2) * sizeof(unsigned char *));
1024 for (x = 0, y = 0; old[x]; ++x) {
1025 for (z = 0; s[z] != '='; ++z)
1026 if (s[z] != old[x][z])
1040 /* Create a shell process */
1042 MPX *mpxmk(int *ptyfd, const unsigned char *cmd, unsigned char **args, void (*func) (/* ??? */), void *object, void (*die) (/* ??? */), void *dieobj)
1044 unsigned char buf[80];
1050 unsigned char *name;
1052 /* Get pty/tty pair */
1053 if (!(name = getpty(ptyfd)))
1056 /* Find free slot */
1057 for (x = 0; x != NPROC; ++x)
1058 if (!asyncs[x].func) {
1065 /* PID number pipe */
1069 /* Acknowledgement pipe */
1071 /* don't leak in error case */
1079 /* Fixes cygwin console bug: if you fork() with inverse video he assumes you want
1080 * ESC [ 0 m to keep it in inverse video from then on. */
1081 set_attr(maint->t,0);
1086 /* Bump no. current async inputs to joe */
1089 /* Start input multiplexer */
1099 /* Remember callback function */
1105 /* Create processes... */
1106 if (!(m->kpid = fork())) {
1107 /* This process copies data from shell to joe */
1108 /* After each packet it sends to joe it waits for
1109 an acknowledgement from joe so that it can not get
1110 too far ahead with buffering */
1112 /* Close joe side of pipes */
1116 /* Flag which indicates child died */
1119 joe_set_signal(SIGCHLD, death);
1121 if (!(pid = fork())) {
1122 /* This process becomes the shell */
1125 /* Close pty (we only need tty) */
1128 /* All of this stuff is for disassociating ourself from
1129 controlling tty (session leader) and starting a new
1130 session. This is the most non-portable part of UNIX- second
1131 only to pty/tty pair creation. */
1132 #ifndef HAVE_LOGIN_TTY
1135 x = open("/dev/tty", O_RDWR);
1136 ioctl(x, TIOCNOTTY, 0);
1139 setsid(); /* I think you do setprgp(0,0) on systems with no setsid() */
1141 /* http://mail-index.netbsd.org/pkgsrc-bugs/2011/06/13/msg043281.html */
1142 #ifndef SETPGRP_VOID
1151 for (x = 0; x != 32; ++x)
1152 close(x); /* Yes, this is quite a kludge... all in the
1153 name of portability */
1156 if ((x = open((char *)name, O_RDWR)) != -1) { /* Standard input */
1157 unsigned char **env = newenv(mainenv, US "TERM=");
1159 #ifdef HAVE_LOGIN_TTY
1163 /* This tells the fd that it's a tty (I think) */
1165 ioctl(x, I_PUSH, "ptem");
1166 ioctl(x, I_PUSH, "ldterm");
1169 /* Open stdout, stderr */
1170 if (dup(x)) {} /* standard output */
1171 if (dup(x)) {} /* standard error */
1173 * yes, stdin, stdout, and stderr must
1174 * all be open for reading and writing.
1175 * On some systems the shell assumes this.
1179 /* We could probably have a special TTY set-up for JOE, but for now
1180 * we'll just use the TTY setup for the TTY was was run on */
1181 #ifdef HAVE_POSIX_TERMIOS
1182 tcsetattr(0, TCSADRAIN, &oldterm);
1184 #ifdef HAVE_SYSV_TERMIO
1185 ioctl(0, TCSETAW, &oldterm);
1187 ioctl(0, TIOCSETN, &oarg);
1188 ioctl(0, TIOCSETC, &otarg);
1189 ioctl(0, TIOCSLTC, &oltarg);
1193 /* Execute the shell */
1194 execve((const char *)cmd, (char **)args, (char **)env);
1196 /* If shell didn't execute */
1197 joe_snprintf_1((char *)buf,sizeof(buf),"Couldn't execute shell '%s'\n",cmd);
1198 if (write(0,(char *)buf,strlen((char *)buf))) {}
1205 /* Tell JOE PID of shell */
1206 joe_write(comm[1], &pid, sizeof(pid));
1208 /* sigpipe should be ignored here. */
1210 /* This process copies data from shell to JOE until EOF. It creates a packet
1214 /* We don't really get EOF from a pty- it would just wait forever
1215 until someone else writes to the tty. So: when the shell
1216 dies, the child died signal handler death() puts pty in non-block
1217 mode. This allows us to read any remaining data- then
1218 read returns 0 and we know we're done. */
1224 /* Read data from process */
1225 pack.size = joe_read(*ptyfd, pack.data, 1024);
1227 /* On SUNOS 5.8, the very first read from the pty returns 0 for some reason */
1229 pack.size = joe_read(*ptyfd, pack.data, 1024);
1231 if (pack.size > 0) {
1232 /* Send data to JOE, wait for ack */
1233 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024 + pack.size);
1235 joe_read(fds[0], &pack, 1);
1238 /* Shell died: return */
1239 pack.ch = NO_MORE_DATA;
1241 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
1246 joe_read(comm[0], &m->pid, sizeof(m->pid));
1248 /* We only need comm once */
1252 /* Close other side of copy process pipe */
1257 static void mpxdied(MPX *m)
1261 while (wait(NULL) < 0 && errno == EINTR)
1271 tty_xonoffbaudrst(void)
1273 #ifdef HAVE_POSIX_TERMIOS
1274 struct termios newterm;
1276 #ifdef HAVE_SYSV_TERMIO
1277 struct termio newterm;
1284 #ifdef HAVE_POSIX_TERMIOS
1285 tcgetattr(fileno(termin), &newterm);
1287 newterm.c_iflag &= ~(IXON | IXOFF);
1289 newterm.c_iflag |= (IXON | IXOFF);
1290 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
1291 baud_reset(cfgetospeed(&newterm));
1293 #ifdef HAVE_SYSV_TERMIO
1294 ioctl(fileno(termin), TCGETA, &newterm);
1296 newterm.c_iflag &= ~(IXON | IXOFF);
1298 newterm.c_iflag |= (IXON | IXOFF);
1299 ioctl(fileno(termin), TCSETAW, &newterm);
1300 baud_reset(newterm.c_cflag & CBAUD);
1302 ioctl(fileno(termin), TIOCGETP, &arg);
1303 ioctl(fileno(termin), TIOCGETC, &targ);
1308 targ.t_startc = otarg.t_startc;
1309 targ.t_stopc = otarg.t_stopc;
1311 ioctl(fileno(termin), TIOCSETC, &targ);
1312 baud_reset(arg.sg_ospeed);