1 /* $MirOS: contrib/code/jupp/tty.c,v 1.23 2017/03/19 19:19:51 tg Exp $ */
3 * UNIX Tty and Process interface
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_STAT_H
16 #ifdef HAVE_SYS_IOCTL_H
17 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_WAIT_H
26 #ifdef HAVE_SYS_PARAM_H
27 #include <sys/param.h>
41 /* We use the defines in sys/ioctl to determine what type
42 * tty interface the system uses and what type of system
45 #ifdef HAVE_POSIX_TERMIOS
47 # ifdef HAVE_SYS_TERMIOS_H
48 # include <sys/termios.h>
51 # ifdef HAVE_SYSV_TERMIO
53 # include <sys/termio.h>
70 /* Straight from the GNU autoconf texinfo documentation */
71 #ifdef TIME_WITH_SYS_TIME
72 # include <sys/time.h>
75 # ifdef HAVE_SYS_TIME_H
76 # include <sys/time.h>
82 /* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think
86 #include <sys/stream.h>
104 /* JOE include files */
113 /** Aliased defines **/
115 /* O_NDELAY, O_NONBLOCK, and FNDELAY are all synonyms for placing a descriptor
116 * in non-blocking mode; we make whichever one we have look like O_NDELAY
120 #define O_NDELAY O_NONBLOCK
123 #define O_NDELAY FNDELAY
127 /* Some systems define this, some don't */
129 #define sigmask(x) (1<<((x)-1))
132 /* Some BSDs don't have TILDE */
137 /* Global configuration variables */
139 int noxon = 0; /* Set if ^S/^Q processing should be disabled */
140 int Baud = 0; /* Baud rate from joerc, cmd line or environment */
145 FILE *termout = NULL;
147 /* Original state of tty */
149 #ifdef HAVE_POSIX_TERMIOS
150 struct termios oldterm;
151 #else /* HAVE_POSIX_TERMIOS */
152 #ifdef HAVE_SYSV_TERMIO
153 static struct termio oldterm;
154 #else /* HAVE_SYSV_TERMIO */
155 static struct sgttyb oarg;
156 static struct tchars otarg;
157 static struct ltchars oltarg;
158 #endif /* HAVE_SYSV_TERMIO */
159 #endif /* HAVE_POSIX_TERMIOS */
161 /* Output buffer, index and size */
163 unsigned char *obuf = NULL;
169 unsigned baud; /* Bits per second */
170 unsigned long upc; /* Microseconds per character */
172 /* TTY Speed code to baud-rate conversion table (this is dumb- is it really
173 * too much to ask for them to just use an integer for the baud-rate?)
176 static int speeds[] = {
177 B50, 50, B75, 75, B110, 110, B134, 134, B150, 150, B200, 200,
178 B300, 300, B600, 600,
179 B1200, 1200, B1800, 1800, B2400, 2400, B4800, 4800, B9600, 9600
196 int have = 0; /* Set if we have pending input */
197 static unsigned char havec; /* Character read in during pending input check */
198 int leave = 0; /* When set, typeahead checking is disabled */
200 /* TTY mode flag. 1 for open, 0 for closed */
201 static int ttymode = 0;
203 /* Signal state flag. 1 for joe, 0 for normal */
204 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];
226 /* Set signals for JOE */
232 joe_set_signal(SIGHUP, ttsig);
233 joe_set_signal(SIGTERM, ttsig);
234 joe_set_signal(SIGINT, SIG_IGN);
235 joe_set_signal(SIGPIPE, SIG_IGN);
238 /* Restore signals for exiting */
244 joe_set_signal(SIGHUP, SIG_DFL);
245 joe_set_signal(SIGTERM, SIG_DFL);
246 joe_set_signal(SIGINT, SIG_DFL);
247 joe_set_signal(SIGPIPE, SIG_DFL);
250 /* Open terminal and set signals */
258 /* Close terminal and restore signals */
266 static volatile sig_atomic_t winched = 0;
268 /* Window size interrupt handler */
269 static RETSIGTYPE winchd(int unused)
272 REINSTALL_SIGHANDLER(SIGWINCH, winchd);
278 static volatile sig_atomic_t ticked = 0;
280 static RETSIGTYPE dotick(int unused)
293 joe_set_signal(SIGALRM, dotick);
299 static void baud_reset(int);
305 #ifdef HAVE_POSIX_TERMIOS
306 struct termios newterm;
308 #ifdef HAVE_SYSV_TERMIO
309 struct termio newterm;
313 struct ltchars ltarg;
318 if (idleout ? (!(termin = stdin) || !(termout = stdout)) : (!(termin = fopen("/dev/tty", "r")) || !(termout = fopen("/dev/tty", "w")))) {
319 fprintf(stderr, "Couldn\'t open /dev/tty\n");
323 joe_set_signal(SIGWINCH, winchd);
333 #ifdef HAVE_POSIX_TERMIOS
334 tcgetattr(fileno(termin), &oldterm);
338 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
340 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
342 newterm.c_cc[VMIN] = 1;
343 newterm.c_cc[VTIME] = 0;
344 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
345 bbaud = cfgetospeed(&newterm);
347 #ifdef HAVE_SYSV_TERMIO
348 ioctl(fileno(termin), TCGETA, &oldterm);
352 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
354 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
356 newterm.c_cc[VMIN] = 1;
357 newterm.c_cc[VTIME] = 0;
358 ioctl(fileno(termin), TCSETAW, &newterm);
359 bbaud = (newterm.c_cflag & CBAUD);
361 ioctl(fileno(termin), TIOCGETP, &arg);
362 ioctl(fileno(termin), TIOCGETC, &targ);
363 ioctl(fileno(termin), TIOCGLTC, <arg);
367 arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK);
382 ioctl(fileno(termin), TIOCSETN, &arg);
383 ioctl(fileno(termin), TIOCSETC, &targ);
384 ioctl(fileno(termin), TIOCSLTC, <arg);
385 bbaud = arg.sg_ospeed;
392 baud_reset(int bbaud)
398 while (x < NELEM(speeds))
399 if (bbaud == speeds[x]) {
400 baud = speeds[x + 1];
408 upc = DIVIDEND / baud;
414 obufsiz = 1000000 / (TIMES * upc);
420 obuf = (unsigned char *) joe_malloc(obufsiz);
439 #ifdef HAVE_POSIX_TERMIOS
440 tcsetattr(fileno(termin), TCSADRAIN, &oldterm);
442 #ifdef HAVE_SYSV_TERMIO
443 ioctl(fileno(termin), TCSETAW, &oldterm);
445 ioctl(fileno(termin), TIOCSETN, &oarg);
446 ioctl(fileno(termin), TIOCSETC, &otarg);
447 ioctl(fileno(termin), TIOCSLTC, &oltarg);
454 /* Timer interrupt handler */
456 static volatile sig_atomic_t yep;
457 static RETSIGTYPE dosig(int unused)
462 /* FLush output and check for typeahead */
464 #ifdef HAVE_SETITIMER
466 static void maskit(void)
471 sigaddset(&set, SIGALRM);
472 sigprocmask(SIG_SETMASK, &set, NULL);
475 static void unmaskit(void)
480 sigprocmask(SIG_SETMASK, &set, NULL);
483 static void pauseit(void)
492 static void maskit(void)
494 sigsetmask(sigmask(SIGALRM));
497 static void unmaskit(void)
502 static void pauseit(void)
514 unsigned long usec = obufp * upc; /* No. usecs this write should take */
516 #ifdef HAVE_SETITIMER
517 if (usec >= 50000 && baud < 9600) {
518 struct itimerval a, b;
520 a.it_value.tv_sec = usec / 1000000;
521 a.it_value.tv_usec = usec % 1000000;
522 a.it_interval.tv_usec = 0;
523 a.it_interval.tv_sec = 0;
525 joe_set_signal(SIGALRM, dosig);
528 setitimer(ITIMER_REAL, &a, &b);
529 joe_write(fileno(termout), obuf, obufp);
534 joe_write(fileno(termout), obuf, obufp);
538 joe_write(fileno(termout), obuf, obufp);
541 if (baud < 9600 && usec / 1000)
550 /* Ack previous packet */
551 if (ackkbd != -1 && tty_accept != NO_MORE_DATA && !have) {
554 if (pack.who && pack.who->func)
555 joe_write(pack.who->ackfd, &c, 1);
557 joe_write(ackkbd, &c, 1);
558 tty_accept = NO_MORE_DATA;
561 /* Check for typeahead or next packet */
563 if (!have && !leave) {
565 fcntl(mpxfd, F_SETFL, O_NDELAY);
566 if (read(mpxfd, &pack, sizeof(struct packet) - 1024) > 0) {
567 fcntl(mpxfd, F_SETFL, 0);
568 joe_read(mpxfd, pack.data, pack.size);
570 tty_accept = pack.ch;
572 fcntl(mpxfd, F_SETFL, 0);
574 /* Set terminal input to non-blocking */
575 fcntl(fileno(termin), F_SETFL, O_NDELAY);
578 if (read(fileno(termin), &havec, 1) == 1)
581 /* Set terminal back to blocking */
582 fcntl(fileno(termin), F_SETFL, 0);
588 /* Read next character from input */
590 void mpxdied(MPX *m);
592 static time_t last_time;
602 new_time = time(NULL);
603 if (new_time != last_time) {
604 last_time = new_time;
620 if (!have) { /* Wait for input */
621 stat_ = read(mpxfd, &pack, sizeof(struct packet) - 1024);
623 if (pack.size && stat_ > 0) {
624 joe_read(mpxfd, pack.data, pack.size);
625 } else if (stat_ < 1) {
626 if (winched || ticked)
631 tty_accept = pack.ch;
634 if (pack.who) { /* Got bknd input */
635 if (tty_accept != NO_MORE_DATA) {
636 if (pack.who->func) {
637 pack.who->func(pack.who->object, pack.data, pack.size);
644 if (tty_accept != NO_MORE_DATA) {
658 if (read(fileno(termin), &havec, 1) < 1) {
659 if (winched || ticked)
669 /* Write string to output */
671 void ttputs(unsigned char *s)
674 obuf[obufp++] = *s++;
675 if (obufp == obufsiz)
680 /* Get window size */
682 void ttgtsz(int *x, int *y)
685 struct ttysize getit;
688 struct winsize getit;
696 if (ioctl(fileno(termout), TIOCGSIZE, &getit) != -1) {
702 if (ioctl(fileno(termout), TIOCGWINSZ, &getit) != -1) {
710 void ttshell(unsigned char *cmd)
712 int x, omode = ttymode;
717 if ((x = fork()) != 0) {
725 execl(sh, sh, "-c", cmd, NULL);
727 fprintf(stderr, "You are at the command shell. Type 'exit' to return\n");
734 /* Create keyboard task */
736 static int mpxresume(void)
744 tty_accept = NO_MORE_DATA;
746 if (!(kbdpid = fork())) {
753 sta = joe_read(fileno(termin), &c, 1);
755 pack.ch = NO_MORE_DATA;
759 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
760 } while (joe_read(fds[0], &pack, 1) == 1);
768 /* Kill keyboard task */
770 static void mpxsusp(void)
774 while (wait(NULL) < 0 && errno == EINTR)
780 /* We used to leave the keyboard copy task around during suspend, but
781 Cygwin gets confused when two processes are waiting for input and you
782 change the tty from raw to cooked (on the call to ttopnn()): the keyboard
783 process was stuck in cooked until he got a carriage return- then he
784 switched back to raw (he's supposed to switch to raw without waiting for
785 the end of line). Probably this should be done for ttshell() as well. */
795 fprintf(stderr, "You have suspended the program. Type 'fg' to return\n");
806 /* Stuff for asynchronous I/O multiplexing. We do not use streams or
807 select() because joe needs to work on versions of UNIX which predate
808 these calls. Instead, when there is multiple async sources, we use
809 helper processes which packetize data from the sources. A header on each
810 packet indicates the source. There is no guarentee that packets getting
811 written to the same pipe don't get interleaved, but you can reasonable
812 rely on it with small packets. */
814 /* This code will explode if pipe, fork, etc. fail. --mirabilos */
816 static int mpxstart(void)
827 return (mpxresume());
830 static void mpxend(void)
840 /* Get a pty/tty pair. Returns open pty in 'ptyfd' and returns tty name
841 * string in static buffer or NULL if couldn't get a pair.
854 /* Newer sgi machines can do it the __svr4__ way, but old ones can't */
856 extern char *_getpty(int *fildes, int oflag, mode_t mode, int nofork);
858 static unsigned char *getpty(int *ptyfd)
860 return (unsigned char *)_getpty(ptyfd, O_RDWR, 0600, 0);
866 /* Strange streams way */
868 extern char *ptsname(int);
870 static unsigned char *getpty(int *ptyfd)
874 *ptyfd = fdm = open("/dev/ptmx", O_RDWR);
877 return (unsigned char *)ptsname(fdm);
883 /* BSD function, present in libc5 and glibc2 and (duh) the BSDs */
885 static unsigned char *getpty(int *ptyfd)
887 static unsigned char name[32];
890 if (openpty(ptyfd, &ttyfd, name, NULL, NULL) == 0)
897 /* The normal way: for each possible pty/tty pair, try to open the pty and
898 * then the corresponding tty. If both could be opened, close them both and
899 * then re-open the pty. If that succeeded, return with the opened pty and the
902 * Logically you should only have to succeed in opening the pty- but the
903 * permissions may be set wrong on the tty, so we have to try that too.
904 * We close them both and re-open the pty because we want the forked process
905 * to open the tty- that way it gets to be the controlling tty for that
906 * process and the process gets to be the session leader.
909 static unsigned char *getpty(int *ptyfd)
912 unsigned char *orgpwd = pwd();
913 static unsigned char **ptys = NULL;
914 static unsigned char *ttydir;
915 static unsigned char *ptydir;
916 static unsigned char ttyname[32];
919 ttydir = US "/dev/pty/";
920 ptydir = US "/dev/ptym/"; /* HPUX systems */
921 if (chpwd(ptydir) || !(ptys = rexpnd(US "pty*")))
923 ttydir = ptydir = US "/dev/"; /* Everyone else */
925 ptys = rexpnd(US "pty*");
931 for (fd = 0; ptys[fd]; ++fd) {
932 strlcpy((char *)ttyname, (char *)ptydir, 32);
933 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
934 if ((*ptyfd = open((char *)ttyname, O_RDWR)) >= 0) {
936 strlcpy((char *)ttyname, (char *)ttydir, 32);
937 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
939 x = open((char *)ttyname, O_RDWR);
943 strlcpy((char *)ttyname, (char *)ptydir, 32);
944 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
945 *ptyfd = open((char *)ttyname, O_RDWR);
947 strlcpy((char *)ttyname, (char *)ttydir, 32);
948 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
962 /* Shell dies signal handler. Puts pty in non-block mode so
963 * that read returns with <1 when all data from process has
965 static volatile sig_atomic_t dead = 0;
967 static RETSIGTYPE death(int unused)
969 fcntl(death_fd,F_SETFL,O_NDELAY);
975 #define SIGCHLD SIGCLD
978 /* Build a new environment, but replace one variable */
980 extern unsigned char **mainenv;
982 static unsigned char **newenv(unsigned char **old, unsigned char *s)
987 for (x = 0; old[x]; ++x) ;
988 new = (unsigned char **) joe_malloc((x + 2) * sizeof(unsigned char *));
990 for (x = 0, y = 0; old[x]; ++x) {
991 for (z = 0; s[z] != '='; ++z)
992 if (s[z] != old[x][z])
1006 /* Create a shell process */
1008 MPX *mpxmk(int *ptyfd, const unsigned char *cmd, unsigned char **args, void (*func) (/* ??? */), void *object, void (*die) (/* ??? */), void *dieobj)
1010 unsigned char buf[80];
1016 unsigned char *name;
1018 /* Get pty/tty pair */
1019 if (!(name = getpty(ptyfd)))
1022 /* Find free slot */
1023 for (x = 0; x != NPROC; ++x)
1024 if (!asyncs[x].func) {
1031 /* PID number pipe */
1035 /* Acknowledgement pipe */
1037 /* don't leak in error case */
1045 /* Fixes cygwin console bug: if you fork() with inverse video he assumes you want
1046 * ESC [ 0 m to keep it in inverse video from then on. */
1047 set_attr(maint->t,0);
1052 /* Bump no. current async inputs to joe */
1055 /* Start input multiplexer */
1065 /* Remember callback function */
1071 /* Create processes... */
1072 if (!(m->kpid = fork())) {
1073 /* This process copies data from shell to joe */
1074 /* After each packet it sends to joe it waits for
1075 an acknowledgement from joe so that it can not get
1076 too far ahead with buffering */
1078 /* Close joe side of pipes */
1082 /* Flag which indicates child died */
1085 joe_set_signal(SIGCHLD, death);
1087 if (!(pid = fork())) {
1088 /* This process becomes the shell */
1091 /* Close pty (we only need tty) */
1094 /* All of this stuff is for disassociating ourself from
1095 controlling tty (session leader) and starting a new
1096 session. This is the most non-portable part of UNIX- second
1097 only to pty/tty pair creation. */
1098 #ifndef HAVE_LOGIN_TTY
1101 x = open("/dev/tty", O_RDWR);
1102 ioctl(x, TIOCNOTTY, 0);
1105 setsid(); /* I think you do setprgp(0,0) on systems with no setsid() */
1107 /* http://mail-index.netbsd.org/pkgsrc-bugs/2011/06/13/msg043281.html */
1108 #ifndef SETPGRP_VOID
1117 for (x = 0; x != 32; ++x)
1118 close(x); /* Yes, this is quite a kludge... all in the
1119 name of portability */
1122 if ((x = open((char *)name, O_RDWR)) != -1) { /* Standard input */
1123 unsigned char **env = newenv(mainenv, US "TERM=");
1125 #ifdef HAVE_LOGIN_TTY
1129 /* This tells the fd that it's a tty (I think) */
1131 ioctl(x, I_PUSH, "ptem");
1132 ioctl(x, I_PUSH, "ldterm");
1135 /* Open stdout, stderr */
1136 if (dup(x)) {} /* standard output */
1137 if (dup(x)) {} /* standard error */
1139 * yes, stdin, stdout, and stderr must
1140 * all be open for reading and writing.
1141 * On some systems the shell assumes this.
1145 /* We could probably have a special TTY set-up for JOE, but for now
1146 * we'll just use the TTY setup for the TTY was was run on */
1147 #ifdef HAVE_POSIX_TERMIOS
1148 tcsetattr(0, TCSADRAIN, &oldterm);
1150 #ifdef HAVE_SYSV_TERMIO
1151 ioctl(0, TCSETAW, &oldterm);
1153 ioctl(0, TIOCSETN, &oarg);
1154 ioctl(0, TIOCSETC, &otarg);
1155 ioctl(0, TIOCSLTC, &oltarg);
1159 /* Execute the shell */
1160 execve((const char *)cmd, (char **)args, (char **)env);
1162 /* If shell didn't execute */
1163 joe_snprintf_1((char *)buf,sizeof(buf),"Couldn't execute shell '%s'\n",cmd);
1164 if (write(0,(char *)buf,strlen((char *)buf))) {}
1171 /* Tell JOE PID of shell */
1172 joe_write(comm[1], &pid, sizeof(pid));
1174 /* sigpipe should be ignored here. */
1176 /* This process copies data from shell to JOE until EOF. It creates a packet
1180 /* We don't really get EOF from a pty- it would just wait forever
1181 until someone else writes to the tty. So: when the shell
1182 dies, the child died signal handler death() puts pty in non-block
1183 mode. This allows us to read any remaining data- then
1184 read returns 0 and we know we're done. */
1190 /* Read data from process */
1191 pack.size = joe_read(*ptyfd, pack.data, 1024);
1193 /* On SUNOS 5.8, the very first read from the pty returns 0 for some reason */
1195 pack.size = joe_read(*ptyfd, pack.data, 1024);
1197 if (pack.size > 0) {
1198 /* Send data to JOE, wait for ack */
1199 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024 + pack.size);
1201 joe_read(fds[0], &pack, 1);
1204 /* Shell died: return */
1205 pack.ch = NO_MORE_DATA;
1207 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
1212 joe_read(comm[0], &m->pid, sizeof(m->pid));
1214 /* We only need comm once */
1218 /* Close other side of copy process pipe */
1223 void mpxdied(MPX *m)
1227 while (wait(NULL) < 0 && errno == EINTR)
1236 tty_xonoffbaudrst(void)
1238 #ifdef HAVE_POSIX_TERMIOS
1239 struct termios newterm;
1241 #ifdef HAVE_SYSV_TERMIO
1242 struct termio newterm;
1249 #ifdef HAVE_POSIX_TERMIOS
1250 tcgetattr(fileno(termin), &newterm);
1252 newterm.c_iflag &= ~(IXON | IXOFF);
1254 newterm.c_iflag |= (IXON | IXOFF);
1255 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
1256 baud_reset(cfgetospeed(&newterm));
1258 #ifdef HAVE_SYSV_TERMIO
1259 ioctl(fileno(termin), TCGETA, &newterm);
1261 newterm.c_iflag &= ~(IXON | IXOFF);
1263 newterm.c_iflag |= (IXON | IXOFF);
1264 ioctl(fileno(termin), TCSETAW, &newterm);
1265 baud_reset(newterm.c_cflag & CBAUD);
1267 ioctl(fileno(termin), TIOCGETP, &arg);
1268 ioctl(fileno(termin), TIOCGETC, &targ);
1273 targ.t_startc = otarg.t_startc;
1274 targ.t_stopc = otarg.t_stopc;
1276 ioctl(fileno(termin), TIOCSETC, &targ);
1277 baud_reset(arg.sg_ospeed);