1 /* C K U P T Y -- C-Kermit pseudoterminal control functions for UNIX */
4 Copyright 1995 by the Massachusetts Institute of Technology.
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted, provided
8 that the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation, and that the name of M.I.T. not be used in advertising or
11 publicity pertaining to distribution of the software without specific,
12 written prior permission. Furthermore if you modify this software you must
13 label your software as modified software and not distribute it in such a
14 fashion that it might be confused with the original M.I.T. software.
15 M.I.T. makes no representations about the suitability of this software for
16 any purpose. It is provided "as is" without express or implied warranty.
18 Modified for use in C-Kermit, and new material added, by:
20 Jeffrey Altman <jaltman@secure-endpoints.com>
21 Secure Endpoints Inc., New York City
26 Built and tested successully on:
27 . 4.4BSD, including BSDI/OS, NetBSD, FreeBSD, OpenBSD, Mac OS X
30 . Digital UNIX 3.2 and 4.0
31 . HP-UX 9.00 and later
35 . QNX 4.25 (except PTY process termination not detected)
42 Included but not tested yet in:
43 . Macintosh OSX, OpenBSD, and any other BSD44-based system not listed above
46 . SCO UNIX 3.2v4.2 (compile fails with syntax error in <memory.h>)
47 . HP-UX 8.00 and earlier (no vhangup or ptsname routines)
51 #include "ckcdeb.h" /* To pick up NETPTY definition */
53 #ifndef NETPTY /* Selector for PTY support */
55 char * ptyver = "No PTY support";
57 #else /* (rest of this module...) */
59 char * ptyver = "PTY support 8.0.014, 20 Aug 2002";
61 /* These will no doubt need adjustment... */
93 #endif /* BSD44ORPOSIX */
98 #endif /* USE_TERMIO */
105 #define POSIX_TERMIOS /* Seems to be a misnomer */
106 #endif /* USE_TERMIO */
109 #ifndef GETPGRP_ONEARG
110 #define GETPGRP_ONEARG
111 #endif /* GETPGRP_ONEARG */
114 #ifdef WANT_UTMP /* See ckupty.h */
116 WANT_UTMP is not defined because (a) the utmp/wtmp junk is the most
117 nonportable part of this module, and (b) we're not logging anybody
118 in, we're just running a process, and don't need to write utmp/wtmp records.
120 #ifndef HAVE_SETUTXENT /* Who has <utmpx.h> */
122 #define HAVE_SETUTXENT
125 #define HAVE_SETUTXENT
128 #define HAVE_SETUTXENT
131 #define HAVE_SETUTXENT
134 #define HAVE_SETUTXENT
137 #define HAVE_SETUTXENT
139 #endif /* UNIXWARE */
141 #endif /* CK_SCOV5 */
144 #endif /* HAVE_SETUTXENT */
146 #ifndef HAVE_UTHOST /* Does utmp include ut_host[]? */
147 #ifdef HAVE_SETUTXENT /* utmpx always does */
150 #ifdef LINUX /* Linux does */
153 #ifdef SUNOS4 /* SunOS does */
156 #ifdef AIX41 /* AIX 4.1 and later do */
161 #endif /* HAVE_SETUTXENT */
162 #endif /* HAVE_UTHOST */
167 #endif /* NO_UT_HOST */
168 #endif /* HAVE_UT_HOST */
170 #endif /* WANT_UTMP */
174 #define HAVE_SYS_SELECT_H
175 #define HAVE_GETUTENT
176 #define HAVE_SETUTENT
186 #endif /* HAVE_PTYTRAP */
194 #endif /* HAVE_PTYTRAP */
215 #define PUSH_TTCOMPAT
232 #endif /* HAVE_PTMX */
236 #endif /* HAVE_STREAMS */
244 #define O_NDELAY O_NONBLOCK
245 #endif /* O_NONBLOCK */
246 #endif /* O_NDELAY */
247 #else /* PTYNOBLOCK */
250 #endif /* O_NDELAY */
252 #endif /* PTYNOBLOCK */
259 #include <sys/wait.h>
260 #endif /* CK_WAIT_H */
265 #endif /* INIT_SPTY */
267 #include <sys/stream.h>
271 /* Make sure we don't get the BSD version */
273 #ifdef HAVE_SYS_TTY_H
274 #include "/usr/include/sys/tty.h"
275 #endif /* HAVE_SYS_TTY_H */
277 #ifdef HAS_PTYVAR /* Where is this set? */
279 #include <sys/ptyvar.h>
281 #else /* HAS_PTYVAR */
283 #ifndef TIOCPKT_FLUSHWRITE
284 #define TIOCPKT_FLUSHWRITE 0x02
285 #define TIOCPKT_NOSTOP 0x10
286 #define TIOCPKT_DOSTOP 0x20
287 #define TIOCPKT_IOCTL 0x40
288 #endif /* TIOCPKT_FLUSHWRITE */
290 #endif /* HAS_PTYVAR */
294 #endif /* HAVE_TTY_H */
296 Because of the way ptyibuf is used with streams messages, we need
297 ptyibuf+1 to be on a full-word boundary. The following weirdness
298 is simply to make that happen.
300 long ptyibufbuf[BUFSIZ/sizeof(long)+1];
301 char *ptyibuf = ((char *)&ptyibufbuf[1])-1;
302 char *ptyip = ((char *)&ptyibufbuf[1])-1;
303 char ptyibuf2[BUFSIZ];
304 unsigned char ctlbuf[BUFSIZ];
305 struct strbuf strbufc, strbufd;
309 #else /* ! STREAMSPTY */
311 /* I/O data buffers, pointers, and counters. */
313 char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
314 char ptyibuf2[BUFSIZ];
316 #endif /* ! STREAMSPTY */
327 #define cfsetospeed(tp,val) (tp)->sg.sg_ospeed = (val)
328 #define cfsetispeed(tp,val) (tp)->sg.sg_ispeed = (val)
329 #define cfgetospeed(tp) (tp)->sg.sg_ospeed
330 #define cfgetispeed(tp) (tp)->sg.sg_ispeed
332 #else /* USE_TERMIO */
335 #define termios termio
336 #endif /* SYSV_TERMIO */
342 #define TCSANOW TCSETS
343 #define TCSADRAIN TCSETSW
344 #define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
349 #define TCSANOW TCSETA
350 #define TCSADRAIN TCSETAW
351 #define tcgetattr(f,t) ioctl(f,TCGETA,(char *)t)
353 #define TCSANOW TIOCSETA
354 #define TCSADRAIN TIOCSETAW
355 #define tcgetattr(f,t) ioctl(f,TIOCGETA,(char *)t)
360 #define tcsetattr(f,a,t) ioctl(f,a,t)
361 #define cfsetospeed(tp,val) (tp)->c_cflag &= ~CBAUD;(tp)->c_cflag|=(val)
362 #define cfgetospeed(tp) ((tp)->c_cflag & CBAUD)
365 #define cfsetispeed(tp,val) \
366 (tp)->c_cflag &= ~CIBAUD; (tp)->c_cflag |= ((val)<<IBSHIFT)
367 #define cfgetispeed(tp) (((tp)->c_cflag & CIBAUD)>>IBSHIFT)
369 #define cfsetispeed(tp,val) (tp)->c_cflag &= ~CBAUD; (tp)->c_cflag|=(val)
370 #define cfgetispeed(tp) ((tp)->c_cflag & CBAUD)
375 struct termios termbuf, termbuf2; /* pty control structure */
378 static int spty = -1;
379 #endif /* INIT_SPTY */
381 #endif /* USE_TERMIO */
383 extern int ttyfd; /* Standard Kermit usage */
386 /* termbuf routines (begin) */
392 These three routines are used to get and set the "termbuf" structure
393 to and from the kernel. init_termbuf() gets the current settings.
394 copy_termbuf() hands in a new "termbuf" to write to the kernel, and
395 set_termbuf() writes the structure into the kernel.
400 memset(&termbuf,0,sizeof(termbuf));
401 memset(&termbuf2,0,sizeof(termbuf2));
403 rc = ioctl(ttyfd, TIOCGETP, (char *)&termbuf.sg);
404 rc |= ioctl(ttyfd, TIOCGETC, (char *)&termbuf.tc);
405 rc |= ioctl(ttyfd, TIOCGLTC, (char *)&termbuf.ltc);
407 rc |= ioctl(ttyfd, TIOCGSTATE, (char *)&termbuf.state);
408 #endif /* TIOCGSTATE */
409 #else /* USE_TERMIO */
412 rc = tcgetattr(spty, &termbuf);
413 debug(F111,"init_termbuf() tcgetattr(spty)",ckitoa(rc),errno);
415 rc = tcgetattr(ttyfd, &termbuf);
416 debug(F111,"init_termbuf() tcgetattr(ttyfd)",ckitoa(rc),errno);
417 #endif /* INIT_SPTY */
418 #endif /* USE_TERMIO */
425 copy_termbuf(cp, len) char *cp; int len; {
426 if (len > sizeof(termbuf))
427 len = sizeof(termbuf);
428 memcpy((char *)&termbuf, cp, len);
431 #endif /* TIOCPKT_IOCTL */
434 set_termbuf() { /* Only make the necessary changes. */
436 if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg)))
437 ioctl(ttyfd, TIOCSETN, (char *)&termbuf.sg);
438 if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc)))
439 ioctl(ttyfd, TIOCSETC, (char *)&termbuf.tc);
440 if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc,
441 sizeof(termbuf.ltc)))
442 ioctl(ttyfd, TIOCSLTC, (char *)&termbuf.ltc);
443 if (termbuf.lflags != termbuf2.lflags)
444 ioctl(ttyfd, TIOCLSET, (char *)&termbuf.lflags);
445 #else /* USE_TERMIO */
446 if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) {
450 x = tcsetattr(spty, TCSANOW, &termbuf);
451 debug(F111,"set_termbuf tcsetattr(spty)",ckitoa(x),errno);
453 x = tcsetattr(ttyfd, TCSANOW, &termbuf);
454 debug(F111,"set_termbuf tcsetattr(ttyfd)",ckitoa(x),errno);
455 #endif /* INIT_SPTY */
457 #endif /* USE_TERMIO */
459 /* termbuf routines (end) */
466 /* Initialize "sa" structure. */
467 sigemptyset(&sa.sa_mask);
469 sa.sa_handler = SIG_IGN;
470 sigaction(SIGHUP, &sa, (struct sigaction *)0);
472 sa.sa_handler = SIG_DFL;
473 sigaction(SIGHUP, &sa, (struct sigaction *)0);
474 #else /* CK_POSIX_SIG */
475 signal(SIGHUP,SIG_IGN);
477 signal(SIGHUP,SIG_DFL);
478 #endif /* CK_POSIX_SIG */
479 #endif /* CK_VHANGUP */
483 This routine is called twice. It's not particularly important that the
484 setsid() or TIOCSTTY ioctls succeed (they may not the second time), but
485 rather that we have a controlling terminal at the end. It is assumed that
486 vhangup doesn't exist and confuse the process's notion of controlling
487 terminal on any system without TIOCNOTTY. That is, either vhangup() leaves
488 the controlling terminal in tact, breaks the association completely, or the
489 system provides TIOCNOTTY to get things back into a reasonable state. In
490 practice, vhangup() either breaks the association completely or doesn't
491 effect controlling terminals, so this condition is met.
494 ptyint_void_association() {
498 "ptyint_void_association()",
503 #endif /* HAVE_SETSID */
506 /* Void tty association first */
508 con_fd = open("/dev/tty", O_RDWR);
510 "ptyint_void_association() open(/dev/tty,O_RDWR)",
514 ioctl(con_fd, TIOCNOTTY, 0);
518 else debug(F101, "ptyint_void_association() open() errno","",errno);
520 #endif /* TIOCNOTTY */
521 #endif /* NO_DEVTTY */
525 /* PID may be zero for unknown.*/
528 pty_cleanup(slave, pid, update_utmp) char *slave; int pid; int update_utmp; {
531 #endif /* VHANG_LAST */
533 debug(F111,"pty_cleanup()",slave,pid);
536 pty_update_utmp(PTY_DEAD_PROCESS,
541 PTY_UTMP_USERNAME_VALID
543 #endif /* WANT_UTMP */
553 Revoke isn't guaranteed to send a SIGHUP to the processes it
554 dissociates from the terminal. The best solution without a Posix
555 mechanism for forcing a hangup is to killpg() the process group of the
556 pty. This will at least kill the shell and hopefully, the child
557 processes. This is not always the case, however. If the shell puts
558 each job in a process group and doesn't pass along SIGHUP, all
559 processes may not die.
565 kill(-(pid), SIGHUP);
566 #endif /*HAVE_KILLPG*/
568 #else /* HAVE_REVOKE*/
575 sigaddset(&new, SIGCHLD);
576 sigprocmask(SIG_BLOCK, &new, &old);
577 #else /*CK_POSIX_SIG*/
578 int mask = sigblock(sigmask(SIGCHLD));
579 #endif /*CK_POSIX_SIG*/
580 switch (retval = fork()) {
583 sigprocmask(SIG_SETMASK, &old, 0);
584 #else /*CK_POSIX_SIG*/
586 #endif /*CK_POSIX_SIG*/
589 ptyint_void_association();
590 if (retval = (pty_open_ctty(slave, &fd)))
597 waitpid(retval, &status, 0);
598 #else /*HAVE_WAITPID*/
600 #endif /* HAVE_WAITPID */
602 sigprocmask(SIG_SETMASK, &old, 0);
603 #else /*CK_POSIX_SIG*/
605 #endif /*CK_POSIX_SIG*/
609 #endif /*VHANG_LAST*/
610 #endif /* HAVE_REVOKE*/
612 slave[strlen("/dev/")] = 'p';
617 #endif /* HAVE_STREAMS */
622 pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; {
630 #endif /* HAVE__GETPTY */
631 #endif /* HAVE_OPENPTY */
633 char *slaveret; /* Temp to hold pointer to slave */
634 #endif /*HAVE__GETPTY*/
639 debug(F100,"HAVE_OPENPTY","",0);
651 #else /* HAVE_OPENPTY */
655 This code is included for Irix; as of version 5.3, Irix has /dev/ptmx, but
656 it fails to work properly; even after calling unlockpt, root gets permission
657 denied opening the pty. The code to support _getpty should be removed if
658 Irix gets working streams ptys in favor of maintaining the least needed code
661 debug(F100,"HAVE__GETPTY","",0);
662 if ((slaveret = _getpty(fd, O_RDWR | O_NDELAY, 0600, 0)) == 0) {
664 return(PTY_GETPTY_NOPTY);
666 if (strlen(slaveret) > slavelength - 1) {
669 return(PTY_GETPTY_SLAVE_TOOLONG);
671 ckstrncpy(slave, slaveret, slavelength);
675 #else /* HAVE__GETPTY */
677 *fd = open("/dev/ptym/clone", O_RDWR|O_NDELAY); /* HPUX */
679 debug(F110,"pty_getpty()","open(/dev/ptym/clone) success",0);
684 debug(F100,"HAVE_PTMX","",0);
685 *fd = open("/dev/ptmx",O_RDWR|O_NDELAY);
687 debug(F110,"pty_getpty()","open(/dev/ptmx) success",0);
690 #endif /* HAVE_PTMX */
692 *fd = open("/dev/ptc", O_RDWR|O_NDELAY); /* AIX */
694 debug(F110,"pty_getpty()","open(/dev/ptc) success",0);
697 *fd = open("/dev/pty", O_RDWR|O_NDELAY); /* sysvimp */
699 debug(F110,"pty_getpty()","open(/dev/pty) success",0);
705 debug(F100,"HAVE_GRANTPT","",0);
706 if (grantpt(*fd) || unlockpt(*fd))
707 return(PTY_GETPTY_STREAMS);
708 #endif /* HAVE_PTMX */
709 #endif /* HAVE_GRANTPT */
712 debug(F100,"HAVE_PTSNAME","",0);
713 p = (char *)ptsname(*fd);
714 debug(F110,"pty_getpty() ptsname()",p,0);
717 debug(F100,"HAVE_TTYNAME","",0);
719 debug(F110,"pty_getpty() ttyname()",p,0);
721 /* XXX If we don't have either what do we do? */
722 return(PTY_GETPTY_NOPTY); /* punt */
723 #endif /* HAVE_TTYNAME */
724 #endif /* HAVE_PTSNAME */
726 if (strlen(p) > slavelength - 1) {
729 return(PTY_GETPTY_SLAVE_TOOLONG);
731 ckstrncpy(slave, p, slavelength);
734 if (fstat(*fd, &stb) < 0) {
736 return(PTY_GETPTY_FSTAT);
738 ptynum = (int)(stb.st_rdev&0xFF);
739 sprintf(slavebuf, "/dev/ttyp%x", ptynum); /* safe */
740 if (strlen(slavebuf) > slavelength - 1) {
743 return(PTY_GETPTY_SLAVE_TOOLONG);
745 debug(F110,"pty_getpty() slavebuf",slavebuf,0);
746 ckstrncpy(slave, slavebuf, slavelength);
749 for (cp = "pqrstuvwxyzPQRST";*cp; cp++) {
750 sprintf(slavebuf,"/dev/ptyXX"); /* safe */
751 slavebuf[sizeof("/dev/pty") - 1] = *cp;
752 slavebuf[sizeof("/dev/ptyp") - 1] = '0';
753 if (stat(slavebuf, &stb) < 0)
755 for (i = 0; i < 16; i++) {
756 slavebuf[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
757 *fd = open(slavebuf, O_RDWR|O_NDELAY);
760 debug(F110,"pty_getpty() found pty master",slavebuf,0);
761 slavebuf[sizeof("/dev/") - 1] = 't'; /* got pty */
762 if (strlen(slavebuf) > slavelength -1) {
765 return(PTY_GETPTY_SLAVE_TOOLONG);
767 debug(F110,"pty_getpty() slavebuf [2]",slavebuf,0);
768 ckstrncpy(slave, slavebuf, slavelength);
772 return(PTY_GETPTY_NOPTY);
774 #endif /*HAVE__GETPTY*/
775 #endif /* HAVE_OPENPTY */
782 debug(F100,"HAVE_PTYM","",0);
783 tty_bank = &master_name[strlen("/dev/ptym/pty")];
784 tty_num = &master_name[strlen("/dev/ptym/ptyX")];
785 slave_bank = &slave_name[strlen("/dev/pty/tty")];
786 slave_num = &slave_name[strlen("/dev/pty/ttyX")];
792 The following is an array of modules that should be pushed on the stream.
793 See configure.in for caviats and notes about when this array is used and not
797 #ifndef HAVE_LINE_PUSH
798 static char *push_list[] = {
810 #endif /* HAVE_LINE_PUSH */
811 #endif /* HAVE_STREAMS */
814 pty_initialize_slave (fd) int fd; {
817 struct termios new_termio;
823 #endif /* POSIX_TERMIOS */
829 #endif /* POSIX_TERMIOS */
831 debug(F111,"pty_initialize_slave()","fd",fd);
834 #ifdef HAVE_LINE_PUSH
835 while (ioctl(fd,I_POP,0) == 0) ; /* Clear out any old lined's */
837 if (line_push(fd) < 0) {
838 debug(F110,"pty_initialize_slave()","line_push() failed",0);
841 return(PTY_OPEN_SLAVE_LINE_PUSHFAIL);
843 #else /*No line_push */
845 char **module = &push_list[0];
847 if (ioctl(fd, I_PUSH, *(module++)) < 0) {
848 debug(F110,"pty_initialize_slave()","ioctl(I_PUSH) failed",0);
849 return(PTY_OPEN_SLAVE_PUSH_FAIL);
854 #endif /*HAVE_STREAMS*/
856 Under Ultrix 3.0, the pgrp of the slave pty terminal needs to be set
857 explicitly. Why rlogind works at all without this on 4.3BSD is a mystery.
859 #ifdef GETPGRP_ONEARG
860 pid = getpgrp(getpid());
863 #endif /* GETPGRP_ONEARG */
865 debug(F111,"pty_initialize_slave()","pid",pid);
868 ioctl(fd, TIOCSPGRP, &pid);
869 #endif /* TIOCSPGRP */
875 rc = tcgetattr(fd,&new_termio);
876 debug(F111,"pty_initialize_slave tcgetattr(fd)",ckitoa(rc),errno);
878 new_termio.c_cc[VMIN] = 1;
879 new_termio.c_cc[VTIME] = 0;
880 rc = tcsetattr(fd,TCSANOW,&new_termio);
881 debug(F111,"pty_initialize_slave tcsetattr(fd)",ckitoa(rc),errno);
884 #endif /* POSIX_TERMIOS */
890 pty_logwtmp (tty, user, host) char *user, *tty, *host; {
892 logwtmp(tty,user,host);
898 int loggingin = user[0]; /* Will be empty for logout */
901 strncpy(ut.ut_host, host, sizeof(ut.ut_host));
902 #endif /* NO_UT_HOST */
904 strncpy(ut.ut_line, tty, sizeof(ut.ut_line));
905 ut.ut_time = time(0);
908 ut.ut_pid = getpid();
909 strncpy(ut.ut_user, user, sizeof(ut.ut_user));
911 tmpx = tty + strlen(tty) - 2;
912 ckmakmsg(utmp_id,5,"kr",tmpx,NULL,NULL);
913 strncpy(ut.ut_id, utmp_id, sizeof(ut.ut_id));
914 ut.ut_pid = (loggingin ? getpid() : 0);
915 ut.ut_type = (loggingin ? USER_PROCESS : DEAD_PROCESS);
917 strncpy(ut.ut_name, user, sizeof(ut.ut_name));
918 #endif /* NO_UT_PID */
920 return(ptyint_update_wtmp(&ut, host, user));
922 #endif /* HAVE_LOGWTMP */
924 #endif /* WANT_UTMP */
927 This routine is called twice. It's not particularly important that the
928 setsid() or TIOCSTTY ioctls succeed (they may not the second time), but
929 rather that we have a controlling terminal at the end. It is assumed that
930 vhangup doesn't exist and confuse the process's notion of controlling
931 terminal on any system without TIOCNOTTY. That is, either vhangup() leaves
932 the controlling terminal in tact, breaks the association completely, or the
933 system provides TIOCNOTTY to get things back into a reasonable state. In
934 practice, vhangup() either breaks the association completely or doesn't
935 effect controlling terminals, so this condition is met.
938 pty_open_ctty(slave, fd) char * slave; int *fd; {
941 debug(F110,"pty_open_ctty() slave",slave,0);
943 /* First, dissociate from previous terminal */
945 if ((retval = ptyint_void_association()) != 0) {
948 "ptyint_void_association() failed",
956 The Ultrix (and other BSD tty drivers) require the process group
957 to be zero in order to acquire the new tty as a controlling tty.
960 #endif /* MUST_SETPGRP */
963 *fd = open(slave, O_RDWR);
965 debug(F111,"pty_open_ctty() open failure", slave, errno);
966 return(PTY_OPEN_SLAVE_OPENFAIL);
970 debug(F110, "pty_open_ctty() open ok", slave, 0);
975 setpgrp(0, getpid());
976 #endif /* MUST_SETPGRP */
980 retval = ioctl(*fd, TIOCSCTTY, 0); /* Don't check return.*/
981 debug(F111,"pty_open_ctty() ioctl TIOCSCTTY",ckitoa(retval),errno);
982 #endif /* TIOCSTTY */
987 pty_open_slave(slave, fd) char *slave; int *fd; {
993 sigemptyset(&sa.sa_mask); /* Initialize "sa" structure. */
995 #endif /* CK_POSIX_SIG */
998 First, chmod and chown the slave. If we have vhangup then we really need
999 pty_open_ctty to make sure our controlling terminal is the pty we're
1000 opening. However, if we are using revoke or nothing then we just need a
1001 file descriiptor for the pty. Considering some OSes in this category break
1002 on the second call to open_ctty (currently OSF but others may), we simply
1003 use a descriptor if we can.
1006 if ((retval = pty_open_ctty(slave, &vfd)) != 0) {
1008 "pty_open_slave() VHANG_FIRST",
1009 "pty_open_ctty() failed",
1016 "pty_open_slave() VHANG_FIRST",
1017 "PTY_OPEN_SLAVE_OPENFAIL",
1020 return(PTY_OPEN_SLAVE_OPENFAIL);
1022 #endif /* VHANG_FIRST */
1024 if (slave == NULL || *slave == '\0') {
1025 debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_TOOSHORT",0);
1026 return(PTY_OPEN_SLAVE_TOOSHORT);
1030 if (chmod(slave, 0)) {
1031 debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_CHMODFAIL",0);
1032 return(PTY_OPEN_SLAVE_CHMODFAIL);
1034 if (chown(slave, 0, 0 ) == -1 ) {
1035 debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_CHOWNFAIL",0);
1036 return(PTY_OPEN_SLAVE_CHOWNFAIL);
1042 #endif /* VHANG_FIRST */
1044 if ((retval = ptyint_void_association()) != 0) {
1047 "ptyint_void_association() failed",
1054 if (revoke (slave) < 0 ) {
1055 debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_REVOKEFAIL",0);
1056 return(PTY_OPEN_SLAVE_REVOKEFAIL);
1058 #endif /* HAVE_REVOKE */
1060 /* Open the pty for real. */
1062 retval = pty_open_ctty(slave, fd);
1064 debug(F111,"pty_open_slave()","pty_open_ctty() failed",retval);
1065 return(PTY_OPEN_SLAVE_OPENFAIL);
1067 retval = pty_initialize_slave(*fd);
1069 debug(F111,"pty_open_slave()","pty_initialize_slave() failed",retval);
1074 testfd = open("/dev/tty", O_RDWR|O_NDELAY);
1076 debug(F111,"pty_open_slave() open failed","/dev/tty",errno);
1079 return(PTY_OPEN_SLAVE_NOCTTY);
1082 #endif /* NO_DEVTTY */
1083 debug(F110,"pty_open_slave()","success",0);
1091 #define UTMP_FILE _PATH_UTMP
1092 #endif /* _PATH_UTMP */
1093 #endif /* UTMP_FILE */
1095 /* If it is *still* missing, assume /etc/utmp */
1098 #define UTMP_FILE "/etc/utmp"
1099 #endif /* UTMP_FILE */
1102 #define WTMP_REQUIRES_USERNAME
1103 #endif /* NO_UT_PID */
1106 pty_update_utmp(process_type, pid, username, line, host, flags)
1109 char *username, *line, *host;
1111 /* pty_update_utmp */ {
1112 struct utmp ent, ut;
1113 #ifndef HAVE_SETUTENT
1116 #endif /* HAVE_SETUTENT */
1117 #ifdef HAVE_SETUTXENT
1119 #endif /* HAVE_SETUTXENT */
1123 #endif /* NO_UT_PID */
1127 debug(F100,"pty_update_utmp()","",0);
1128 strncpy(ent.ut_line, line+sizeof("/dev/")-1, sizeof(ent.ut_line));
1129 ent.ut_time = time(0);
1132 if (process_type == PTY_LOGIN_PROCESS)
1134 #else /* NO_UT_PID */
1138 switch (process_type) {
1139 case PTY_LOGIN_PROCESS:
1140 ent.ut_type = LOGIN_PROCESS;
1142 case PTY_USER_PROCESS:
1143 ent.ut_type = USER_PROCESS;
1145 case PTY_DEAD_PROCESS:
1146 ent.ut_type = DEAD_PROCESS;
1149 return(PTY_UPDATE_UTMP_PROCTYPE_INVALID);
1151 #endif /*NO_UT_PID*/
1155 strncpy(ent.ut_host, host, sizeof(ent.ut_host));
1157 ent.ut_host[0] = '\0';
1158 #endif /* NO_UT_HOST */
1161 if (!strcmp (line, "/dev/console")) {
1174 strncpy(ent.ut_id, s, 4);
1178 tmpx = line + strlen(line)-1;
1179 if (*(tmpx-1) != '/') tmpx--; /* last 2 chars unless it's a '/' */
1181 ckstrncpy(utmp_id, tmpx, 5);
1183 ckmakmsg(utmp_id,5,"kl",tmpx,NULL,NULL);
1185 strncpy(ent.ut_id, utmp_id, sizeof(ent.ut_id));
1187 strncpy(ent.ut_user, username, sizeof(ent.ut_user));
1191 strncpy(ent.ut_name, username, sizeof(ent.ut_name));
1193 #endif /* NO_UT_PID */
1196 strncpy(userbuf, username, sizeof(userbuf));
1200 #ifdef HAVE_SETUTENT
1202 utmpname(UTMP_FILE);
1205 If we need to preserve the user name in the wtmp structure and Our flags
1206 tell us we can obtain it from the utmp and we succeed in obtaining it, we
1207 then save the utmp structure we obtain, write out the utmp structure and
1208 change the username pointer so it is used by update_wtmp.
1211 #ifdef WTMP_REQUIRES_USERNAME
1212 if ((!username[0]) && (flags&PTY_UTMP_USERNAME_VALID) &&line) {
1214 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
1215 utptr = getutline(&ut);
1217 strncpy(userbuf,utptr->ut_user,sizeof(ut.ut_user));
1219 #endif /* WTMP_REQUIRES_USERNAME */
1224 #ifdef HAVE_SETUTXENT
1226 #ifdef HAVE_GETUTMPX
1227 getutmpx(&ent, &utx);
1228 #else /* HAVE_GETUTMPX */
1229 /* For platforms like HPUX and Dec Unix which don't have getutmpx */
1230 strncpy(utx.ut_user, ent.ut_user, sizeof(ent.ut_user));
1231 strncpy(utx.ut_id, ent.ut_id, sizeof(ent.ut_id));
1232 strncpy(utx.ut_line, ent.ut_line, sizeof(ent.ut_line));
1233 utx.ut_pid = pid; /* kludge for Irix, etc. to avoid trunc. */
1234 utx.ut_type = ent.ut_type;
1235 #ifdef UT_EXIT_STRUCTURE_DIFFER
1236 utx.ut_exit.ut_exit = ent.ut_exit.e_exit;
1237 #else /* UT_EXIT_STRUCTURE_DIFFER */
1238 /* KLUDGE for now; eventually this will be a feature test... See PR#[40] */
1240 utx.ut_exit.__e_termination = ent.ut_exit.e_termination;
1241 utx.ut_exit.__e_exit = ent.ut_exit.e_exit;
1243 /* XXX do nothing for now; we don't even know the struct member exists */
1245 #endif /* UT_EXIT_STRUCTURE_DIFFER */
1246 utx.ut_tv.tv_sec = ent.ut_time;
1247 utx.ut_tv.tv_usec = 0;
1248 #endif /* HAVE_GETUTMPX */
1250 strncpy(utx.ut_host, host, sizeof(utx.ut_host));
1255 #endif /* HAVE_SETUTXENT */
1257 #else /* HAVE_SETUTENT */
1258 if (flags&PTY_TTYSLOT_USABLE) {
1263 if ((fd = open(UTMP_FILE, O_RDWR)) < 0)
1266 lseek(fd, (off_t)(lc * sizeof(struct utmp)), SEEK_SET) != -1;
1272 ) != sizeof(struct utmp)
1275 if (strncmp(ut.ut_line, ent.ut_line, sizeof(ut.ut_line)) == 0) {
1277 #ifdef WTMP_REQUIRES_USERNAME
1278 if (!username&&(flags&PTY_UTMP_USERNAME_VALID))
1279 strncpy(userbuf, ut.ut_user, sizeof(ut.ut_user));
1280 #endif /* WTMP_REQUIRES_USERNAME */
1286 if (tty > 0 && (fd = open(UTMP_FILE, O_WRONLY, 0)) >= 0) {
1287 lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
1288 write(fd, (char *)&ent, sizeof(struct utmp));
1291 #endif /* HAVE_SETUTENT */
1293 /* Don't record LOGIN_PROCESS entries. */
1294 if (process_type == PTY_LOGIN_PROCESS)
1297 return(ptyint_update_wtmp(&ent, host, userbuf));
1301 #define WTMP_FILE _PATH_WTMP
1302 #endif /* _PATH_WTMP */
1303 #endif /* WTMP_FILE */
1307 #ifdef HAVE_UPDWTMPX
1308 #define WTMPX_FILE _PATH_WTMPX
1309 #endif /* HAVE_UPDWTMPX */
1310 #endif /* _PATH_WTMPX */
1311 #endif /* WTMPX_FILE */
1313 /* If it is *still* missing, assume /usr/adm/wtmp */
1316 #define WTMP_FILE "/usr/adm/wtmp"
1317 #endif /* WTMP_FILE */
1320 /* The following test can not be made portably */
1322 /* #if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) */
1324 This is ugly, but the lack of standardization in the utmp/utmpx space, and
1325 what glibc implements and doesn't make available, is even worse.
1327 /* #undef HAVE_UPDWTMPX */ /* Don't use updwtmpx for glibc 2.1 */
1328 /* #endif */ /* __GLIBC__ etc */
1333 #undef HAVE_UPDWTMPX /* Don't use updwtmpx for glibc period */
1334 #endif /* __GLIBC__ */
1335 #endif /* COMMENT */
1338 ptyint_update_wtmp(ent,host,user) struct utmp *ent; char *host; char *user; {
1343 #ifdef HAVE_UPDWTMPX
1346 getutmpx(ent, &utx);
1348 strncpy(utx.ut_host, host, sizeof(utx.ut_host) );
1352 strncpy(utx.ut_user, user, sizeof(utx.ut_user));
1353 updwtmpx(WTMPX_FILE, &utx);
1354 #endif /* HAVE_UPDWTMPX */
1357 #ifndef HAVE_UPDWTMPX
1358 /* This is already performed byupdwtmpx if present.*/
1359 updwtmp(WTMP_FILE, ent);
1360 #endif /* HAVE_UPDWTMPX*/
1361 #else /* HAVE_UPDWTMP */
1363 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) >= 0) {
1364 if (!fstat(fd, &statb)) {
1365 memset((char *)&ut, 0, sizeof(ut));
1367 strncpy(ut.ut_id, ent->ut_id, sizeof (ut.ut_id));
1369 strncpy(ut.ut_line, ent->ut_line, sizeof(ut.ut_line));
1370 strncpy(ut.ut_name, ent->ut_name, sizeof(ut.ut_name));
1372 strncpy(ut.ut_host, ent->ut_host, sizeof(ut.ut_host));
1373 #endif /* NO_UT_HOST */
1376 ut.ut_time = uttime;
1378 #ifdef HAVE_GETUTENT
1382 ut.ut_pid = getpid();
1384 ut.ut_type = USER_PROCESS;
1386 ut.ut_type = ent->ut_type;
1394 ut.ut_type = DEAD_PROCESS; /* For Linux brokenness*/
1398 #endif /* USER_PROCESS */
1399 #endif /* HAVE_GETUTENT */
1401 if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
1402 sizeof(struct utmp))
1404 ftruncate(fd, statb.st_size);
1406 chsize(fd, statb.st_size);
1407 #endif /* COHERENT */
1411 #endif /* HAVE_UPDWTMP */
1412 return(0); /* no current failure cases; file not found is not failure!*/
1414 #endif /* WANT_UTMP */
1416 static char Xline[17] = { 0, 0 };
1417 int pty_fork_pid = -1;
1421 Open the slave side of the pty, and do any initialization that is necessary.
1422 The return value is a file descriptor for the slave side.
1430 extern int cmd_rows, cmd_cols;
1431 #endif /* TIOCGWINSZ */
1433 debug(F100,"getptyslave()","",0);
1436 * Opening the slave side may cause initilization of the
1437 * kernel tty structure. We need remember the state of:
1438 * if linemode was turned on
1439 * terminal window size
1441 * so that we can reset them if we need to.
1443 if ((retval = pty_open_slave(Xline, &t)) != 0) {
1446 debug(F111,"getptyslave()","Unable to open slave",retval);
1450 debug(F111,"getptyslave","ttyfd",ttyfd);
1451 debug(F111,"getptyslave","t",t);
1454 #endif /* INIT_SPTY */
1456 if (ioctl(t,I_PUSH,"pckt") < 0) {
1457 debug(F111,"getptyslave()","ioctl(I_PUSH) failed",errno);
1459 fatal("I_PUSH pckt");
1462 #endif /* STREAMSPTY */
1464 /* Set up the tty modes as we like them to be. */
1467 if (cmd_rows || cmd_cols) {
1468 memset((char *)&ws, 0, sizeof(ws));
1469 ws.ws_col = cmd_cols;
1470 ws.ws_row = cmd_rows;
1471 ioctl(t, TIOCSWINSZ, (char *)&ws);
1473 #endif /* TIOCGWINSZ */
1475 /* Settings for sgtty based systems */
1478 termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
1479 #endif /* USE_TERMIO */
1485 /* Settings for UNICOS and HPUX */
1488 termbuf.c_oflag = OPOST|ONLCR|TAB3;
1489 termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
1490 termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
1491 termbuf.c_cflag = EXTB|HUPCL|CS8;
1494 termbuf.c_oflag = OPOST|ONLCR|TAB3;
1495 termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
1496 termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
1497 termbuf.c_cflag = EXTB|HUPCL|CS8;
1501 Settings for all other termios/termio based systems, other than 4.4BSD.
1502 In 4.4BSD the kernel does the initial terminal setup.
1506 termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
1507 termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
1508 termbuf.c_iflag |= ICRNL|IGNPAR;
1509 termbuf.c_cflag |= HUPCL;
1510 termbuf.c_iflag &= ~IXOFF;
1513 termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG;
1514 termbuf.c_oflag |= ONLCR|OXTABS|OPOST;
1515 termbuf.c_iflag |= ICRNL|IGNPAR;
1516 termbuf.c_cflag |= HUPCL;
1517 termbuf.c_iflag &= ~IXOFF;
1519 #endif /* USE_TERMIO */
1523 set_termbuf(); /* Set the tty modes, and make this our controlling tty. */
1543 To be called to determine if a trap is pending on a pty
1544 if and only if select() cannot be used.
1547 pty_trap_pending(fd) int fd; {
1551 rc = ioctl(fd, TIOCTRAPSTATUS, (char *)&pending, sizeof(pending));
1553 debug(F101,"pty_trap_pending()","",pending);
1556 debug(F111,"pty_trap_pending()","ioctl() failed",rc);
1562 To be called after select() has returned indicating that an exception is
1563 waiting on a pty. It should be called with the file descriptor of the pty.
1564 Returns -1 on error; 0 if pty is still open; 1 if pty has closed.
1567 pty_trap_handler(fd) int fd; {
1568 struct request_info ri;
1570 memset(&ri,0,sizeof(ri));
1571 if (ioctl(fd,TIOCREQCHECK,(char *)&ri, sizeof(ri)) != 0) {
1572 debug(F111,"pty_trap_handler()","ioctl(TIOCREQCHECK) failed",errno);
1575 switch (ri.request) {
1577 debug(F110,"pty_trap_handler()","an open() call",0);
1580 debug(F110,"pty_trap_handler()","a close() call",0);
1583 debug(F110,"pty_trap_handler()","an ioctl() call",0);
1584 ri.errno_error = EINVAL;
1586 if (ioctl(fd, TIOCREQSET, (char *)&ri,sizeof(ri)) != 0) {
1587 debug(F111,"pty_trap_handler()","ioctl(TIOCREQSET) failed",errno);
1590 if (ri.request == TIOCCLOSE)
1595 #endif /* HAVE_PTYTRAP */
1598 exec_cmd(s) char * s; {
1599 struct stringarray * q;
1600 char ** args = NULL;
1605 q = cksplit(1,0,s,NULL,"\\%[]&$+-/=*^_@!{}/<>|.#~'`:;?",7,0,0);
1608 args = q->a_head + 1;
1609 execvp(args[0],args);
1612 /* Get a pty, scan input lines. */
1615 do_pty(cmd) char * cmd; {
1621 #endif /* HAVE_PTYTRAP */
1623 msg = 0; /* Message counter */
1624 pty_init(); /* Find an available pty to use. */
1627 if ((retval = pty_getpty(&ttyfd, Xline, 20)) != 0) {
1630 debug(F111,"do_pty()","pty_getpty() fails",retval);
1633 debug(F110,"do_pty() Xline",Xline,0);
1637 Ignoring SIGTTOU keeps the kernel from blocking us. we tweak the tty with
1638 an ioctl() (in ttioct() in /sys/tty.c in a BSD kernel)
1640 signal(SIGTTOU, SIG_IGN);
1641 #endif /* SIGTTOU */
1643 /* Start up the command on the slave side of the terminal */
1645 if (pipe(syncpipe) < 0) {
1646 debug(F110,"do_pty()","pipe() fails",0);
1647 perror("pipe() failed");
1649 debug(F111,"do_pty()","pipe fails",errno);
1652 if ((i = fork()) < 0) {
1653 /* XXX - need to clean up the allocated pty */
1654 perror("fork() failed");
1656 debug(F111,"do_pty()","fork fails",errno);
1659 if (i) { /* Wait for child before writing to parent side of pty. */
1663 #endif /* HAVE_PTYTRAP */
1666 if (read(syncpipe[0], &c, 1) == 0) { /* Slave side died */
1667 perror("Pipe read() failed");
1669 debug(F110,"do_pty()","Slave fails to initialize",0);
1673 pty_fork_pid = i; /* So we can clean it up later */
1674 debug(F101,"do_pty pty_fork_pid","",pty_fork_pid);
1676 /* HPUX does not allow the master to read end of file. */
1677 /* Therefore, we must determine that the slave has been */
1678 /* closed by trapping the call to close(). */
1680 x = ioctl(ttyfd, TIOCTRAP, (char *)&on);
1681 debug(F111,"do_pty ioctl(TIOCTRAP)",ckitoa(x),errno);
1682 #endif /* HAVE_PTYTRAP */
1683 debug(F111,"do_pty()","synchronized - pty_fork_pid",pty_fork_pid);
1686 debug(F110,"do_pty()","Slave starts",0);
1687 if (getptyslave() == 0) {
1689 pty_update_utmp(PTY_USER_PROCESS,
1696 #endif /* WANT_UTMP */
1697 /* Notify our parent we're ready to continue.*/
1698 debug(F110,"do_pty()","slave synchronizing",0);
1699 write(syncpipe[1],"y",1);
1704 debug(F111,"do_pty()","exec_cmd() returns - why?",errno);
1706 debug(F110,"do_pty()","getptyslave() fails - exiting",0);
1710 } /* end of do_pty() */
1715 msg = 0; /* Message counter */
1716 if (Xline[0] && pty_fork_pid >= 0) {
1717 pty_cleanup(Xline,pty_fork_pid,1);