brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (07/26/90)
A few people have reported trouble ftping pty 3.0, Rich appears to be on vacation, and I'm getting worn out sending copies through the mail. So here 'tis. This is NOT the official comp.sources.unix version. Major pty features not in mtty (sorry, Maarten): - disconnectable sessions: faster, easier, more flexible than ``screen'' - efficiency: doesn't dawdle, just as fast even after a reconnect - telnetd patches: as fast as w/o pty, but suddenly users can reconnect! - security: tty group, utmp, detects processes lingering on a tty, more - portability: already reported to work on Sun, Ultrix, Convex, others - twenty-one utilities, including a ``script'' that understands utmp - over fifty options for precise control---nothing is forced upon you - a reasonably smart install script One user reported unpacking, configuring, and installing pty on a Sun 4 in ten minutes, though I think that's a bit optimistic for most systems. Plan on twenty minutes to get a working program, ten minutes more if you have privileges and want to take advantage of pty's security features. (Send comments on this time estimate to the Paperwork Reduction Division of the OMB... seriously, let me know how well installation goes and whether you find pty useful.) ---Dan #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: CHANGES README INSTALL TESTS Makefile pty.1 config.h pty.h # sigler.h master.h slave.h err.h tty.h getopt.h texts.h sig.h # logs.h file.h file.h.old file.h.new sock.h misc.h pty.c sigler.c # master.c slave.c err.c tty.c texts.c sig.c globals.c logs.c sock.c # misc.c patch patch/README patch/TELNET.FTP patch/Makefile # patch/telnetd.patch patch/igntt.c util util/Makefile util/biff.1 # util/biff.c util/condom util/condom.1 util/disconnect.1 # util/disconnect.c util/excloff.1 util/excloff.c util/exclon.1 # util/exclon.c util/lock.1 util/lock.c util/mesg.1 util/mesg.c # util/reconnect.1 util/reconnect.c util/script util/script.1 # util/script.tidy util/script.tidy.1 util/sess util/sess.1 # util/sesskill.1 util/sesskill.c util/sesslist.1 util/sesslist.c # util/sessname.1 util/sessname.c util/sessuser.1 util/sessuser.c # util/sessutil.c util/sessutil.h util/tiocsti.1 util/tiocsti.c # util/tty.1 util/tty.c util/u.1 util/u.c util/wall.1 util/wall.c # util/who.1 util/who.c util/write.1 util/write.c util/xsessutil.c # Wrapped by brnstnd@kramden on Mon Jun 11 18:07:50 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'CHANGES' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'CHANGES'\" else echo shar: Extracting \"'CHANGES'\" \(733 characters\) sed "s/^X//" >'CHANGES' <<'END_OF_FILE' X6/11/90: Packaged for comp.sources.unix. X6/1/90: pty 3.0. X X5/24/90: who. X5/23/90: lock, exclon, excloff, tiocsti, script, sess, script.tidy. X5/19/90: All working. On to the utilities... X5/12/90: What would it take to use vfork() safely? X5/12/90: -xe destroys csh, not just more. Amusing. -xE default, I guess. X5/12/90: Separated out texts.c, globals.c, sig.c. X5/12/90: Separated out configuration into config.h (as well as Makefile). X5/11/90: Passes lint -haxc, except bzero and some correct char * casts. Grrrr. X5/11/90: Removed last traces of structure copying/comparing. Sigh. X5/10/90: Mostly done. Still needs utmp, other security, ctrlr passing. X5/6/90: Progress. Chug chug chug. X5/1/90: Begin complete rewrite for version 3.0. END_OF_FILE if test 733 -ne `wc -c <'CHANGES'`; then echo shar: \"'CHANGES'\" unpacked with wrong size! fi # end of 'CHANGES' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1989 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' Xpty - run a program under a pty session X Xpty is meant as the sole interface between pseudo-terminals and the rest Xof the system. Some features: improved security; over fifty options for Xprecise control; session disconnecting and reconnecting; full efficiency Xafter a reconnect; a gradual upgrade path so that all your old utilities Xwill work without change; support for applications that need full pty Xcontrol; and a reasonably smart install script. Also in this package are Xtwenty-one utilities, including a ``script'' that actually works. X Xpty has been thoroughly tested on several BSD 4.3-based machines and Xtested on several BSD 4.2-based machines. It should be easy to port to Xany system supporting pseudo-terminals and UNIX-domain sockets. If you Xknow any application of ptys that this program can't handle, or if you Xhave a different machine that would benefit from the modularity and Xpower of a pty port, please let the author know. X Xpty version 3.0, June 1, 1990. XCopyright (c) 1990, Daniel J. Bernstein. XAll rights reserved. X XThis distribution packaged June 11, 1990. X XGeneral layout: XCHANGES Description of changes since first distributed version XCOPYRIGHT A comprehensible copyright notice XINSTALL A script to guide you through compilation and installation XMANIFEST Shipping list XREADME This document XTESTS A script to guide you through some tests XMakefile Installation commands X*.c Programs X*.h Header files Xpty.man Documentation Xpatch/* Patches to other programs to better use pty Xutil/* Utilities X XBefore you do anything else, run the INSTALL script. It'll guide you Xthrough compiling and installing pty and the utilities. Then run TESTS Xto exercise a few of pty's features on your system. patch/ is separate; Xread patch/README at your leisure. X XRead CHANGES for a list of changes. pty -C and pty -W give copyright and Xwarranty information; pty -H prints a help screen. END_OF_FILE if test 1989 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'INSTALL' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'INSTALL'\" else echo shar: Extracting \"'INSTALL'\" \(24808 characters\) sed "s/^X//" >'INSTALL' <<'END_OF_FILE' X#!/bin/sh X# This is a shell script. Feed it to sh. Xecho ' XHi, and welcome to the pty install script. Xpty is a program for managing pseudo-terminals. X XI'\''m not actually going to install anything. XI'\''ll just guide you through what has to be done, Xfrom compiling through installation. X XOne advantage of this hands-off philosophy is that Xyou can kill and restart this script at any time. X XIn the first part of this script, I'\''ll lead you through configuration. XFor the moment, don'\''t actually change anything in your system. Just Xalternate between reading this script and fooling around with the XMakefile, and I'\''ll remind you later, after compilation, of what has Xto be changed elsewhere. X XI need the following programs: echo, tr [-d], man, sed [-n], grep, test. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThe first versions of pty were designed on a BSD 4.2-based system. XDuring May 1990 pty 3.0 was written completely from scratch, Xon a BSD 4.3-based system. It has been thoroughly tested on Xseveral BSD 4.3-based systems, tested with a few Makefile changes Xon several BSD 4.2-based systems, and run on at least one mutant. X XIf you make it through installation and testing and get pty running, Xplease send a note to the author, Dan Bernstein, on the Internet Xat brnstnd@kramden.acf.nyu.edu. Let him know your computer model, OS Xversion, and what changes you had to make. If you have any trouble, Xplease also get in touch with the author. If you have a different kind Xof system with pseudo-terminal support that could use a pty port, the Xauthor would love to hear about it. X XOne note: Like all software, pty comes without warranty, to the extent Xpermitted by applicable law. Use it at your own risk. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XI assume that you don'\''t want to make any major changes Xto your computer just to take advantage of pty'\''s capabilities. XYou might not even have a privileged account, for all I know. X XThe first major decision you have to make is what privileges to give pty. XObviously if you don'\''t have privileges then pty won'\''t either; Xthis is Case UN. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThere are several good reasons for giving programs privileges. XMost importantly, privileged programs can precisely control access Xto a shared resource. pty supports an interactive user list in X/etc/utmp, a login-logout record in /usr/adm/wtmp, and controlled Xaccess to the pseudo-terminal files themselves, including security and Xmode changing. None of these are possible if pty isn'\''t setuid. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XIf you do want pty to have privileges, you have to decide between Xsetting up pty as root (Case ROOT) or as some other user, say ``pty'\'\'' X(Case PTY). Staying away from root has the advantage that problems Xcan'\''t possibly destroy all security in one blow. However, root has Xseveral advantages, mainly because of BSD limitations: 1. A process Xcan'\''t change ownership of a file between its euid and uid. Only root Xcan change ownership. It shouldn'\''t be necessary to change ownership Xof ptys anyway, but that'\''s what many programs assume. 2. MAXUPRC in X<sys/param.h> limits the number of processes under a given *effective* Xuid. This absolutely idiotic behavior is a serious problem for programs Xsetuid to anything other than root. pty tries to work around this Xproblem, but there are no true solutions. 3. Despite the documentation Xin kill(2), many machines don'\''t allow non-root processes to send XCONT to children with a different uid. Unlike almost all other programs, Xpty sensibly handles its child stopping and restarting; but this won'\''t Xwork on those machines if pty isn'\''t root. X XYou should decide now between Case ROOT and Case PTY. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XWhether you'\''ve decided on ROOT, PTY, or UN, Xnow is the time to check the security of the entire pty package. XIt'\''d be difficult for anything to slip through the checks by the Xauthor, testers, and source group moderator, but you should take Xat least a moment to look for your pet security peeves. X XA couple of security notes: The only open(,O_CREAT) in pty is in Xmaster.c, at the top of master(), of a file with a very restricted Xform constructed from the filename of a /dev/ttyxx. There are no Xcreat()s. The only bind() is in sock.c'\''s pty_readsock(), which is Xcalled only from master.c'\''s master() to create a socket with a Xsimilarly restricted name. (fnsty is changed in sigler but not in Xmaster.) The only exec() is in slave.c, after a forced setreuid() Xto a uid that is only set to getuid(). unlink(), mkdir(), and rename() Xare only used with restricted pathnames. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XIn Case PTY, you have to set up a new uid, say ``pty'\'\'' (or whatever Xusername you want). pty should not allow logins. Its password should Xbe an asterisk; its home directory should be /nonexistent; its shell Xshould be /bin/true. In other words, the only access to user pty Xshould be through these setuid programs. X XIn any privileged case you should have group tty, Xwhich most BSD 4.3 systems have as gid 4. X' X Xecho 'Running '\ X'$ ttygroup="`sed -n '\''s/^tty:[^:]*:\(.*\):.*/\1/p'\'' < /etc/group`" ...' Xttygroup="`sed -n 's/^tty:[^:]*:\(.*\):.*/\1/p' < /etc/group`" X Xcase "$ttygroup" in X4) echo ' XI see that you have a tty group, 4 as usual. Good. X' ;; X'') echo ' XYou don'\''t have a tty group. Unless you have a suitable group under a Xdifferent name, you should add the line Xtty:*:4:root Xto /etc/group. X' ;; X*) echo ' XI see that you have a tty group, gid '"$ttygroup"' rather than 4. Okay. X' ;; Xesac X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNext, we'\''re going to drudge through the config.h file. X XIn Cases ROOT or PTY, you have to set up a system-wide directory Xfor storing information about pty sessions. (A session is a program Xstuck under a pty that you can disconnect and reconnect.) Users can Xget around this directory, storing the information in ~/.pty and Xrevoking any privileges, with pty -xS; you can even make this default Xby setting flagxsetuid = 0 in globals.c. However, a single system-wide Xdirectory is safer. X XAnyway, that directory, PTYDIR, should be mode 0700, owner root in case XROOT or pty in case PTY, group irrelevant. PTYDIR is defined in config.h Xas /usr/etc/pty by default; if you choose a different directory, add X-DPTYDIR=\"/what/ever/dir/ect/ory\" to CCOPTS in the Makefile. X XIn Case UN, you may want to set up a directory ~/PTY inside your home Xdirectory, and set PTYDIR to that; you may want to set flagxsetuid = 0 Xin globals.c; or you may want to just type -xS to pty all the time. I Xrecommend the first strategy. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNext come pseudo-terminal pathnames. I assume that your ptys are Xlabelled as /dev/ptyxx and /dev/ttyxx, where xx is any two-letter Xextension. pty can support any initial strings instead of these, Xbut some of the utilities have /dev/tty hardcoded, and lots of other Xprograms running around assume those names. If you'\''re desperate, Xdefine DEVMTY and DEVSTY in the Makefile, and figure out what has to Xbe changed in util/*. (Sorry.) X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThe extension is traditionally [p-za-o][0-9a-f]. The first letter of the Xsecond string is special: if /dev/ttyq0 doesn'\''t exist, for example, then Xnone of /dev/ttyq[0-9a-f] will be used. (That'\''s called a pty bank.) X XYou can change those strings by defining, e.g., PTY1=\"ABCDEFG\" or XPTY2=\"02468abfq\" in the Makefile. You have at least four choices here: X1. Give pty its own, new, banks of new pseudo-terminals with weird bank Xnames not including a through z, and define PTY1 appropriately. pty will Xonly use those new ptys, and older programs will just use the old ptys. XThis may require reconfiguring your system for the larger number of Xterminals. 2. Give pty some new banks, and take out some old ones to Xmake up for it. 3. Give pty a chunk of the old banks. 4. Give pty all Xthe old banks. X XNote that pty searches randomly through PTY1 and PTY2 by default; for Xefficiency, PTY1 really should reflect exactly the pty banks you have. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XSo now you'\''ve decided on new ptys, or set aside some or all of your Xold ptys, and changed PTY1 and PTY2 appropriately. Because BSD gives a Xprocess very little control over its controlling terminal, pty has to Xpass a terminal name explicitly when it reconnects. If any of your tty X(not just pseudo-tty!) filenames are longer than 30 characters, you have Xto set -DTTYNAMELEN=60 or whatever in the Makefile. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' X/etc/utmp has traditionally listed all users logged on. XThis nebulous concept has evolved through the years; given the way Xthat most programs use utmp, utmp is probably better defined as a Xlist of all interactive sessions. Anyway, pty supports this file, Xand will make an entry in it when given -xu. If you have a different Xfile, add -DPTYUTMP_FILE=\"/foo/bar/utmp\" to DEFINES in the Makefile. X XNote that /etc/utmp is unprotected (mode 666) on Suns. This was Sun'\''s Xattempt to let unprivileged programs manage the file. Unforunately, this Xerror in judgment opens up a huge security hole, which even on Xsingle-user machines is an invitation to make mistakes. I advise you to Xmake /etc/utmp owned by root in Case ROOT or pty in Case PTY, mode 644. XThere aren'\''t many unprivileged programs that don'\''t understand the Xpty interface; people on non-Suns have survived so far, and there'\''s Xno reason for you to keep subscribing to an insecure model of resource Xsharing. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XAlthough pty will put an entry into utmp, it doesn'\''t really support Xthe remote-host field. There is no logical association between sessions Xand connections; why should a login require a pty, and why should a Xsession only stick around for one connection? So pty just puts "pty" in Xthe host field. You can change this with -DPTYUTMP_HOST=\"foo\"; you may Xeven want to make PTY_UTMPHOST call a function that you define. X X(Similar comments apply to PTYWTMP_HOST. There are also PTYUTMP_SWHOST Xand PTYWTMP_SWHOST, defaults PTYUTMP_HOST and "pty-sessuser" Xrespectively; see util/sessuser.1 for more information.) X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' X/usr/adm/wtmp has traditionally recorded all logins, logouts, reboots, Xftp sessions, and various other interesting events. (The entry for a Xline in utmp is the last entry for that line in wtmp.) pty supports wtmp Xfully, in the same way as utmp. You should make wtmp mode 644, owner Xroot or pty as appropriate. Change PTYWTMP_FILE if /usr/adm/wtmp is Xsomewhere else. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XYou can turn off utmp support by uncommenting the NO_UTMP line in Xconfig.h. (Just delete the initial /*.) You can also turn off support Xfor wtmp, disconnectable sessions, or changing pseudo-terminal file Xownership; just uncomment the appropriate line. You can force use of Xeach of these features by defining MUST_UTMP, etc., in the same way. X XNote that the usual login programs do their own wtmp management, and Xmany accounting programs incorrectly equate session time with connect Xtime, so users will rarely want wtmp. As more programs appear using the Xpty interface, and as login programs start using a more logical system Xfor accounting, MUST_WTMP may become an appropriate way to monitor Xpseudo-terminal usage. For the moment, I do not advise setting any NO Xor MUST. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' Xpty subscribes (not very willingly) to the BSD 4.3 model of pty Xprotection, with a few twists. All ptys are under group tty, at all Xtimes; if you don'\''t have a tty group, all ptys should be under some Xother protected group at all times. Anyway, pty will change the group of Xeach pseudo-terminal file to PTYGROUP, default 4, under -xc. X' X Xcase "$ttygroup" in X4) echo ' XSince you already have a tty group, gid 4, you'\''re fine. X' ;; X'') echo ' XYou don'\''t have a tty group. You should pick a gid and define X-DPTYGROUP=whatever in the Makefile. X' ;; X*) echo ' XYou have a tty group, but it'\''s not gid 4. You should define X-DPTYGROUP='"$ttygroup"' in the Makefile. X' ;; Xesac X Xecho ' X(Under Case UN, don'\''t worry about all this group stuff. Just leave XPTYGROUP alone.) X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XUnder -xc, pty changes the pseudo-terminal owner to the current real Xuid. The fact that terminals need to be in the file hierarchy reflects a Xserious failure in the BSD terminal model; but anyway, many programs Xexpect to be able to fool around with /dev/tty. After pty is done with Xthe terminal, it sets the owner back to its effective uid. You can Xmodify this behavior by changing PTYOWNER from euid to something else. X XUnder Case UN, PTYOWNER is irrelevant. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XWhile a tty is unused, BSD systems traditionally leave it unprotected. XUnfortunately, this leaves some huge security holes. Here'\''s one of Xthe biggest advantages of a privileged pty manager: unused ttys don'\''t Xhave to be left open for any random program to use. After it'\''s done Xwith a pseudo-terminal, pty changes it to mode UNUSEDPTYMODE, default X0600. (This is another case of a poor model for kludging pty support Xinto unprivileged programs. If you haven'\''t set up separate pty banks Xfor pty, and you really want to allow unprivileged access to unused Xptys, try mode 0660 and make those other programs setgid to tty.) X XA tty that someone'\''s using can be in many different modes. Typically XUSEDPTYMODE should be 0600, which means user-only access, messages off, Xbiff off. If you really, really, really want to make the mistake of Xhaving messages or biff on by default, try 0620, 0700, or 0720. Note Xthat the world protection should always be 0: the point of the tty group Xis that only setgid-tty programs can access ttys. X XAs usual, you can'\''t do anything about these in Case UN. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNow we'\''re past generic configuration and down to your system'\''s Xnitty-gritty. X XFor purity, pty has SIGRET_TYPE, default int, as the type returned by a Xsignal handler. On Suns and ANSI-compliant machines, you should define XSIGRET_TYPE=void. Then you can admire the lint -haxc *.c output. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XOUTBUFSIZE, default 16384, is the size of the buffer pty keeps between Xthe incoming data and the pseudo-tty, and between the pseudo-tty and the Xoutgoing data. If you want to spare less than 32K per pty just for Xbuffer space, feel free to change this. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XGENERIC is another concession to ANSI taste. GENERIC * should be a Xpointer type that any other pointer can be safely converted to and back; XGENERIC is char by default, but on newer machines can be set to void. XAs it'\''ll be at least five years before ANSI manages to outlaw char *, Xand as void * is often invalid, I advise you to leave GENERIC alone. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' Xpty attempts to forward window-size changes transparently. It also Xsupports the auxiliary characters structure, which lets you type a Xsingle character to get system and tty status. X' X Xecho 'Running $ man 4 tty | sed '\''s/_//g'\'' | grep -s winsize' Xif man 4 tty | sed 's/_//g' | grep -s winsize Xthen havewin=y; echo ' XI see you have window sizes. X' Xelse havewin=n; echo ' XI see you don'\''t have window sizes. XYou'\''ll have to comment out #define TTY_WINSIZE in config.h. X(This may be inaccurate on Suns; try grep winsize /usr/include/sys/tt*.h.) X' X case "$ttygroup" in X 4) ;; X '') echo '(Let me guess: This isn'\''t a BSD 4.3 system.)' ;; X *) ;; X esac Xfi X Xecho 'Running $ man 4 tty | sed '\''s/_//g'\'' | grep -s auxchars' Xif man 4 tty | sed 's/_//g' | grep -s auxchars Xthen haveaux=y; echo ' XI see you have auxiliary characters. XYou should uncomment #define TTY_AUXCHARS in config.h. X' Xelse haveaux=n; echo ' XI see you don'\''t have auxiliary characters. X' Xfi X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' Xpty also puts siginterrupt() and usleep() to good use in working Xaround BSD limitations. Without siginterrupt(), there is absolutely Xno way to get per-process non-blocking I/O, so pty may block when it Xdoesn'\''t have to (namely, when it has N bytes to write to the output, Xthe output is a pipe with M bytes of space, and N > M > 0). If you Xdefine SIGINTERRUPT (as default), pty will take some extra effort to Xnever, ever, ever block when it doesn'\''t have to. X XAt one point, pty pauses to kludge around a common bug in UNIX-domain Xsockets. With usleep() it will pause much more briefly. This makes Xreconnects much faster. X' X Xecho 'Running terrupt(). Good. X' X case "$ttygroup$havewinsize" in X 4y) echo 'I'\''ll bet you'\''re a BSD 4.3 system.' ;; X *) echo 'Weird, are you BSD 4.3?' ;; X esac Xelse havesigintr=n; echo ' XI see you don'\''t have siginterrupt(). You have to comment out X#define SIGINTERRUPT in config.h. X' Xfi X Xecho 'Running $ man usleep | sed '\''s/_//g'\'' | grep -s usleep' Xif man usleep | sed 's/_//g' | grep -s usleep Xthen haveusleep=y; echo ' XI see you have usleep(). Good. X' Xelse haveusleep=n; echo ' XI see you don'\''t have usleep(). You have to comment out X#define USLEEP in config.h. X' Xfi X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XFinally (almost done with configuration!), we have to make sure that you Xhave UNIX-domain sockets, and check whether you have file descriptor Xpassing. X' X Xecho 'Running $ test -r /usr/include/sys/un.h' Xif test -r /usr/include/sys/un.h Xthen haveunsocks=y; echo ' XI see you have UNIX-domain sockets. Good. X' X echo 'Running $ man recvmsg | sed '\''s/_//g'\'' | grep -s rights' X if man recvmsg | sed 's/_//g' | grep -s rights X then havefdpass=y; echo ' XI see you have file descriptor passing. Good. (Your fd passing may be Xbuggy, as it'\''s a relatively new, powerful, and rarely exploited Xfeature. If you have trouble, try defining NO_FDPASSING.) X' X else havefdpass=n; echo ' XI see you don'\''t have file descriptor passing. This isn'\''t a Xdisaster, though it means that reconnected sessions will be as Xinefficient as they are in programs other than this one, boo hoo hoo. XUncomment NO_FDPASSING in config.h. X' X fi Xelse haveunsocks=n; echo ' XUh-oh. I don'\''t see <sys/un.h>, which means you probably don'\''t have XUNIX-domain sockets. Without sockets you can'\''t have disconnectable Xsessions. Sigh. I guess you have to define NO_UNIXSOCKS in config.h. X' Xfi X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XWhew! We'\''ve finally made it through configuration. If you want, try Xrunning lint -haxc *.c or whatever your favorite code checker is; the Xlint here gives some bogus errors about correct char * casts, an error Xbecause the SIG_IGN definition is unportable, and a couple of bzero Xcomplaints because FD_ZERO isn'\''t defined very well. X XIf you'\''re on an old system without FD_ZERO, FD_SET, and FD_ISSET in X<sys/types.h>, I recommend you upgrade. Try copying file.h.old to file.h X(the original version is in file.h.new). X XWe'\''re nearly at the end. Change CC and CCOPTS in the Makefile for any Xlast-minute additions; if you'\''re worried, change -s to -g for debugging. XNow compile! % (date; make) >>& Makelog & and come back to this script. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XWhile the program is compiling, let'\''s start providing some support. X(If the compile finishes or has an error, just ignore it for the Xmoment.) These are some real changes, so watch out! X XFirst, in Case PTY, make a new uid, pty, not allowing logins. X X/etc/passwd: pty:*:whatever:4:::/bin/true X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XSecond, make sure you have a tty group set up. X X/etc/group: tty:*:4:root X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThird, make PTYDIR. X XROOT: # mkdir /usr/etc/pty; chmod 700 /usr/etc/pty XPTY: # mkdir /usr/etc/pty; chown pty /usr/etc/pty; chmod 700 /usr/etc/pty XUN: % mkdir ~/PTY; chmod 700 ~/PTY X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XFourth, make sure you have all the pseudo-terminal files set up and Xappropriately configured. X XChange each of them to group tty. X XChange each of them to owner pty in case PTY or root in case ROOT. X XChange each of them to mode UNUSEDPTYMODE (0600). X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XFifth, make sure /etc/utmp and /usr/adm/wtmp exist, owned by root in XCase ROOT or pty in Case PTY, each mode 644. (In Case UN, you probably Xwant to be working on a Sun with its insecure /etc/utmp, so that pty can Xtake advantage of it. Sigh.) X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XSixth, I advise that you make a special directory /usr/local/ptybin for Xpty and its associated utilities, with symbolic links in /usr/local. XThis choice isn'\''t too important, but it'\''s easier if you know a Xplace to put programs. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XOkay. You'\''ve done about all that can be done before compilation Xfinishes, so go do something else. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThe compile should be done now. If all went well, the make should have Xexited normally, Makelog should show no errors, and you should have an Xexecutable pty program sitting in this directory. Unfortunately, life Xisn'\''t always so generous. Here are a few of the more common errors. X XThe loader complains about missing getopt(), optarg, and optind: These Xare all in the getopt library, available in the early volumes of Xcomp.sources.unix. If you use GNU getopt, please make sure you Xunderstand the implications of its license. X XThe loader complains about missing FD_ZERO, FD_SET, and FD_ISSET: Try Xcopying file.h.old to file.h and recompiling. If the compiler gives Xvarious further errors related to these macros, uncomment the Xcommented-out lines in file.h.old, copy to file.h, and try once again. X Xbcopy() isn'\''t defined: In pty.h, change bcopy(src,dst,num) to Xmemcpy(dst,src,num). (Note the order.) Try again. X XIf you still can'\''t get the code to compile, let the author know. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNext, compile the utilities. There isn'\''t much to say about them; Xthey'\''re all public domain, some of them clones of or improvements Xover standard utilities, some of them clones with special features to Xwork with pty, some of them entirely new programs for pty'\''s new Xfeatures. Just glance at the top of util/Makefile; then X% cd util; (date; make) >>& Makelog & and wait. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XSo now you should have some executables sitting around collecting dust. XIn Cases ROOT and PTY, put {pty,biff,disconnect,mesg,reconnect,sesskill, Xsesslist,sessname,sessuser} into a system-wide directory, owner root or Xpty, mode 4755. Put wall and write into the same place, group tty, mode X2755. Put {condom,excloff,exclon,last,lock,script,script.tidy,sess,tiocsti, Xtty,u,who,xdisconnect,xreconnect,xsesskill,xsesslist,xsessname,xsessuser} Xinto the same place, mode 755. Make absolutely sure none of the shell Xscripts are setuid. X XIn Case UN, just put all the programs somewhere in your PATH. X XThat'\''s it! Try running TESTS now. X X(Two other things you have to do at some point: move your old script, Xbiff, mesg, and so on to script.old, biff.old, mesg.old, etc., with Xsymbolic links to the new versions in their place; and move the old X/usr/man/{script,biff,mesg,...}.1 somewhere else, copy all the *.1 Xhere to /usr/man/man1, and run /etc/catman.) X' END_OF_FILE echo shar: 10 control characters may be missing from \"'INSTALL'\" if test 24808 -ne `wc -c <'INSTALL'`; then echo shar: \"'INSTALL'\" unpacked with wrong size! fi chmod +x 'INSTALL' # end of 'INSTALL' fi if test -f 'TESTS' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'TESTS'\" else echo shar: Extracting \"'TESTS'\" \(3669 characters\) sed "s/^X//" >'TESTS' <<'END_OF_FILE' X#!/bin/sh X# This is a shell script. Feed it to sh. Xecho ' XHi, and welcome to the pty test script. X XAs in installation, I'\''m not actually going to do anything. XI'\''ll just guide you through a few (non-comprehensive) tests. X XRemember: Like all software, pty comes without warranty, to the extent Xpermitted by applicable law. Use it at your own risk. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XFirst, just try % pty vi. You shouldn'\''t be able to tell the Xdifference between this and a normal vi; stopping and restarting should Xwork perfectly, as should normal typing. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNext, try % pty -0 tr \! \? | pty vi. This should work just like the Xlast vi, with the following differences: 1. Stopping will require two X^Zs, because csh idiotically doesn'\''t think a pipeline has stopped Xwhen just its second component stops. 2. Exiting will require an extra Xline to feed through tr, so that it gets a broken pipe; this is more Xsensible than #1. 3. All exclamation points will be turned into question Xmarks. This has obvious applications. :w /dev/null helps to escape. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XContinuing along the lines of how to stick annoying programs into a Xpipe, try % pty -0 sed '\''s/foo/bar/g'\'' | more. (Remember that sed outputs Xeach line only after it receives the next.) Try the same thing without Xpty. X XNote that pty -0 can be abbreviated as condom. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XNext, try using the replacement script program. % script. Type various Xcommands; try logging on to another terminal and using talk; observe Xthat you'\''re listed in /etc/utmp. Try the clones of tty, mesg, biff, Xu, wall, who, lock. (Try them under a non-pty session too.) X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XMuch of the fun of pty is in disconnecting and reconnecting sessions. XIf you'\''re ambitious, try % sess sh. ^Z will get you out and back in. XTry sessname without an argument; try it with an argument. Try sesslist. XFinally, try $ disconnect, and go on to the next part of this script. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XAlthough it looks like your sh session has finished, it'\''s actually Xsitting in limbo, waiting for you to reconnect. You can still see it Xunder who, sesslist, or ps. Now try % sess reconnect q7 or whatever the Xextension of the disconnected session is; you should be right back in. X XYou can try the same thing by actually hanging up your connection, then Xlogging in again and reconnecting. X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XFor one final trick, X% sess sh X$ PS1=FOO.; disconnect X% sess sh X$ sesslist X... X$ PS1=BAR.; reconnect q7; disconnect XFOO.reconnect p4; disconnect XBAR.reconnect q7; disconnect XFOO.echo Neat, flipping right back and forth! X' X Xecho '----- Press return to continue. ' | tr -d '\012'; read contline X Xecho ' XThat'\''s it! Make sure the manual pages and programs are easily Xaccessible. To repeat a note from INSTALL: X XIf you make it through installation and testing and get pty running, Xplease send a note to the author, Dan Bernstein, on the Internet Xat brnstnd@kramden.acf.nyu.edu. Let him know your computer model, OS Xversion, and what changes you had to make. If you have any trouble, Xplease also get in touch with the author. If you have a different kind Xof system with pseudo-terminal support that could use a pty port, the Xauthor would love to hear about it. X XThanks! X' END_OF_FILE if test 3669 -ne `wc -c <'TESTS'`; then echo shar: \"'TESTS'\" unpacked with wrong size! fi # end of 'TESTS' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(3507 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' XCC=cc XCCOPTS=-O2 -s X XNROFF=nroff XNROFFOPTS=-man X Xdefault: all X Xall: pty pty.man X Xshar: pty.shar X Xpty: pty.o sigler.o master.o slave.o err.o tty.o texts.o sig.o globals.o logs.o sock.o misc.o Makefile X cc $(CCOPTS) -o pty texts.o globals.o pty.o sigler.o master.o slave.o err.o tty.o sig.o logs.o sock.o misc.o X Xpty.o: pty.c pty.h getopt.h sigler.h master.h slave.h err.h tty.h config.h texts.h sig.h logs.h file.h misc.h Makefile X cc $(CCOPTS) -c pty.c X Xsigler.o: sigler.c pty.h sigler.h config.h sig.h sock.h file.h err.h misc.h Makefile X cc $(CCOPTS) -c sigler.c X Xmaster.o: master.c pty.h master.h err.h config.h sig.h tty.h file.h logs.h sock.h misc.h Makefile X cc $(CCOPTS) -c master.c X Xslave.o: slave.c pty.h tty.h slave.h err.h config.h sig.h file.h logs.h Makefile X cc $(CCOPTS) -c slave.c X Xerr.o: err.c err.h pty.h config.h Makefile X cc $(CCOPTS) -c err.c X Xtty.o: tty.c tty.h err.h config.h file.h Makefile X cc $(CCOPTS) -c tty.c X Xsig.o: sig.c sig.h config.h Makefile X cc $(CCOPTS) -c sig.c X Xtexts.o: texts.c texts.h config.h Makefile X cc $(CCOPTS) -c texts.c X Xglobals.o: globals.c config.h pty.h tty.h Makefile X cc $(CCOPTS) -c globals.c X Xlogs.o: logs.c config.h pty.h file.h Makefile X cc $(CCOPTS) -c logs.c X Xsock.o: sock.c config.h sock.h tty.h err.h Makefile X cc $(CCOPTS) -c sock.c X Xmisc.o: misc.c config.h pty.h misc.h Makefile X cc $(CCOPTS) -c misc.c X Xpty.man: pty.1 Makefile X nroff $(NROFFOPTS) < pty.1 > pty.man X Xpty.shar: CHANGES README INSTALL TESTS Makefile pty.1 config.h pty.h sigler.h master.h slave.h err.h tty.h getopt.h texts.h sig.h logs.h file.h file.h.old file.h.new sock.h misc.h pty.c sigler.c master.c slave.c err.c tty.c texts.c sig.c globals.c logs.c sock.c misc.c patch patch/README patch/TELNET.FTP patch/Makefile patch/telnetd.patch patch/igntt.c util util/Makefile util/biff.1 util/biff.c util/condom util/condom.1 util/disconnect.1 util/disconnect.c util/excloff.1 util/excloff.c util/exclon.1 util/ex clon.c util/lock.1 util/lock.c util/mesg.1 util/mesg.c util/reconnect.1 util/reconnect.c util/script util/script.1 util/script.tidy util/script.tidy.1 util/sess util/sess.1 util/sesskill.1 util/sesskill.c util/sesslist.1 util/sesslist.c util/sessname.1 util/sessname.c util/sessuser.1 util/sessuser.c util/sessutil.c util/sessutil.h util/tiocsti.1 util/tiocsti.c util/tty.1 util/tty.c util/u.1 util/u.c util/wall.1 util/wall.c util/who.1 util/who.c util/write.1 util/write.c util/xsessutil.c X shar CHANGES README INSTALL TESTS Makefile pty.1 config.h pty.h sigler.h master.h slave.h err.h tty.h getopt.h texts.h sig.h logs.h file.h file.h.old file.h.new sock.h misc.h pty.c sigler.c master.c slave.c err.c tty.c texts.c sig.c globals.c logs.c sock.c misc.c patch patch/README patch/TELNET.FTP patch/Makefile patch/telnetd.patch patch/igntt.c util util/Makefile util/biff.1 util/biff.c util/condom util/condom.1 util/disconnect.1 util/disconnect.c util/excloff.1 util/excloff.c util/exclon.1 util/exclon c util/lock.1 util/lock.c util/mesg.1 util/mesg.c util/reconnect.1 util/reconnect.c util/script util/script.1 util/script.tidy util/script.tidy.1 util/sess util/sess.1 util/sesskill.1 util/sesskill.c util/sesslist.1 util/sesslist.c util/sessname.1 util/sessname.c util/sessuser.1 util/sessuser.c util/sessutil.c util/sessutil.h util/tiocsti.1 util/tiocsti.c util/tty.1 util/tty.c util/u.1 util/u.c util/wall.1 util/wall.c util/who.1 util/who.c util/write.1 util/write.c util/xsessutil.c > pty.shar X chmod 400 pty.shar X Xpty.h: tty.h Xsig.h: config.h Xtty.h: config.h END_OF_FILE if test 3507 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'pty.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pty.1'\" else echo shar: Extracting \"'pty.1'\" \(12042 characters\) sed "s/^X//" >'pty.1' <<'END_OF_FILE' X.TH pty 1 X.SH NAME Xpty \- run a program under a pseudo-terminal session X.SH SYNTAX Xpty X[ X\fB\-qQvdDe3EjJsStT0\fI X] [ X\fB\-F\fI X] [ X\fB\-f\fIn X] [ X\fB\-p\fI[cCdDeEnNrRsS0] X] [ X\fB\-x\fI[cCeEnNoOrRsSuUwWxX] X] [ X\fB\-ACHUVW\fI X] Xprogram X[ Xarg ... X] X.SH DESCRIPTION X.I pty Xdetaches itself from its original Xterminal, Xallocates a pseudo-terminal, Xand runs X.I program Xon that pseudo-terminal Xwith any given arguments. X.I pty Xlets you easily disconnect from and reconnect to Xsessions; Xit has over fifty options for precise control, Xand is meant to act as the sole interface Xbetween pseudo-terminals and the rest of the system. X.PP XThere are a few very common invocations of X.I pty. XThe most common is just X.I pty program, Xwith no options; Xthis has several effects as described below. X.I pty \-s program Xsets up a disconnectable session; Xit is described further in X.I sess(1). X.I pty \-0 program Xisolates the rest of the world from X.I program Xin several ways; Xit is described further in X.I condom(1). X.PP XThe two next most commonly used options Xare X.I\-d, Xif X.I pty Xis started without a controlling terminal; Xand X.I\-xu, Xwhich makes an entry in X/etc/utmp. X.PP XSome programs (such as X.I vi) Xdon't like taking input or output Xfrom a pipe; under X.I pty, Xthey won't notice a thing. XOther programs buffer as much output as possible Xif they're in a pipe; Xunder X.I pty, Xthey'll buffer at most a line of output. X.I pty Xis very careful to restore terminal modes upon Xstopping or exiting; Xa careless X.I program Xis shielded from your terminal. XOtherwise, X.I pty program Xshould feel just like X.I program. X.PP X.I pty Xchanges the original terminal to character-at-a-time X(raw, cbreak, and noecho) mode; it sets the pseudo-terminal to Xthe original mode. When X.I program Xfinishes, X.I pty Xwill set the original terminal back to its original mode. XAny mode changes on the pseudo-terminal will be lost. X.PP X.I pty Xsets file descriptor 0 to input from the Xpseudo-terminal, 1 and 2 to output. X.I pty Xalso supports the ``3 is /dev/tty'' convention: Xit sets up file descriptor 3 for input from, output to, Xand terminal commands for X/dev/tty X(not the original tty, but the pseudo tty). X.PP XOptions X.B ACHUVW Xprint the authorship notice, Xcopyright notice, Xhelp notice, Xshort usage summary, Xversion number, Xand warranty information respectively. X.PP X.I pty Xhas quite a few flags, Xprocessed left to right: X.TP 12 X\fB\-d\fI X.I pty Xwill assume it is already detached from the terminal. XThis forces X.B\-T; Xit sets but doesn't force X.B\-J. XAlso, instead of copying pseudo-terminal modes from Xthe original terminal, X.I pty Xwill set up a generic new-discipline line-at-a-time mode. X.TP X\fB\-D\fI X.I pty Xwill assume that it is attached to a terminal (default). XThis sets X.B\-jt. X.TP X\fB\-e\fI XPreserve Xstandard error (file descriptor 2) Xand standard tty (file descriptor 3). X.TP X\fB\-3\fI XPreserve fd 3, but point fd 2 at the pseudo-terminal. X.TP X\fB\-E\fI XDirect both file descriptors to the pseudo-terminal (default). X(Actually, X.I pty Xwill point standard error at the tty by name, Xbut fd 3 at /dev/tty, Xso that various X.I ioctl() Xcommands on fd 3 will work.) XAlso close all higher-numbered file descriptors. X.TP X\fB\-j\fI XJob control (default): When X.I program Xstops, X.I pty Xstops. XWhen X.I pty Xis restarted, Xit restarts X.I program. X.TP X\fB\-J\fI XNo job control. XIf X.I program Xstops, X.I pty Xwill ignore it X(though it will always restart it upon a reconnect). XThis behavior is much less Xantisocial than Xthe behavior of the previous X.I pty Xversion. X.TP X\fB\-t\fI X.I pty Xwill set the Xoriginal terminal to Xno-echo, character-at-a-time mode (default). X.TP X\fB\-T\fI X.I pty Xwill not touch the original terminal, if there is one. XIt is always dangerous to put two programs in a pipe if both Xchange tty modes; X.I pty, X.I vi, Xand X.I more Xare examples of such programs. If you use X.I pty Xin a pipe Xwith another tty-mode-changing program, Xmake sure to specify X.B\-0 X(which is an abbreviation for X.B\-Tp0) Xso that X.I pty Xwill neither affect nor be affected by the other program. XIf you use a pipe of ptys, Xyou should probably specify X.B\-0 Xin all but one. X.TP X\fB\-s\fI XSession. XWhen the connection is hung up or manually disconnected, X.I pty Xwill patiently wait for a Xreconnection. X.I program Xwon't notice a thing. X.B\-s Xsets but does not force X.B\-xu. XIt forces X.B\-E. X.TP X\fB\-S\fI XNo session (default). XWhen the connection is hung up, X.I pty Xwill send a HUP to X.I program. X.B\-S Xsets but does not force X.B\-xU. X.TP X\fB\-q\fI XQuiet. X.I pty Xwill print absolutely nothing on standard error. XIt will communicate strange events through its exit code. X.TP X\fB\-Q\fI XNot quiet (default). X.I pty Xwill generate bits of chatter about interesting Xevents. X.TP X\fB\-v\fI XVerbose. X.I pty Xwill blabber on and on and on and on and on and on and on and on. XIt keeps going, Xand going, Xand going, Xand going ... X.TP X\fB\-f\fIfd XPass the master and slave sides of the pseudo-terminal Xup in file descriptor X.I fd. XThis is only available if your machine supports descriptor passing. X XThe protocol, from the point of view of the receiver (``controller''), Xis to pty_getch a character off the other side Xof the passing descriptor, perhaps check that it is a G, Xand pty_putgetint a G for the process id of the signaller X(the process to recieve a HUP if the connection drops); Xpty_getch a character, perhaps check that it is m, Xand pty_putgetfd an m for the master side of the pseudo-terminal; Xand similarly for s and the slave side. (These functions are all Xavailable in sock.c in the X.I pty Xsource code.) X XWhen the connection is dropped, X.I pty Xwill send up a period, Xfollowed by one pid and two new descriptors as above Xif it reconnects. XIn the meantime, the controller has full responsibility for Xperforming terminal I/O. X.TP X\fB\-F\fI XDo not pass anything (default). X.TP X\fB\-p\fImmm XSet the Xpseudo-terminal to modes specified by X.I m. XUnder X.B\-d, Xdefaults are taken from the Xcurrent terminal; Xunder X.B\-D, Xdefaults are as below. XPredefined modes: X.RS X.TP 5 X.I c XSet cbreak (character-at-a-time) mode. X.TP X.I C XDo not set cbreak mode (default). X.TP X.I d XUse the new discipline (default, breaking with tradition). X.TP X.I D XUse the old discipline, without job control or other fancy tty features. X.TP X.I e XEcho characters (default). X.TP X.I E XDo not echo. X.TP X.I n XChange return to newline (default). X.TP X.I N XDo not change return to newline. X.TP X.I r XSet raw mode: do not produce signals, and pass eight-bit characters. X.TP X.I R XSet non-raw (``cooked'') mode (default). X.TP X.I s XSet line editing modes appropriate for a screen (default). X.TP X.I S XDo not set crt line editing modes. X.TP X.I 0 XAn abbreviation for pcEN. X.RE X.TP X\fB\-x\fIsss XUse security, experimental, or extended measures specified by X.I s. XSome of these may be required or disabled by your system administrator. XPredefined values: X.RS X.TP 5 X.I c XChange the ownership and protections of the pty appropriately. XThis reflects several errors in the X.I pty Xmodel, but it's customary. X.TP X.I C XDo not change pty ownership (default). X.TP X.I e XOpen stderr write-only to the pseudo-terminal. XThis should be default, but such programs as X.I csh Xand X.I more Xinsist on reading from stderr and dying horribly Xif they fail. X.B\-xe Xis useless under X.B\-e. X.TP X.I E XOpen stderr for reading and writing (default). X.TP X.I u XEnter login name into /etc/utmp. XAs a rule of thumb, Xyou should do this for interactive sessions. X.TP X.I n XUse some heuristics to try to figure out if someone Xhas the pty open (default). X.TP X.I N XDon't worry about pre-opened ptys. X.TP X.I o XSame as X.B\-xn, Xbut go on to the next pseudo-terminal Xif this one is open. X.TP X.I O XDon't skip pre-opened ptys. X.TP X.I r XRandom pseudo-terminal searching (default). XThis can provide a huge boost to speed and security. XIt hasn't been used because programmers don't realize Xthe virtues of modularity, are consequently too lazy to Xwrite something like X.I pty, Xand don't want to take the effort for random pty searching Xin every program that uses pseudo-terminals. X.TP X.I R XStraight pty searching, from the bottom on up. X.TP X.I s XSetuid (default). X.I pty Xwill use a common directory Xfor storing session information. X.TP X.I S XNot setuid. X.I pty Xwill revoke all privileges and Xuse a subdirectory of your HOME directory. X.TP X.I U XDo not use utmp (default). X.TP X.I w XMake an entry in /usr/adm/wtmp. XThis probably isn't a good idea for general use, Xas Xconnection time recorded in X.I wtmp Xis often pressed into unfortunate service as Xa senseless basis for charged computer time. X.TP X.I W XDo not use wtmp (default). X.TP X.I x XSet exclusive use on the pty. XNo processes can open the pty after this; X.I program Xcan't even reopen X/dev/tty! X(It can use file descriptor 3 instead.) XThis can be very important for security when X.I pty Xhas not been installed by the system administrator. XIt should be set all the time, but Xtoo many programs rely on a filename for the terminal. X.TP X.I X XDo not set exclusive use (default). X.RE X.PP X.SH DIAGNOSTICS X.TP Xvarious usage messages XExit 1. X.TP X.I fatal: cannot find control terminal X.I pty Xis unable to find its current control terminal. XExit 2. X.TP X.I fatal: cannot get current tty modes XThis shouldn't happen. XExit 3. X.TP X.I fatal: cannot set modes of original tty XThis shouldn't happen. XExit 4. X.TP X.I fatal: no ptys available XSelf-explanatory. XExit 5. X.TP X.I fatal: can't fcntl pty X.TP X.I fatal: slave unopenable XThere's a serious problem with your pseudo-terminal setup. XReport this error to your system administrator. XExit 6. X.TP X.I fatal: cannot fork X.I pty Xhas run out of processes. XExit 7. X.TP X.I fatal: cannot change to session directory XSelf-explanatory. XExit 8. X.TP X.I fatal: cannot open internal pipe. XCannot happen. XExit 10. X.TP X.I fatal: socket read error XSelf-explanatory. Exit 11. X.TP X.I fatal: socket write error XSelf-explanatory. Exit 12. X.TP X.I fatal: input read error XSelf-explanatory. Exit 13. X.TP X.I fatal: output write error XSelf-explanatory. Exit 14. X.SH RESTRICTIONS XThere are many security problems Xand limitations associated with BSD-style ttys. X.I pty Xdoes its best to avoid them, Xbut a Streams-based system would be much better. XThe author plans to rewrite X.I pty, Xwith the same interface, Xfor a Streams system. X.PP XThe current X.B\-J Xbehavior is a bit dull. XI wish programs would use the job control Xmechanisms more cleanly. X.PP XTo avoid a race condition, X.I pty Xchews up a tiny bit more CPU time than it should every Xtime X.I program Xis stopped and then restarted. X.PP XIf X.I program Xcloses the pseudo-terminal but doesn't die, X.I pty Xwill wait for it, even though it will have no further interaction with Xit. X.PP X.I pty Xdoes not provide any way to loudly proclaim that X.I program Xdoesn't exist. XIt simply dies quietly. X.PP XBecause of BSD's ridiculous controlling terminal mechanism, Xa reconnecting X.I pty Xhas to Xpass the name of its original terminal Xto the session underneath. XSince there is no portable way to find out that name, X.I pty Xrequires that some file descriptor be open, pointing Xto the current terminal X.I (not X/dev/tty!). X(More precisely, Xthe highest-numbered file descriptor that Xis a terminal file but not /dev/tty Xmust be the real name of the original controlling terminal. XThat's one good use for fd 3. XIf that file descriptor is some other terminal, Xthe reconnect will fail miserably.) X.SH BUGS XNone known, but they're probably hiding somewhere. XIt is the author's opinion that X.I pty Xis the ``right'' way to handle pseudo-terminals; Xif programmers use X.I pty Xinstead of writing equivalent code in each program, Xthen everything becomes much more portable and bug-free. XAs different systems provide different Xpseudo-terminal mechanisms, Xthe only program that need be changed is X.I pty. X(This is called ``modularity,'' X``interface design,'' Xor ``outside-in programming.'') X.SH MACHINES X.I pty Xhas been tested thoroughly Xon several BSD 4.3-based machines Xand tested on Xseveral BSD 4.2-based machines. X.SH VERSION Xpty version 3.0, dated June 1, 1990. X.SH AUTHOR XCopyright 1990, Daniel J. Bernstein. X.SH "SEE ALSO" Xpty(4), Xtty(4) END_OF_FILE if test 12042 -ne `wc -c <'pty.1'`; then echo shar: \"'pty.1'\" unpacked with wrong size! fi # end of 'pty.1' fi if test -f 'config.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'config.h'\" else echo shar: Extracting \"'config.h'\" \(9610 characters\) sed "s/^X//" >'config.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_CONFIG_H X#define PTY_CONFIG_H X X/* It is recommended that you make local changes in the Makefile, */ X/* except for feature control, which is best done here. */ X X/* Section 1: Feature control. */ X/* Section 2: Pseudo-terminal pathnames. */ X/* Section 3: Logging. */ X/* Section 4: Protection. */ X/* Section 5: Other. */ X/* Section 6: Sanity checks. */ X X/* Each section starts with the necessary defines and finishes with */ X/* an explanation. */ X X X/* Section 1: Feature control. */ X X/*#define MUST_UTMP /* force utmp logging */ X/*#define NO_UTMP /* prohibit utmp logging */ X/*#define MUST_WTMP /* force utmp logging */ X/*#define NO_WTMP /* prohibit wtmp logging */ X/*#define MUST_SESSION /* force disconnectable sessions (what for?) */ X/*#define NO_SESSION /* prohibit disconnectable sessions */ X/*#define MUST_CHOWN /* force changing pty owner */ X/*#define NO_CHOWN /* prohibit changing pty owner */ X/*#define NO_FDPASSING /* prohibit file descriptor passing */ X/*#define NO_UNIXSOCKS /* prohibit use of UNIX-domain sockets */ X#define TTY_WINDOWS /* have winsize, WINCH, etc. in tty (4.3) */ X/*#define TTY_AUXCHARS /* have systat, e.g. with ^T, in tty */ X#define DESPERATE_ALARMS /* if NDELAY may not be enough */ X#define DONT_NDELAY /* might as well, with DESPERATE_ALARMS */ X#define SIGINTERRUPT /* if you have siginterrupt() (4.3) */ X#define USLEEP /* if you have usleep() */ X X/* These are mostly self-explanatory. If your system doesn't support */ X/* some feature, you should prohibit it by defining NO_WHATEVER. */ X X/* Any BSD 4.2 or 4.3 system should have UNIXSOCKS and FDPASSING. Look */ X/* for /sys/h/un.h to see if you have UNIX-domain sockets. Look for */ X/* msg_accrights in the man page for recvmsg() to see if you have */ X/* file desriptor passing. (Sometimes fd passing is available but buggy, */ X/* as it's a relatively new, powerful, and rarely exploited feature. In */ X/* this case you should define NO_FDPASSING.) In the current version, */ X/* NO_UNIXSOCKS implies NO_FDPASSING and NO_SESSION; see Section 6. */ X X/* For TTY_WINDOWS, look for struct winsize in /usr/include/sgtty.h. */ X/* For TTY_AUXCHARS, look for struct auxchars in the same place. */ X/* All BSD 4.3 systems should have winsize; auxchars isn't so common. */ X X/* Opening the slave side of a pty without a master should hang the */ X/* process. Normally, opening with NDELAY gets around this effect; */ X/* opening the slave side first is necessary for some security checks. */ X/* On some systems, however, NDELAY doesn't do its job; you must */ X/* define DESPERATE_ALARMS to forcibly interrupt each open(). There */ X/* shouldn't be any problem in leaving this (and DONT_NDELAY) defined */ X/* anyway. (On a few systems, you must have DONT_NDELAY for -xn.) */ X X/* Define SIGINTERRUPT if you have siginterrupt() (i.e., if you're */ X/* running BSD 4.3). Without this call, there's no correct way to do */ X/* per-process nonblocking I/O, and pty may block when it shouldn't */ X/* (namely, when it has N bytes to write to the output, the output is */ X/* a blocking pipe with M bytes of space, and N > M > 0). If you define */ X/* SIGINTERRUPT, pty will take some extra effort to never, ever, ever */ X/* block when it doesn't have to. (See OUTBUFSIZE, below.) */ X X/* Define USLEEP if you have usleep(). */ X X X/* Section 2: Pathnames. */ X X#ifndef PTYDIR X#define PTYDIR "/usr/etc/pty" X#endif X#ifndef DEVMTY X#define DEVMTY "/dev/pty " X#endif X#ifndef DEVSTY X#define DEVSTY "/dev/tty " X#endif X#ifndef PTY1 X#define PTY1 "pqrstuvwxyzabcdefghijklmno" X#endif X#ifndef PTY2 X#define PTY2 "0123456789abcdef" X#endif X#ifndef TTYNAMELEN X#define TTYNAMELEN 30 X#endif X X/* PTYDIR is where pty sessions store their information. You should */ X/* create it mode 700, owner pty, group irrelevant. You must define */ X/* PTYDIR as something even if you don't allow sessions. If you don't */ X/* set up PTYDIR at all but you do allow sessions, pty will switch */ X/* out of setuid after grabbing a pty, then set up session information */ X/* in the user's home directory. This isn't totally secure but it's */ X/* at least partially workable. */ X X/* DEVMTY and DEVSTY are the basenames of the master and slave ptys */ X/* respectively. They must each end with two spaces, filled in with */ X/* characters from PTY1 and PTY2 respectively. If your ptys aren't */ X/* labelled by this convention, I'm surprised you have anything working; */ X/* either set up a new pty bank with normal names or change the pty code */ X/* that allocates ptys. Note that convention allows you to only have */ X/* some of a pty bank, as long as one of them is there; the first letter */ X/* in PTY2 must indicate that one. */ X X/* The order of PTY1 and PTY2 is the pty searching order. pty allows */ X/* (and, as a break from tradition, uses by default) random searches, */ X/* starting from random spots in PTY1 and PTY2 rather than the beginning. */ X/* This has several good effects, although you may think utmp will look */ X/* a bit ugly. */ X X/* If you want to gradually convert your system to using this program, */ X/* PTY1 and PTY2 shouldn't reference all your ptys. Instead, pick a */ X/* large enough subset of your ptys to be usable, and try things out */ X/* with just those. If everything works and you like pty's capabilities, */ X/* remove your old ptys, or recompile pty to reference all of them. */ X X/* TTYNAMELEN should be the maximum length of a tty (not just pty!) */ X/* name. TTYNAMELEN shouldn't have to be there; it's used to work */ X/* around some fundamental failures in the current tty/pty model. */ X X X/* Section 3: Logging. */ X X#ifndef PTYUTMP_FILE X#define PTYUTMP_FILE "/etc/utmp" X#endif X#ifndef PTYWTMP_FILE X#define PTYWTMP_FILE "/usr/adm/wtmp" X#endif X#ifndef PTYUTMP_OFFSET X#define PTYUTMP_OFFSET 5 X#endif X#ifndef PTYWTMP_OFFSET X#define PTYWTMP_OFFSET PTYUTMP_OFFSET X#endif X#ifndef PTYUTMP_HOST X#define PTYUTMP_HOST "pty" X#endif X#ifndef PTYWTMP_HOST X#define PTYWTMP_HOST PTYUTMP_HOST X#endif X#ifndef PTYUTMP_SWHOST X#define PTYUTMP_SWHOST PTYUTMP_HOST X#endif X#ifndef PTYWTMP_SWHOST X#define PTYWTMP_SWHOST "pty-sessuser" X#endif X X/* These are straightforward. FILE is where you want the logs. utmp and */ X/* wtmp are about as sensible as /etc/passwd and unfortunately just as */ X/* popular; typically they're in /etc/utmp and /usr/adm/wtmp, and the */ X/* utility programs in this package support that convention. (wtmp logs */ X/* all ins and outs. utmp only has one entry per line, namely the last */ X/* wtmp entry for it.) OFFSET is the number of chars from a pty extension */ X/* to remove from a log entry; usually this is 5, to make /dev/ttyxx into */ X/* ttyxx. Finally, HOST is the name that you want in the ``remote'' field */ X/* of the log; that field is rather illogical, but it's there. */ X X/* SWHOST is a variant of HOST, for what's logged upon a session uid */ X/* switch. */ X X/* If you're using pty under telnet/login/whatever, which do their own */ X/* utmp and wtmp handling, then pty will usually have -xUW, so these */ X/* defines aren't too important. Except for SWHOST, which is necessary */ X/* for sessuser's action to make sense. */ X X X/* Section 4: Protection. */ X X#ifndef USEDPTYMODE X#define USEDPTYMODE 0600 X#endif X#ifndef UNUSEDPTYMODE X#define UNUSEDPTYMODE 0600 X#endif X#ifndef PTYOWNER X#define PTYOWNER euid X#endif X#ifndef PTYGROUP X#define PTYGROUP 4 X#endif X X/* pty -xc will change the owner of the pty back to PTYOWNER after the */ X/* child has exited. This should be euid, for the effective uid of the */ X/* pty process. If a user runs pty as himself, he won't get to change */ X/* the owner of the pseudo-terminal in the first place. */ X X/* PTYGROUP should be the ``tty'' group in /etc/group, normally 4. All */ X/* ptys and ttys should be in this group at all times. */ X X/* USEDPTYMODE is the mode of ptys while they're being used. 600 is a */ X/* good choice; it means messages off. If you want msgs y by default, */ X/* try 620. Don't use the old 622: it's exceedingly insecure. */ X X/* UNUSEDPTYMODE is the mode of ptys while they're unused. Traditionally */ X/* this would be 666, so that any process needing a pty can just grab */ X/* one. However, this leads to quite a few security holes. If you have */ X/* lots of programs like emacs that really, really, really want to use */ X/* ptys and don't support this interface, try 0660, and make those */ X/* programs setgid tty. Otherwise, stick to 0600. */ X X X/* Section 5: Other. */ X X#ifndef SIGRET_TYPE X#define SIGRET_TYPE int X#endif X X#ifndef OUTBUFSIZE X#define OUTBUFSIZE 16384 X#endif X X#ifndef GENERIC X#define GENERIC char X#endif X X#ifdef TTY_AUXCHARS X#define USESTAT X#endif X X/* SIGRET_TYPE is the type returned by signal handlers. Logically, it */ X/* should be void, but C and signals were around before void was. */ X X/* OUTBUFSIZE is the size of the extra buffers provided by pty, both */ X/* for data coming in to the pseudo-terminal and for data going out. */ X X/* It should be possible to cast any pointer to a GENERIC * pointer */ X/* and back. Again, void makes the most sense and is the best choice */ X/* under ANSI, but char is correct and much more portable. */ X X/* Setting USESTAT is necessary for systems supporting auxchars. */ X X X/* Section 6: Sanity checks. */ X X#ifdef NO_UNIXSOCKS X#define NO_FDPASSING X#define NO_SESSION X#endif X X#ifdef NO_SESSION X#ifdef MUST_SESSION X#undef MUST_SESSION X#endif X#endif X X#ifdef NO_UTMP X#ifdef MUST_UTMP X#undef MUST_UTMP X#endif X#endif X X#ifdef NO_WTMP X#ifdef MUST_WTMP X#undef MUST_WTMP X#endif X#endif X X#ifdef NO_CHOWN X#ifdef MUST_CHOWN X#undef MUST_CHOWN X#endif X#endif X X#endif END_OF_FILE if test 9610 -ne `wc -c <'config.h'`; then echo shar: \"'config.h'\" unpacked with wrong size! fi # end of 'config.h' fi if test -f 'pty.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pty.h'\" else echo shar: Extracting \"'pty.h'\" \(1721 characters\) sed "s/^X//" >'pty.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_H X#define PTY_H X Xextern int uid; Xextern int euid; Xextern int pid; Xextern int pgrp; Xextern long date; Xextern char *username; X Xextern int fdin; /* input */ Xextern int fdout; /* output */ Xextern int fdmty; /* master side of pty */ Xextern int fdsty; /* slave side of pty */ Xextern int fdtty; /* current tty, if connected to one, or -1 */ Xextern int fdre; /* for reconnecting and fd passing, or -1 */ Xextern int fdpass; /* for passing master side up to controller */ X Xextern int flagquiet; /* -q, don't make any noises at all */ Xextern int flagdetached; /* -d, we are detached to start */ Xextern int flagjobctrl; /* -j, we can stop/start */ Xextern int flagttymodes; /* -t, we change modes of original tty */ Xextern int flagsameerr; /* -e (3), we leave fds 2 (3) and up alone */ Xextern int flagfdpass; /* -fn, we pass master side up fd n */ Xextern int flagsession; /* -s, we can disconnect & reconnect */ Xextern int flagverbose; /* -v, complain about everything */ X Xextern int flagxchown; /* -xc, change owner of pty */ Xextern int flagxutmp; /* -xu, add entry to utmp */ Xextern int flagxwtmp; /* -xw, add entry to wtmp */ Xextern int flagxexcl; /* -xx, set exclusive use */ Xextern int flagxerrwo; /* -xe, make stderr write-only */ Xextern int flagxchkopen; /* -xn, check if anyone has pty open */ Xextern int flagxskipopen; /* -xo, skip if anyone has pty open */ Xextern int flagxrandom; /* -xr, search through ptys randomly */ Xextern int flagxsetuid; /* -xs, we're running setuid */ X X#include "tty.h" X Xextern struct ttymodes tmotty; Xextern struct ttymodes tmochartty; Xextern struct ttymodes tmopty; X X#define copy(dst,src,num) bcopy(src,dst,num) X X#endif END_OF_FILE if test 1721 -ne `wc -c <'pty.h'`; then echo shar: \"'pty.h'\" unpacked with wrong size! fi # end of 'pty.h' fi if test -f 'sigler.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sigler.h'\" else echo shar: Extracting \"'sigler.h'\" \(138 characters\) sed "s/^X//" >'sigler.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_SIGLER_H X#define PTY_SIGLER_H X Xextern void sigler(); X X#endif END_OF_FILE if test 138 -ne `wc -c <'sigler.h'`; then echo shar: \"'sigler.h'\" unpacked with wrong size! fi # end of 'sigler.h' fi if test -f 'master.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'master.h'\" else echo shar: Extracting \"'master.h'\" \(138 characters\) sed "s/^X//" >'master.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_MASTER_H X#define PTY_MASTER_H X Xextern void master(); X X#endif END_OF_FILE if test 138 -ne `wc -c <'master.h'`; then echo shar: \"'master.h'\" unpacked with wrong size! fi # end of 'master.h' fi if test -f 'slave.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'slave.h'\" else echo shar: Extracting \"'slave.h'\" \(135 characters\) sed "s/^X//" >'slave.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_SLAVE_H X#define PTY_SLAVE_H X Xextern void slave(); X X#endif END_OF_FILE if test 135 -ne `wc -c <'slave.h'`; then echo shar: \"'slave.h'\" unpacked with wrong size! fi # end of 'slave.h' fi if test -f 'err.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'err.h'\" else echo shar: Extracting \"'err.h'\" \(385 characters\) sed "s/^X//" >'err.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_ERR_H X#define PTY_ERR_H X X#include <errno.h> Xextern int errno; /* not always defined in errno.h, grrrr */ Xextern int sys_nerr; Xextern char *sys_errlist[]; X Xextern void fatal(); Xextern void fatalinfo(); Xextern void fatalerr(); Xextern void fatalerr2p(); Xextern void fatalerrp(); Xextern void warnerr2(); X X#endif END_OF_FILE if test 385 -ne `wc -c <'err.h'`; then echo shar: \"'err.h'\" unpacked with wrong size! fi # end of 'err.h' fi if test -f 'tty.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tty.h'\" else echo shar: Extracting \"'tty.h'\" \(1104 characters\) sed "s/^X//" >'tty.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_TTY_H X#define PTY_TTY_H X X#include "config.h" X#include <sgtty.h> X Xstruct ttymodes X { X int di; X struct sgttyb sg; X struct tchars tc; X long lb; X struct ltchars lt; X#ifdef TTY_WINDOWS X struct winsize ws; X#endif X#ifdef TTY_AUXCHARS X struct auxchars au; X#endif X } X; X Xextern int tty_getctrl(); Xextern tty_setexcl(/* int fd */); Xextern tty_setpgrp(/* int fd; int pg; */); Xextern tty_dissoc(/* int fd */); Xextern tty_getmodes(/* int fd; s ttymodes *tmo */); Xextern tty_setmodes(/* int fd; s ttymodes *tmo */); Xextern tty_modifymodes(/* int fd; s ttymodes *tmonew; s ttymodes *tmoold */); X X/* The following don't do any ioctls; they just mangle internal ttymodes. */ X Xextern void tty_copymodes(/* s ttymodes *tmonew; s ttymodes *tmoold */); Xextern void tty_copywin(/* s ttymodes *tmonew; s ttymodes *tmoold */); Xextern void tty_charmode(/* s ttymodes *tmo */); Xextern void tty_mungemodes(/* s ttymodes *tmo;cbreak;new;echo;crmod;raw;crt */); Xextern void tty_initmodes(/* s ttymodes *tmo;cbreak;new;echo;crmod;raw;crt */); X X#endif END_OF_FILE if test 1104 -ne `wc -c <'tty.h'`; then echo shar: \"'tty.h'\" unpacked with wrong size! fi # end of 'tty.h' fi if test -f 'getopt.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'getopt.h'\" else echo shar: Extracting \"'getopt.h'\" \(250 characters\) sed "s/^X//" >'getopt.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_GETOPT_H X#define PTY_GETOPT_H X X/* The following should be in /usr/include/getopt.h but often aren't. */ Xextern int getopt(); Xextern char *optarg; Xextern int optind; X X#endif END_OF_FILE if test 250 -ne `wc -c <'getopt.h'`; then echo shar: \"'getopt.h'\" unpacked with wrong size! fi # end of 'getopt.h' fi if test -f 'texts.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'texts.h'\" else echo shar: Extracting \"'texts.h'\" \(273 characters\) sed "s/^X//" >'texts.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_TEXTS_H X#define PTY_TEXTS_H X Xextern char *ptyauthor[]; Xextern char *ptyversion[]; Xextern char *ptycopyright[]; Xextern char *ptywarranty[]; Xextern char *ptyusage[]; Xextern char *ptyhelp[]; X X#endif END_OF_FILE if test 273 -ne `wc -c <'texts.h'`; then echo shar: \"'texts.h'\" unpacked with wrong size! fi # end of 'texts.h' fi if test -f 'sig.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sig.h'\" else echo shar: Extracting \"'sig.h'\" \(565 characters\) sed "s/^X//" >'sig.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_SIG_H X#define PTY_SIG_H X X#include "config.h" X#include <signal.h> X Xtypedef void (*sig_handler)(); Xtypedef SIGRET_TYPE (*sig_syshandler)(); X Xextern void nothing(); X X#define SIGNUM 32 Xtypedef int sig_num; X Xextern void sig_init(); Xextern void sig_restore(); X Xextern void sig_ignore(); Xextern void sig_default(); Xextern void sig_handle(); X X#ifdef SIGINTERRUPT Xextern void sig_interrupt(); X#endif X Xextern void sig_sethandler(); X Xextern void sig_startring(); Xextern void sig_stopring(); X X#endif END_OF_FILE if test 565 -ne `wc -c <'sig.h'`; then echo shar: \"'sig.h'\" unpacked with wrong size! fi # end of 'sig.h' fi if test -f 'logs.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'logs.h'\" else echo shar: Extracting \"'logs.h'\" \(169 characters\) sed "s/^X//" >'logs.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_LOGS_H X#define PTY_LOGS_H X Xextern long now(); Xextern int utmp(); Xextern int wtmp(); X X#endif END_OF_FILE if test 169 -ne `wc -c <'logs.h'`; then echo shar: \"'logs.h'\" unpacked with wrong size! fi # end of 'logs.h' fi if test -f 'file.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'file.h'\" else echo shar: Extracting \"'file.h'\" \(224 characters\) sed "s/^X//" >'file.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_FILE_H X#define PTY_FILE_H X X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <fcntl.h> Xextern long lseek(); /* sigh. */ X X#endif END_OF_FILE if test 224 -ne `wc -c <'file.h'`; then echo shar: \"'file.h'\" unpacked with wrong size! fi # end of 'file.h' fi if test -f 'file.h.old' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'file.h.old'\" else echo shar: Extracting \"'file.h.old'\" \(749 characters\) sed "s/^X//" >'file.h.old' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_FILE_H X#define PTY_FILE_H X X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <fcntl.h> Xextern long lseek(); /* sigh. */ X X#define NFDBITS (sizeof(fd_mask) * NBBY) X#define FD_SET(n,p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) X#define FD_ISSET(n,p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) X#define FD_ZERO(p) bzero((caddr_t)(p),sizeof(*(p))) X X/* The following are desperation versions. Ignore pointer warnings. X#undef NFDBITS X#undef FD_SET X#undef FD_ISSET X#undef FD_ZERO X#undef fd_set X#define fd_set long X#define FD_SET(n,p) ((*p) |= (1 << (n))) X#define FD_ISSET(n,p) ((*p) & (1 << (n))) X#define FD_ZERO(n,p) (*p = 0L) X*/ X X#endif END_OF_FILE if test 749 -ne `wc -c <'file.h.old'`; then echo shar: \"'file.h.old'\" unpacked with wrong size! fi # end of 'file.h.old' fi if test -f 'file.h.new' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'file.h.new'\" else echo shar: Extracting \"'file.h.new'\" \(224 characters\) sed "s/^X//" >'file.h.new' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_FILE_H X#define PTY_FILE_H X X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <fcntl.h> Xextern long lseek(); /* sigh. */ X X#endif END_OF_FILE if test 224 -ne `wc -c <'file.h.new'`; then echo shar: \"'file.h.new'\" unpacked with wrong size! fi # end of 'file.h.new' fi if test -f 'sock.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sock.h'\" else echo shar: Extracting \"'sock.h'\" \(579 characters\) sed "s/^X//" >'sock.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_SOCK_H X#define PTY_SOCK_H X Xextern int pty_readsock(); Xextern int pty_writesock(); Xextern int pty_acceptsock(); X X/* -1 fail, 0 success */ Xextern int pty_putgetstr(); Xextern int pty_putgetfd(); Xextern int pty_putgetonefd(); Xextern int pty_putgetint(); Xextern int pty_putgettty(); Xextern int pty_getch(); X X/* -1 fail, 0 success, 1 refused */ Xextern int pty_sendstr(); Xextern int pty_sendfd(); Xextern int pty_sendonefd(); Xextern int pty_sendint(); Xextern int pty_sendtty(); Xextern int pty_putch(); X X#endif END_OF_FILE if test 579 -ne `wc -c <'sock.h'`; then echo shar: \"'sock.h'\" unpacked with wrong size! fi # end of 'sock.h' fi if test -f 'misc.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'misc.h'\" else echo shar: Extracting \"'misc.h'\" \(190 characters\) sed "s/^X//" >'misc.h' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#ifndef PTY_MISC_H X#define PTY_MISC_H X Xextern int sessdir(); Xextern char *real_ttyname(); Xextern void setusername(); X X#endif END_OF_FILE if test 190 -ne `wc -c <'misc.h'`; then echo shar: \"'misc.h'\" unpacked with wrong size! fi # end of 'misc.h' fi if test -f 'pty.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pty.c'\" else echo shar: Extracting \"'pty.c'\" \(10547 characters\) sed "s/^X//" >'pty.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X/* Xpty.c: run a program under a pty session X*/ X X#include <stdio.h> Xextern unsigned short getuid(); /* grrrr */ Xextern unsigned short geteuid(); /* grrrr */ X#include "config.h" X#include "getopt.h" X#include "err.h" X#include "pty.h" X#include "tty.h" X#include "texts.h" X#include "sig.h" X#include "sigler.h" X#include "master.h" X#include "slave.h" X#include "file.h" X#include "logs.h" X#include "misc.h" X Xint flagpcbreak = 0; /* -pc, character-at-a-time */ Xint flagpnew = 1; /* -pd, new line discipline---traditionally off to start */ Xint flagpecho = 1; /* -pe, echo characters */ Xint flagpcrmod = 1; /* -pn, munge carriage returns */ Xint flagpraw = 0; /* -pr, raw mode */ Xint flagpcrt = 1; /* -ps, screen */ X Xgetfreepty(fnmty,fnsty,pty1,pty2) Xregister char fnmty[sizeof(DEVMTY)]; Xregister char fnsty[sizeof(DEVSTY)]; Xregister char pty1[sizeof(PTY1)]; Xregister char pty2[sizeof(PTY2)]; X{ X register char *c1; X register char *c2; X register char *c1start; /* for ``random'' pty searching */ X register char *c2start; X int e; X X if (flagxrandom) X { X c1start = pty1 + (pid % (sizeof(PTY1) - 1)); X c2start = pty2 + ((pid + date) % (sizeof(PTY2) - 1)); X } X else X { X c1start = pty1; X c2start = pty2; X } X X c1 = c1start; X do X { X fnmty[sizeof(DEVMTY) - 3] = *c1; X fnmty[sizeof(DEVMTY) - 2] = pty2[0]; X if (!access(fnmty,F_OK)) X { X c2 = c2start; X fnsty[sizeof(DEVSTY) - 3] = *c1; X fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2; X do X { X#ifdef DESPERATE_ALARMS X sig_startring(); X#endif X X/* Some other process could come along and mess up our test by opening */ X/* the master side before we do. But in that case they'll get the pty */ X/* anyway, and we'll move on to another possibility without comment. */ X if (flagxchkopen) X { X#ifdef DONT_NDELAY X fdsty = open(fnsty,O_RDWR); X#else X fdsty = open(fnsty,O_RDWR | O_NDELAY); X#endif X e = errno; X fdmty = open(fnmty,O_RDWR); X } X else X { X fdmty = open(fnmty,O_RDWR); X fdsty = open(fnsty,O_RDWR); X e = errno; X } X X#ifdef DESPERATE_ALARMS X sig_stopring(); X#endif X X if (fdmty != -1) X { X if (flagxskipopen && (fdsty != -1)) X warnerr2("pty: warning: slave %s still in use\n",fnsty); X else X { X if ((fdsty == -1) && (e != EINTR) && (e != EWOULDBLOCK)) X fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e); X if (flagxchkopen) X if (fdsty == -1) X { X fdsty = open(fnsty,O_RDWR); X e = errno; X } X else X warnerr2("pty: warning: slave %s still in use\n",fnsty); X if (fdsty == -1) X fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e); X else X { X if (flagxchkopen) X if (fcntl(fdsty,F_SETFL,0) == -1) X fatalerrp(6,"pty: fatal: can't fcntl pty",e); X return 0; X } X } X } X X if (fdmty != -1) (void) close(fdmty); X if (fdsty != -1) (void) close(fdsty); X if (!(*(++c2))) X c2 = pty2; X fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2; X } X while (c2 != c2start); X } X if (!(*(++c1))) X c1 = pty1; X } X while (c1 != c1start); X return -1; X} X Xchar fnmty[sizeof(DEVMTY)] = DEVMTY; Xchar fnsty[sizeof(DEVSTY)] = DEVSTY; Xchar pty1[sizeof(PTY1)] = PTY1; Xchar pty2[sizeof(PTY2)] = PTY2; X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X int opt; X int f; X X uid = getuid(); X euid = geteuid(); X pid = getpid(); X pgrp = getpgrp(0); X date = now(); X setusername(); X X while ((opt = getopt(argc,argv,"qQvdDe3Ef:FjJsStTp:x:0ACHUVW")) != EOF) X switch(opt) X { X case 'A': fatalinfo(1,ptyauthor); X case 'C': fatalinfo(1,ptycopyright); X case 'H': fatalinfo(1,ptyhelp); X case 'U': fatalinfo(1,ptyusage); X case 'V': fatalinfo(1,ptyversion); X case 'W': fatalinfo(1,ptywarranty); X case '?': fatalinfo(1,ptyusage); X case 'q': flagquiet = 1; break; X case 'Q': flagquiet = 0; flagverbose = 0; break; X case 'v': flagverbose = 1; break; X case 'd': flagdetached = 1; flagjobctrl = 0; flagttymodes = 0; break; X case 'D': flagdetached = 0; flagjobctrl = 1; flagttymodes = 1; break; X case 'e': flagsameerr = 2; break; X case '3': flagsameerr = 1; break; X case 'E': flagsameerr = 0; break; X case 'f': flagfdpass = 1; X if (sscanf(optarg,"%d",&fdpass) < 1) fatalinfo(1,ptyusage); X break; X case 'F': flagfdpass = 0; break; X case 'j': flagjobctrl = 1; break; X case 'J': flagjobctrl = 0; break; X case 's': flagsession = 1; flagxutmp = 1; break; X case 'S': flagsession = 0; flagxutmp = 0; break; X case 't': flagttymodes = 1; break; X case 'T': flagttymodes = 0; break; X case '0': flagsameerr = 2; flagsession = 0; flagttymodes = 0; X flagxutmp = 0; flagxwtmp = 0; X flagpcbreak = 3; flagpraw = 3; flagpecho = 2; flagpnew = 2; X break; X case 'p': while (opt = *(optarg++)) X switch(opt) X { X case 'c': flagpcbreak = 3; break; X case 'C': flagpcbreak = 2; break; X case 'd': flagpnew = 3; break; X case 'D': flagpnew = 2; break; X case 'e': flagpecho = 3; break; X case 'E': flagpecho = 2; break; X case 'n': flagpcrmod = 3; break; X case 'N': flagpcrmod = 2; break; X case 'r': flagpraw = 3; break; X case 'R': flagpraw = 2; break; X case 's': flagpcrt = 3; break; X case 'S': flagpcrt = 2; break; X case '0': flagpcbreak = 3; flagpraw = 3; X flagpecho = 2; flagpnew = 2; X break; X default: fatalinfo(1,ptyusage); break; X } X break; X case 'x': while (opt = *(optarg++)) X switch(opt) X { X case 'c': flagxchown = 1; break; X case 'C': flagxchown = 0; break; X case 'u': flagxutmp = 1; break; X case 'U': flagxutmp = 0; break; X case 'w': flagxwtmp = 1; break; X case 'W': flagxwtmp = 0; break; X case 'x': flagxexcl = 1; break; X case 'X': flagxexcl = 0; break; X case 'e': flagxerrwo = 1; break; X case 'E': flagxerrwo = 0; break; X case 'n': flagxchkopen = 1; break; X case 'N': flagxchkopen = 0; break; X case 'o': flagxskipopen = 1; break; X case 'O': flagxskipopen = 0; break; X case 'r': flagxrandom = 1; break; X case 'R': flagxrandom = 0; break; X case 's': flagxsetuid = 1; break; X case 'S': flagxsetuid = 0; break; X default: fatalinfo(1,ptyusage); break; X } X break; X } X argv += optind, argc -= optind; X X if (!*argv) X fatalinfo(1,ptyusage); X X /* Option forcing. */ X#ifdef NO_UTMP X if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced off\n"); X flagxutmp = 0; X#endif X#ifdef NO_WTMP X if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced off\n"); X flagxwtmp = 0; X#endif X#ifdef NO_CHOWN X if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced off\n"); X flagxchown = 0; X#endif X#ifdef NO_SESSION X if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced off\n"); X flagsession = 0; X#endif X#ifdef MUST_UTMP X if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced on\n"); X flagxutmp = 1; X#endif X#ifdef MUST_WTMP X if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced on\n"); X flagxwtmp = 1; X#endif X#ifdef MUST_CHOWN X if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced on\n"); X flagxchown = 1; X#endif X#ifdef MUST_SESSION X if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced on\n"); X flagsession = 1; X#endif X#ifdef NO_FDPASSING X if (flagfdpass) if (flagverbose) warnerr2("%s","pty: fd passing forced off\n"); X flagfdpass = 0; X#endif X X /* Option munging. */ X if (flagsession) flagsameerr = 0; X if (flagdetached) flagttymodes = 0; X if (flagxskipopen) flagxchkopen = 1; X X if (!flagxsetuid) X { X (void) setreuid(uid,uid); X euid = uid; X } X X X sig_init(); X sig_sethandler(SIGALRM,nothing); sig_handle(SIGALRM); X sig_default(SIGTTIN); X sig_default(SIGTTOU); X X if (fdpass == -1) /* wow, was this a source of bugs. */ X { X fdin = 0; X fdout = 1; X } X X if (flagdetached) X { X tty_initmodes(&tmopty,flagpcbreak,flagpnew,flagpecho, X flagpcrmod,flagpraw,flagpcrt); X } X else X { X if ((fdtty = tty_getctrl()) == -1) X fatalerr(2,"pty: fatal: cannot find control terminal; try -d?\n"); X if (tty_getmodes(fdtty,&tmotty) == -1) X fatalerr(3,"pty: fatal: cannot get current tty modes\n"); X /* XXX: is there a way to recover more gracefully? */ X tty_copymodes(&tmopty,&tmotty); X tty_mungemodes(&tmopty,flagpcbreak,flagpnew,flagpecho, X flagpcrmod,flagpraw,flagpcrt); X tty_copymodes(&tmochartty,&tmotty); X if (flagttymodes) X tty_charmode(&tmochartty); X } X X /* XXX: Here would be a good spot to include pty limits, say through */ X /* the file PTYDIR/LIMITS. Lines of the form user group num, saying */ X /* that user in that group is limited to num ptys, with * for all. */ X /* All pty use would have to be logged somewhere. Anyway, with a */ X /* streams-based pty, there wouldn't be much point to limits. */ X X if (getfreepty(fnmty,fnsty,pty1,pty2) == -1) X fatalerr(5,"pty: fatal: no ptys available\n"); X X if (flagverbose) X warnerr2("pty: successfully opened pty %s\n",fnsty); X X if (tty_modifymodes(fdtty,&tmochartty,&tmotty) == -1) X { X (void) tty_setmodes(fdtty,&tmotty); /* XXX --- gasp */ X fatalerr(4,"pty: fatal: cannot set modes of original tty\n"); X } X X/* In general, BSD systems check MAXUPRC against the effective uid, */ X/* rather than the real uid; and they check it during a fork(). */ X/* The combination of these annoying behaviors means that we have */ X/* to switch uids while forking, hence possibly losing any security */ X/* measures we may have set up before the fork(). Grrrr. */ X X (void) setreuid(euid,uid); X if ((f = fork()) == -1) X { X (void) tty_modifymodes(fdtty,&tmotty,&tmochartty); X fatalerr(7,"pty: fatal: cannot fork once\n"); X /* After this, the signaller will handle tty modes. */ X } X else if (f == 0) X if ((f = fork()) == -1) X { X (void) kill(pid,SIGTERM); /*XXX*/ X fatalerr(7,"pty: fatal: cannot fork twice\n"); X } X else if (f == 0) X { X (void) setreuid(uid,euid); X slave(fnsty,argv); X } X else X { X (void) setreuid(uid,euid); X if (flagsession) X if (sessdir() == -1) X fatal(1); X master(fnsty,f); X } X else X { X (void) setreuid(uid,euid); X if (flagsession) X if (sessdir() == -1) X { X fatalerr(8,"pty: fatal: cannot change to session directory\n"); X (void) tty_modifymodes(fdtty,&tmotty,&tmochartty); X } X sigler(fnsty,f); X } X X fatal(9); /* just in case */ X /*NOTREACHED*/ X} END_OF_FILE if test 10547 -ne `wc -c <'pty.c'`; then echo shar: \"'pty.c'\" unpacked with wrong size! fi # end of 'pty.c' fi if test -f 'sigler.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sigler.c'\" else echo shar: Extracting \"'sigler.c'\" \(8107 characters\) sed "s/^X//" >'sigler.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include <sys/types.h> X#include <sys/time.h> X#include <stdio.h> X#include <strings.h> X#include "pty.h" X#include "sigler.h" X#include "sig.h" X#include "file.h" X#include "sock.h" X#include "err.h" X#include "misc.h" X X/* General XXX: In failure cases, sig.* isn't always removed. */ X Xstatic char *glfnsty; X Xstatic int masterpid; X Xstatic int flagmaster = 1; Xstatic int flagcont = 0; Xstatic int pi[2]; X Xstatic void deatherrp(i,s) Xint i; Xchar *s; X{ X int e = errno; X X (void) kill(masterpid,SIGTERM); X /* XXX: should wait while flagmaster */ X (void) tty_modifymodes(fdtty,&tmotty,&tmochartty); X fatalerrp(i,s,e); X} X Xstatic void finish(i) Xsig_num i; X{ X if (i == SIGTERM) X { X flagmaster = 0; X (void) write(pi[1],".",1); X } X else X (void) write(pi[1],",",1); X} X X/*ARGSUSED*/ Xstatic void sig_usr2(i) Xsig_num i; X{ X if (flagsession) X { X char fnsess[20]; X int fdsess; X int newuid = uid; X char newsuid[10]; X X /* XXX: We should have some error recovery here! */ X X /* We can make quite a few assumptions, because we only get USR2 */ X /* forwarded from the master. */ X X (void) sprintf(fnsess,"sess.%s",glfnsty + sizeof(DEVSTY) - 3); X if ((fdsess = open(fnsess,O_RDONLY,0600)) != -1) X { X (void) read(fdsess,(char *) &newuid,sizeof(int)); X (void) sprintf(newsuid,"%d",newuid); X X (void) chdir(".."); X (void) chdir(newsuid); X X uid = newuid; X (void) setreuid(uid,euid); X (void) close(fdsess); X } X } X} X X/*ARGSUSED*/ Xstatic void sig_cont(i) Xsig_num i; X{ X flagcont = 1; X (void) write(pi[1]," ",1); X} X Xvoid sigler(fnsty,master) Xchar *fnsty; Xint master; X{ X char ch; X char path[100]; X int fd = -1; X char *ttyn; X char fntty[TTYNAMELEN]; X X glfnsty = fnsty; X masterpid = master; X /* pid = getpid() is already true */ X (void) sprintf(path,"sig.%s",fnsty + sizeof(DEVSTY) - 3); X (void) unlink(path); X X (void) close(fdmty); X (void) close(fdsty); X if (fdre != -1) (void) close(fdre); X X if (pipe(pi) == -1) /* clumsy, but stops several races */ X /* This is absolutely impossible. fdmty and fdsty must have been open */ X /* before this, and we just closed them, so there must be two fds */ X /* available for the pipe. */ X deatherrp(10,"pty: fatal: cannot open internal pipe"); X (void) fcntl(pi[1],F_SETFL,FNDELAY); X X sig_ignore(SIGCHLD); X sig_ignore(SIGXCPU); X sig_ignore(SIGXFSZ); X sig_ignore(SIGPROF); X sig_ignore(SIGVTALRM); X X sig_default(SIGEMT); /* XXX: really dump? */ X sig_default(SIGIOT); X sig_default(SIGILL); X sig_default(SIGSEGV); X X sig_default(SIGTTOU); X sig_default(SIGTTIN); X sig_default(SIGTSTP); X sig_default(SIGSTOP); X X sig_sethandler(SIGTERM,finish); sig_handle(SIGTERM); X sig_sethandler(SIGINT,finish); sig_handle(SIGINT); X sig_sethandler(SIGQUIT,finish); sig_handle(SIGQUIT); X sig_sethandler(SIGHUP,finish); sig_handle(SIGHUP); X sig_sethandler(SIGUSR1,finish); sig_handle(SIGUSR1); /* disconnect */ X X sig_sethandler(SIGCONT,sig_cont); sig_handle(SIGCONT); X X sig_sethandler(SIGUSR2,sig_usr2); sig_handle(SIGUSR2); X X for (;;) X { X if (flagcont) X { X flagcont = 0; X if (tty_modifymodes(fdtty,&tmochartty,&tmotty) == -1) X ; /* XXX: impossible, but what if it happens? */ X (void) kill(masterpid,SIGUSR1); /* not CONT---see master.c's sig_cont() */ X } X#ifdef NO_FDPASSING X if (flagmaster == 2) X { X static fd_set rfds; X static fd_set wfds; X static int r; X static int w; X static char foobuf[OUTBUFSIZE]; X int fdnum; X static char *s; X X fdnum = fd; if (fdin > fdnum) fdnum = fdin; X if (pi[0] > fdnum) fdnum = pi[0]; fdnum++; X X FD_ZERO(&rfds); X FD_ZERO(&wfds); X FD_SET(fd,&rfds); X FD_SET(fdin,&rfds); X FD_SET(pi[0],&rfds); X X r = select(fdnum,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0); X if (r > 0) X { X if (FD_ISSET(fd,&rfds)) X { X if ((r = read(fd,foobuf,OUTBUFSIZE)) == -1) X deatherrp(11,"pty: fatal: socket read error"); X s = foobuf; X /* XXX: r can't be zero, but what if it is? */ X while (r) X { X if ((w = write(fdout,s,r)) == -1) X deatherrp(14,"pty: fatal: output write error"); X r -= w; s += w; X } X } X if (FD_ISSET(fdin,&rfds)) X { X if ((r = read(fdin,foobuf,OUTBUFSIZE)) == -1) X deatherrp(13,"pty: fatal: input read error"); X s = foobuf; X /* XXX: What if r is zero? Can't pass EOF, grrrr */ X while (r) X { X if ((w = write(fd,s,r)) == -1) X deatherrp(12,"pty: fatal: socket write error"); X r -= w; s += w; X } X } X if (FD_ISSET(pi[0],&rfds)) X (void) read(pi[0],&ch,1); X } X } X else X (void) read(pi[0],&ch,1); X#else X (void) read(pi[0],&ch,1); X#endif X if ((ch == '.') || (ch == ',')) X { X if (fd != -1) X (void) close(fd); X if (flagmaster) X { X if (kill(masterpid,SIGTERM) == -1) X flagmaster = 0; /* XXX */ X X if (fdpass != -1) X (void) write(fdpass,".",1); X X while (flagmaster) X ; X while (ch != '.') X (void) read(pi[0],&ch,1); X } X X /* We don't test at this point whether the killing signal was a HUP. */ X /* This means that hanging up on a reconnecting sigler won't stop */ X /* the reconnect; instead, the new session will be instantly hung */ X /* up. The USR1 used for a manual disconnect could be HUP for this */ X /* reason. */ X if (flagsession X &&((fd = open(path,O_RDONLY)) != -1) X &&(read(fd,fnsty,sizeof(DEVSTY) - 1) > 0)) X { X warnerr2("pty: reconnecting to %s\r\n",fnsty); X (void) close(fd); X (void) unlink(path); X if ((fd = pty_writesock(fnsty)) == -1) X warnerr2("pty: reconnect failed: cannot talk to %s\r\n",fnsty); X else X { X if ((pty_getch(fd,&ch) != -1) && (ch == 'p')) X if (pty_putgetint(fd,'p',&masterpid) != -1) X do X { X#ifdef NO_FDPASSING X if (fdtty != -1) X { X if (!(ttyn = real_ttyname(fdtty))) break; X /* XXX: Should we NXCL here? */ X (void) strncpy(fntty,ttyn,TTYNAMELEN - 1); X if (pty_sendstr(fd,'s',fntty) == -1) break; X if (flagverbose) X warnerr2("pty: sent parent tty %s\r\n",fntty); X } X#else X if (fdtty != -1) X { X if (!(ttyn = real_ttyname(fdtty))) break; X /* XXX: Should we NXCL here? */ X (void) strncpy(fntty,ttyn,TTYNAMELEN - 1); X if (pty_sendstr(fd,'s',fntty) == -1) break; X if (flagverbose) X warnerr2("pty: sent parent tty %s\r\n",fntty); X /* We shouldn't have to send the parent tty name, */ X /* but passing the fd alone doesn't set the control */ X /* terminal of the receiver (grrrr), and a detached */ X /* process effectively has no pgrp. Aargh. */ X } X X if (fdpass == -1) X { X if (pty_sendfd(fd,'0',&fdin) == -1) break; X if (pty_sendfd(fd,'1',&fdout) == -1) break; X } X else X if (pty_sendfd(fd,'f',&fdpass) == -1) break; X if (fdtty != -1) X if (pty_sendfd(fd,'t',&fdtty) == -1) break; X#endif X if (pty_sendint(fd,'p',&pid) == -1) break; X if (pty_sendint(fd,'g',&pgrp) == -1) break; X if (pty_sendint(fd,'j',&flagjobctrl) == -1) break; X if (fdtty != -1) X { X if (pty_sendtty(fd,'c',&tmochartty) == -1) break; X if (pty_sendtty(fd,'n',&tmotty) == -1) break; X } X if (pty_putch(fd," ") == -1) break; X#ifdef NO_FDPASSING X flagmaster = 2; /* Success, but pain coming up. */ X#else X flagmaster = 1; /* Successfully reconnected! */ X#endif X } X while(0); X if (flagmaster < 2) X { X (void) close(fd); X fd = -1; X } X } X if (flagmaster) X { X /* remake path for new pty */ X (void) sprintf(path,"sig.%s",fnsty + sizeof(DEVSTY) - 3); X (void) unlink(path); X warnerr2("pty: successfully reconnected to %s\r\n",fnsty); X } X else X warnerr2("pty: reconnect to %s failed\r\n",fnsty); X } X if (!flagmaster) X { X if (tty_modifymodes(fdtty,&tmotty,&tmochartty) == -1) X warnerr2("%s","pty: can't restore tty modes\n"); /*XXX*/ X fatal(0); X /*NOTREACHED*/ X } X } X } X} END_OF_FILE if test 8107 -ne `wc -c <'sigler.c'`; then echo shar: \"'sigler.c'\" unpacked with wrong size! fi # end of 'sigler.c' fi if test -f 'master.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'master.c'\" else echo shar: Extracting \"'master.c'\" \(18086 characters\) sed "s/^X//" >'master.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include <sys/types.h> X#include <sys/time.h> X#include <sys/resource.h> X#include <sys/wait.h> X#include <stdio.h> X#include "err.h" X#include "config.h" X#include "pty.h" X#include "master.h" X#include "sig.h" X#include "tty.h" X#include "file.h" X#include "sock.h" X#include "logs.h" X#include "misc.h" X Xstatic char fnre[20]; X Xstatic char fnsess[20]; Xstatic int fdsess; X Xstatic char *glfnsty; X Xstatic char soutbuf[OUTBUFSIZE]; Xstatic char sptybuf[OUTBUFSIZE]; X Xstatic struct ttymodes tmowinpty; Xstatic struct ttymodes tmowintty; X Xstatic char *outbuf = soutbuf; Xstatic int outbufsiz = OUTBUFSIZE; Xstatic int outbuflen = 0; Xstatic char *ptybuf = sptybuf; Xstatic int ptybufsiz = OUTBUFSIZE; Xstatic int ptybuflen = 0; X Xstatic int flagconnected = 1; /* 0: disconnected. 2: idling for stop. */ X /* 3: idling for stop but child is dead. */ Xstatic int flagchild = 1; /* 0: dead. 2: stopped. */ Xstatic int childsig; /* signal that stopped/killed child */ Xstatic int flagsigler = 1; /* 0: dead. */ Xstatic int siglerpid; /* only defined if flagconnected */ Xstatic int slavepid; X Xstatic int flagqwinch = 0; X Xstatic void quickdeath(i) Xint i; X{ X /* All exits from master() go through here. */ X if (flagsession) (void) unlink(fnsess); X if (flagxchown) X (void) fchown(fdsty,PTYOWNER,PTYGROUP); X (void) fchmod(fdsty,UNUSEDPTYMODE); X date = now(); X if (flagxutmp) X if (utmp(glfnsty + PTYUTMP_OFFSET,"","",date) == -1) X ; /* too bad. */ X if (flagxwtmp) X if (wtmp(glfnsty + PTYWTMP_OFFSET,"","",date) == -1) X ; /* too bad. */ X fatal(i); X} X Xstatic void death(i) Xint i; X{ X (void) kill(siglerpid,SIGTERM); X /* XXX: should wait while flagsigler */ X quickdeath(i); X} X X/*ARGSUSED*/ Xstatic void sig_force(i) Xsig_num i; X{ X /* Forced death, presumably from the sesskill program. */ X sig_ignore(SIGCHLD); X /* XXX: Should we test for !flagchild here? sesskill does. */ X flagchild = 0; X quickdeath(SIGCHLD); X} X X/*ARGSUSED*/ Xstatic void sig_usr2(i) Xsig_num i; X{ X if (flagsession) X { X int newuid = uid; X char newsuid[10]; X char foo[100]; X X /* XXX: We should have some error recovery here! */ X X (void) lseek(fdsess,(long) 0,0); X (void) read(fdsess,(char *) &newuid,sizeof(int)); X (void) sprintf(newsuid,"%d",newuid); X X (void) chdir(".."); X if (chdir(newsuid) == -1) X { X (void) mkdir(newsuid,0700); X (void) chdir(newsuid); X } X X (void) sprintf(foo,"../%d/%s",uid,fnsess); X (void) rename(foo,fnsess); X X (void) sprintf(foo,"../%d/%s",uid,fnre); X (void) rename(foo,fnre); /* in case we're already disconnected */ X X uid = newuid; X (void) setreuid(uid,euid); X setusername(); X X if (flagxutmp) X if (utmp(glfnsty + PTYUTMP_OFFSET,username,PTYUTMP_SWHOST,date) == -1) X ; /* too bad. */ X if (flagxwtmp) X if (wtmp(glfnsty + PTYWTMP_OFFSET,username,PTYWTMP_SWHOST,date) == -1) X ; /* too bad. */ X if (flagsigler) X (void) kill(siglerpid,SIGUSR2); X } X} X X/*ARGSUSED*/ Xstatic void sig_pipe(i) Xsig_num i; X{ X flagsigler = 0; /* XXX: is this appropriate? race? */ X /* Will end up giving child HUP. */ X} X X/*ARGSUSED*/ Xstatic void sig_chld(i) Xsig_num i; X{ X union wait w; X X if (wait3(&w,WNOHANG | WUNTRACED,(struct rusage *) 0) <= 0) X return; /* why'd we get the CHLD? it must have stopped & restarted? */ X X if (w.w_stopval == WSTOPPED) X { X childsig = w.w_stopsig; X flagchild = 2; X } X else X { X childsig = w.w_termsig; /* can't do much with this */ X flagchild = 0; X } X} X X/*ARGSUSED*/ Xstatic void sig_term(i) Xsig_num i; X{ X flagsigler = 0; X} X X/* If we have made it to being master, we should never get TTIN or TTOU, */ X/* except possibly while restarting after a stop (e.g., if the user puts */ X/* us back into the background). But we let the signaller handle putting */ X/* the tty modes back before restarting us, so we should never, ever, */ X/* ever get a TTIN or TTOU. If the user is messing around and we do get */ X/* a TTIN or TTOU, we'll just pretend the child died and hope we get */ X/* around to telling the signaller about it. */ X X/*ARGSUSED*/ Xstatic void sig_ttin(i) Xsig_num i; X{ X if (flagchild) X { X childsig = SIGTTIN; X flagchild = 2; X } X} X X/*ARGSUSED*/ Xstatic void sig_ttou(i) Xsig_num i; X{ X if (flagchild) X { X childsig = SIGTTOU; X flagchild = 2; X } X} X X/*ARGSUSED*/ Xstatic void sig_tstp(i) Xsig_num i; X{ X if (flagchild) X { X childsig = SIGCONT; X flagchild = 2; X } X} X X/* Most job-control shells (including csh) behave absolutely miserably. */ X/* (Well, that goes without saying.) In particular, rather than sending */ X/* a CONT to every one of their children in the process group, they feel */ X/* a need to kill the entire process group. Grrrr. Because of this, we */ X/* are forced to use the nonintuitive USR1 to communicate CONT, and ignore */ X/* CONT entirely. Anyway, read cont as usr1 where necessary. */ X X/* We can only get USR1 from the signaller (or from us after reconnect). */ X/* By convention, the signaller handles setting the tty modes back to */ X/* chartty, even though we handled restoring the modes before stop. */ X X/*ARGSUSED*/ Xstatic void sig_cont(i) Xsig_num i; X{ X if (flagchild) X { X flagchild = 1; X (void) kill(slavepid,SIGCONT); X (void) kill(pid,SIGWINCH); X } X if (flagconnected == 3) X flagconnected = 1; /* XXX: should be internal to master() */ X (void) setpgrp(0,pgrp); X} X X/* If it weren't for WINCH, which must be in the master if NO_FDPASSING, */ X/* and for the stupid conventions surrounding a process's control tty, */ X/* then all mention of fdtty could disappear from master. This would */ X/* slightly complicate the signaller's T{STP,TIN,TOU} handling but make */ X/* reconnect a lot simpler. Sigh. */ X X/*ARGSUSED*/ Xstatic void sig_winch(i) Xsig_num i; X{ X int pg; X X flagqwinch = 0; X#ifdef TTY_WINDOWS X/* An unfortunate but slight race: Another handler could change the pgrp */ X/* if the child suddenly stops and we're queued for delivery. So we have */ X/* to change it back. */ X pg = getpgrp(0); X (void) setpgrp(0,pgrp); X if (!flagsigler) X flagqwinch = 1; X else X if (tty_getmodes(fdsty,&tmopty) == 0) X if (tty_getmodes(fdtty,&tmowintty) == 0) X { X tty_copymodes(&tmowinpty,&tmopty); X tty_copywin(&tmowinpty,&tmowintty); X (void) tty_modifymodes(fdsty,&tmowinpty,&tmopty); X } X (void) setpgrp(0,pg); X#endif X} X Xstatic int disconnect(fnsty) Xchar *fnsty; X{ X if (fdtty != -1) X { X (void) tty_dissoc(fdtty); /* must succeed */ X (void) close(fdtty); X fdtty = -1; X } X if (fdpass != -1) X { X /* We used to write the dot to fdpass here. It's in sigler now, to */ X /* prevent a race condition. */ X (void) close(fdpass); X fdpass = -1; X } X if (fdin != -1) X { X (void) close(fdin); X fdin = -1; X } X if (fdout != -1) X { X (void) close(fdout); X fdout = -1; X } X if (fdre != -1) X { X (void) close(fdre); X fdre = -1; X } X X fdre = pty_readsock(fnsty,fnre); X if (fdre == -1) X return -1; /* damn. */ X return 0; X} X Xstatic int reconnect() X{ X int t; X char buf[1]; X char fntty[TTYNAMELEN]; /* sigh */ X int flags = 0; X X t = pty_acceptsock(fdre); X (void) close(fdre); X fdre = t; X if (fdre == -1) X return -1; X X#define VCF (void) close(fdre) X#define BONK(xxx,yyy) if ((xxx) == -1) { VCF; return -1; } else (yyy); X X/* What about fd 2 for warnings & errors? No, master doesn't use them. */ X X/* Must have: in, out, siglerpid, pgrp, flagjobctrl. 1, 2, 16, 32, 256. */ X/* Except if NO_FDPASSING: just flagjobctrl in that case. */ X/* If fdtty, must have also tmochartty, tmotty, fntty. 8: 64, 128, 1024. */ X/* Finally, fdpass is independent of all the rest. */ X X/* CHANGE: With fdpass, fdin and fdout are irrelevant. */ X X if (pty_sendint(fdre,'p',&pid) == -1) X { X VCF; X return -1; X } X X while (pty_getch(fdre,buf) == 0) X switch(buf[0]) X { X#ifdef NO_FDPASSING X case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 8) break; X#else X case '0': BONK(pty_putgetfd(fdre,'0',&fdin),flags |= 1) break; X case '1': BONK(pty_putgetfd(fdre,'1',&fdout),flags |= 2) break; X case 'f': BONK(pty_putgetfd(fdre,'f',&fdpass),flags |= 4) break; X case 't': BONK(pty_putgetfd(fdre,'t',&fdtty),flags |= 8) break; X case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 1024) break; X#endif X case 'p': BONK(pty_putgetint(fdre,'p',&siglerpid),flags |= 16) break; X case 'g': BONK(pty_putgetint(fdre,'g',&pgrp),flags |= 32) break; X case 'c': BONK(pty_putgettty(fdre,'c',&tmochartty),flags |= 64) break; X case 'n': BONK(pty_putgettty(fdre,'n',&tmotty),flags |= 128) break; X case 'j': BONK(pty_putgetint(fdre,'j',&flagjobctrl),flags |= 256) break; X#ifdef NO_FDPASSING X case ' ': if ((flags & 256) != 256) { VCF; return -1; } X#else X case ' ': if (flags & 4) flags |= 3; X if ((flags & 307) != 307) { VCF; return -1; } X if (flags & 8) if ((flags & 1024) != 1024) { VCF; return -1; } X#endif X if (flags & 8) if ((flags & 192) != 192) { VCF; return -1; } X VCF; /* yahoo! */ X X#ifdef NO_FDPASSING X if ((fdtty = open(fntty,O_RDWR)) == -1) X return -1; X if ((fdin = dup(fdre)) == -1) X { X (void) close(fdtty); X fdtty = -1; X return -1; X } X if ((fdout = dup(fdre)) == -1) X { X (void) close(fdtty); X fdtty = -1; X (void) close(fdout); X fdout = -1; X return -1; X } X#endif X (void) close(open(fntty,O_RDWR)); X /* XXX: do we really have to reattach? */ X /* I wish there were no concept of controlling tty. */ X /* Instead, an ioctl on /dev/tty (i.e., fd 3) would */ X /* return a session identifier. */ X X if (fdpass != -1) X { X if (pty_sendint(fdpass,'G',&siglerpid) == -1) X return -1; X /* XXX: death(1) might be more intuitive. Then */ X /* again, it may also be much more destructive. */ X if (pty_sendfd(fdpass,'m',&fdmty) == -1) X return -1; X if (pty_sendfd(fdpass,'s',&fdsty) == -1) X return -1; X } X X /* So that we can disconnect again, we have to reset the */ X /* siglerpid in fdsess. That done, we've totally severed */ X /* our previous link to a connection. */ X (void) lseek(fdsess,(long) sizeof(int),0); X (void) write(fdsess,(char *) &siglerpid,sizeof(int)); X X flagsigler = 1; X (void) setpgrp(0,pgrp); X (void) kill(pid,SIGUSR1); /* grrrr */ X return 0; X default: (void) pty_putch(fdre," "); break; X } X VCF; X return -1; X} X Xstruct timeval instant = { 0, 0 }; X Xvoid master(fnsty,child) Xchar *fnsty; Xint child; X{ X fd_set rfds; X fd_set wfds; X int fdnum; X int r; X X /* XXX: is it a race for child to set pty modes? */ X X /* Note that we don't close fdsty. */ X X siglerpid = getppid(); X slavepid = child; X pid = getpid(); X glfnsty = fnsty; X X if (flagsession) X { X /* Security note: This is the only file we actually create, */ X /* not counting the reconnect socket. */ X (void) sprintf(fnsess,"sess.%s",fnsty + sizeof(DEVSTY) - 3); X fdsess = open(fnsess,O_RDWR | O_CREAT | O_TRUNC,0600); X (void) write(fdsess,(char *) &uid,sizeof(int)); X (void) write(fdsess,(char *) &siglerpid,sizeof(int)); X (void) write(fdsess,(char *) &pid,sizeof(int)); X (void) write(fdsess,(char *) &slavepid,sizeof(int)); X /* We'll never actually bother closing fdsess. Who cares? */ X } X X sig_ignore(SIGURG); X sig_ignore(SIGIO); X sig_ignore(SIGHUP); X sig_ignore(SIGQUIT); X sig_ignore(SIGINT); X sig_sethandler(SIGXCPU,sig_force); sig_handle(SIGXCPU); X sig_ignore(SIGXFSZ); X sig_ignore(SIGPROF); X sig_ignore(SIGVTALRM); X X sig_default(SIGEMT); /* XXX: really dump? */ X sig_default(SIGIOT); X sig_default(SIGTRAP); X sig_default(SIGSYS); X sig_default(SIGFPE); X sig_default(SIGILL); X sig_default(SIGSEGV); X X sig_default(SIGSTOP); X X sig_sethandler(SIGTTIN,sig_ttin); sig_handle(SIGTTIN); X sig_sethandler(SIGTTOU,sig_ttou); sig_handle(SIGTTOU); X sig_sethandler(SIGTSTP,sig_tstp); sig_handle(SIGTSTP); X sig_sethandler(SIGUSR1,sig_cont); sig_handle(SIGUSR1); X sig_ignore(SIGCONT); /* grrrr. see explanation above sig_cont. */ X sig_sethandler(SIGPIPE,sig_pipe); sig_handle(SIGPIPE); X X sig_sethandler(SIGCHLD,sig_chld); sig_handle(SIGCHLD); X X sig_sethandler(SIGTERM,sig_term); sig_handle(SIGTERM); X sig_sethandler(SIGWINCH,sig_winch); sig_handle(SIGWINCH); X X sig_sethandler(SIGUSR2,sig_usr2); sig_handle(SIGUSR2); X X if (fdpass != -1) X { X if (pty_sendint(fdpass,'G',&siglerpid) == -1) X death(1); X if (pty_sendfd(fdpass,'m',&fdmty) == -1) X death(1); X if (pty_sendfd(fdpass,'s',&fdsty) == -1) X death(1); X } X X#define SET_FDNUM fdnum = fdin; if (fdout > fdnum) fdnum = fdout; \ Xif (fdmty > fdnum) fdnum = fdmty; fdnum++; X X SET_FDNUM X X if (fdpass == -1) X (void) fcntl(fdmty,F_SETFL,FNDELAY); X /* If it doesn't work, too bad. */ X X#ifdef SIGINTERRUPT X sig_interrupt(); X#endif X X for (;;) X { X /* Stage 1: Mangle internal states. This could be made into a */ X /* critical section, but there's no point. */ X X if ((flagconnected == 2) && (flagchild != 2)) X flagconnected = 1 + 2 * (flagchild == 0); X if ((flagconnected != 0) && (flagsigler == 0)) X { X flagconnected = 0; X if (flagsession) X { X (void) kill(siglerpid,SIGTERM); X#ifdef NO_SESSION X ; /* impossible */ X#else X if (disconnect(fnsty) == -1) X quickdeath(1); /* XXX: sigh */ X if (fdnum <= fdre) X fdnum = fdre + 1; X#endif X } X } X X /* Stage 2: Prepare fds, and select(). */ X X FD_ZERO(&rfds); X FD_ZERO(&wfds); X X if ((fdpass == -1) && (outbuflen < outbufsiz)) X FD_SET(fdmty,&rfds); X if ((fdpass == -1) && ptybuflen) X FD_SET(fdmty,&wfds); X if ((fdpass == -1) X &&(ptybuflen < ptybufsiz) && (flagsigler == 1) X &&(flagconnected == 1) && (flagchild == 1)) X FD_SET(fdin,&rfds); X if ((fdpass == -1) X &&(outbuflen) && (flagsigler == 1) && (flagconnected == 1)) X FD_SET(fdout,&wfds); X X if (flagsession && (flagconnected == 0)) X FD_SET(fdre,&rfds); X X /* The times to flush buffers: when the child has stopped and we're */ X /* connected; when the child has died and we're connected; when the */ X /* signaller has died and we don't support sessions. */ X if (((flagconnected == 1) && (flagchild != 1)) X ||((flagconnected == 0) && (flagsession == 0))) X r = select(fdnum,&rfds,&wfds,(fd_set *) 0,&instant); X else X r = select(fdnum,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0); X X X /* Stage 3: Interpret the results and handle special cases. */ X X if (r <= 0) X if (r == -1) X switch(errno) X { X case EBADF: death(1); X break; X case EINTR: break; /* fine. */ X case EINVAL: break; /* impossible. */ X default: break; /* say what? */ X } X else /* r is 0 */ X { X if (flagconnected == 1) /* flagchild is 0 or 2 */ X if (flagchild == 0) X break; /* That's it! Child died, and we're outta here! */ X else X { /* done with flush, time to stop sigler & idle */ X if (flagjobctrl) X { X /* As usual, if we don't have a tty, tmotty == tmochartty X and it won't matter that fdtty is undefined. */ X (void) setpgrp(0,pgrp); X if (tty_modifymodes(fdtty,&tmotty,&tmochartty) == -1) X ; /* XXX: what to do? */ X (void) setpgrp(0,pid); X switch(childsig) X { X case SIGSTOP: (void) kill(siglerpid,SIGSTOP); break; X case SIGTTOU: (void) kill(siglerpid,SIGTTOU); break; X case SIGTTIN: (void) kill(siglerpid,SIGTTIN); break; X case SIGTSTP: (void) kill(siglerpid,SIGTSTP); break; X case SIGCONT: break; /* special case---see sig_tstp */ X default: (void) kill(siglerpid,SIGSTOP); break; X } X flagconnected = 2; X } X } X else if (flagconnected == 0) /* non-session, sigler dead */ X break; /* Giving pty pgrp a HUP, ho hum */ X /* Most pgrp-based killing would be more logically done */ X /* one process at a time, i.e., we should give our child */ X /* a signal specially. But nobody else does, so we won't. */ X } X else X { X#ifndef NO_SESSION X if (flagconnected == 0) X if (FD_ISSET(fdre,&rfds)) X if (reconnect() == -1) X { X if (disconnect(fnsty) == -1) X quickdeath(1); /* sigh */ X if (fdnum <= fdre) X fdnum = fdre + 1; X } X else X { X flagconnected = 1; /* yay! */ X SET_FDNUM X continue; /* XXX */ X } X#endif X X X /* Stage 4: Do normal I/O. */ X X#ifdef SIGINTERRUPT X sig_startring(); /* blocking? never heard of it */ X#endif X X if (FD_ISSET(fdin,&rfds)) X { X /* ptybuflen must be smaller than ptybufsiz. */ X r = read(fdin,ptybuf + ptybuflen,ptybufsiz - ptybuflen); X if (r == -1) X switch(errno) X { X case EINTR: case EWOULDBLOCK: break; /* fine */ X default: death(1); X } X else if (r == 0) /* EOF */ X { X ; /* XXX: there's no way to pass an EOF */ X } X else X ptybuflen += r; X } X if (FD_ISSET(fdmty,&rfds)) X { X /* outbuflen must be smaller than outbufsiz. */ X r = read(fdmty,outbuf + outbuflen,outbufsiz - outbuflen); X if (r == -1) X switch(errno) X { X case EINTR: case EWOULDBLOCK: break; /* fine */ X default: death(1); X } X else if (r == 0) /* EOF */ X { X ; /* This can't happen. The slave can't pass an EOF. */ X /* XXX: Should we close fdout anyway? */ X } X else X outbuflen += r; X } X if (FD_ISSET(fdout,&wfds)) X { X r = write(fdout,outbuf,outbuflen); X if (r == -1) X switch(errno) X { X case EINTR: case EWOULDBLOCK: break; /* fine */ X default: death(1); X } X else if (r == 0) /* ? */ X ; /* impossible */ X else if (r == outbuflen) X outbuflen = 0; X else X { X outbuflen -= r; X copy(outbuf,outbuf + r,outbuflen); X } X } X if (FD_ISSET(fdmty,&wfds)) X { X r = write(fdmty,ptybuf,ptybuflen); X if (r == -1) X switch(errno) X { X case EINTR: case EWOULDBLOCK: break; /* fine */ X default: death(1); X } X else if (r == 0) /* ? */ X ; /* impossible */ X else if (r == ptybuflen) X ptybuflen = 0; X else X { X ptybuflen -= r; X copy(ptybuf,ptybuf + r,ptybuflen); X } X } X X#ifdef SIGINTERRUPT X sig_stopring(); X#endif X } X } X X death(0); X} END_OF_FILE if test 18086 -ne `wc -c <'master.c'`; then echo shar: \"'master.c'\" unpacked with wrong size! fi # end of 'master.c' fi if test -f 'slave.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'slave.c'\" else echo shar: Extracting \"'slave.c'\" \(2701 characters\) sed "s/^X//" >'slave.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "file.h" X#include "config.h" X#include "pty.h" X#include "tty.h" X#include "err.h" X#include "sig.h" X#include "slave.h" X#include "logs.h" X Xvoid slave(fnsty,arg) Xchar *fnsty; Xchar **arg; X{ X sig_ignore(SIGTTOU); X sig_ignore(SIGTTIN); X X if (fdtty > -1) X { X (void) tty_dissoc(fdtty); /* must succeed */ X (void) close(fdtty); X } X if (fdre > -1) X (void) close(fdre); X (void) close(fdin); X (void) close(fdout); X (void) close(fdmty); X (void) close(fdsty); X (void) close(0); X (void) close(1); X if (flagsameerr < 2) X (void) close(2); X if (flagsameerr < 1) X { X (void) close(3); X for (fdout = getdtablesize();fdout > 3;fdout--) X (void) close(fdout); X } X X if (open(fnsty,O_RDONLY) != 0) X fatalerrp(1,"pty: fatal: cannot reopen pty for input\n",errno); X if (open(fnsty,O_WRONLY) != 1) X fatalerrp(1,"pty: fatal: cannot reopen pty for output\n",errno); X if (flagsameerr < 2) X if (open(fnsty,(flagxerrwo ? O_WRONLY : O_RDWR)) != 2) X fatalerrp(1,"pty: fatal: cannot reopen pty for stderr\n",errno); X if (flagsameerr < 1) X if (open("/dev/tty",O_RDWR) != 3) X fatalerrp(1,"pty: fatal: cannot reopen pty for /dev/tty\n",errno); X X if ((fdtty = open("/dev/tty",O_RDWR)) == -1) X fatalerrp(1,"pty: fatal: cannot reopen pty for temporary /dev/tty\n",errno); X X if (setpgrp(0,getpid()) == -1) X fatalerr(1,"pty: fatal: cannot set process group\n"); X if (tty_setpgrp(fdtty,getpid()) == -1) X fatalerrp(1,"pty: fatal: cannot set pty process group",errno); X if (tty_setmodes(fdtty,&tmopty) == -1) X fatalerr(1,"pty: fatal: cannot set pty modes\n"); X if (flagxexcl) X if (tty_setexcl(fdtty) == -1) X fatalerr(1,"pty: fatal: cannot set exclusive use on pty\n"); X X (void) fchmod(fdtty,USEDPTYMODE); X X if (flagxchown) X (void) fchown(fdtty,uid,PTYGROUP); X X (void) close(fdtty); X X date = now(); /* could use original date instead */ X X if (flagxutmp) X { X if (flagverbose) X warnerr2("%s","pty: writing utmp entry\n"); X if (utmp(fnsty + PTYUTMP_OFFSET,username,PTYUTMP_HOST,date) == -1) X warnerr2("pty: warning: cannot write %s utmp entry",fnsty); X } X X if (flagxwtmp) X { X if (flagverbose) X warnerr2("%s","pty: writing wtmp entry\n"); X if (wtmp(fnsty + PTYWTMP_OFFSET,username,PTYWTMP_HOST,date) == -1) X warnerr2("pty: warning: cannot write %s wtmp entry",fnsty); X } X X if (setreuid(uid,uid) == -1) X /* We absolutely refuse to exec while setuid. */ X fatalerrp(1,"pty: fatal: cannot setreuid",errno); X X sig_restore(); X if (flagverbose) X warnerr2("pty: executing program %s\n",arg[0]); X (void) execvp(arg[0],arg); X fatalerr2p(1,"pty: fatal: cannot execute %s",arg[0],errno); X /*NOTREACHED*/ X} END_OF_FILE if test 2701 -ne `wc -c <'slave.c'`; then echo shar: \"'slave.c'\" unpacked with wrong size! fi # end of 'slave.c' fi if test -f 'err.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'err.c'\" else echo shar: Extracting \"'err.c'\" \(1108 characters\) sed "s/^X//" >'err.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include <stdio.h> X#include "config.h" X#include "pty.h" X#include "err.h" X Xstatic void perr(e) Xint e; X{ X if (!flagquiet) X { X if ((e < 0) || (e > sys_nerr)) X (void) fprintf(stderr,"error %d",e); X else X (void) fputs(sys_errlist[e],stderr); X } X} X Xstatic void serr(s) Xchar *s; X{ X if (!flagquiet) X (void) fputs(s,stderr); X} X Xvoid warnerr2(p,s) Xchar *p; Xchar *s; X{ X if (!flagquiet) X (void) fprintf(stderr,p,s); X} X X/* Note that accounting is based on the real uid, as it should be. */ X Xvoid fatal(x) Xint x; X{ X (void) setreuid(uid,uid); X (void) exit(x); X /*NOTREACHED*/ X} X Xvoid fatalinfo(x,s) Xint x; Xchar **s; X{ X while (*s) X { X serr(*(s++)); X serr("\n"); X } X fatal(x); X /*NOTREACHED*/ X} X Xvoid fatalerr(x,s) Xint x; Xchar *s; X{ X serr(s); X fatal(x); X /*NOTREACHED*/ X} X Xvoid fatalerr2p(x,p,s,e) Xint x; Xchar *p; Xchar *s; Xint e; X{ X warnerr2(p,s); X serr(": "); X perr(e); X serr("\n"); X fatal(x); X /*NOTREACHED*/ X} X Xvoid fatalerrp(x,s,e) Xint x; Xchar *s; Xint e; X{ X serr(s); X serr(": "); X perr(e); X serr("\n"); X fatal(x); X /*NOTREACHED*/ X} END_OF_FILE if test 1108 -ne `wc -c <'err.c'`; then echo shar: \"'err.c'\" unpacked with wrong size! fi # end of 'err.c' fi if test -f 'tty.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tty.c'\" else echo shar: Extracting \"'tty.c'\" \(8009 characters\) sed "s/^X//" >'tty.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include <sys/ioctl.h> X#include "file.h" X#include "err.h" X#include "tty.h" X X#define generic GENERIC * X Xstatic int ioc(fd,req,arg) /* non-interruptable ioctl */ Xint fd; Xunsigned long req; Xgeneric arg; X{ X int result; X X do X result = ioctl(fd,req,(char *) arg); X while ((result == -1) && (errno == EINTR)); X return result; X} X Xint tty_getctrl() X{ X int fd; X int dummy; X X#define ISTTY(f) (ioc(f,(unsigned long) TIOCGPGRP,(generic) &dummy) == 0) X X if (ISTTY(3)) X return 3; X if (((fd = open("/dev/tty",O_RDWR)) != -1) && ISTTY(fd)) X return fd; X if (((fd = dup(0)) != -1) && ISTTY(fd)) X return fd; X if (((fd = dup(1)) != -1) && ISTTY(fd)) X return fd; X return -1; X} X Xtty_setexcl(fd) Xint fd; X{ X return ioc(fd,(unsigned long) TIOCEXCL,(generic) 0); X /* setting exclusive use is a bit unusual but it works */ X /* opening /dev/tty should still be allowed, though */ X} X Xtty_setpgrp(fd,pgrp) Xint fd; Xint pgrp; X{ X return ioc(fd,(unsigned long) TIOCSPGRP,(generic) &pgrp); X} X Xtty_dissoc(fd) Xint fd; X{ X return ioc(fd,(unsigned long) TIOCNOTTY,(generic) 0); X} X Xtty_getmodes(fd,tmo) Xint fd; Xstruct ttymodes *tmo; X{ X if (ioc(fd,(unsigned long) TIOCGETD,(generic) &(tmo->di)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCGETP,(generic) &(tmo->sg)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCGETC,(generic) &(tmo->tc)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCLGET,(generic) &(tmo->lb)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCGLTC,(generic) &(tmo->lt)) == -1) return -1; X#ifdef TTY_WINDOWS X if (ioc(fd,(unsigned long) TIOCGWINSZ,(generic) &(tmo->ws)) == -1) return -1; X#endif X#ifdef TTY_AUXCHARS X if (ioc(fd,(unsigned long) TIOCGAUXC,(generic) &(tmo->au)) == -1) return -1; X#endif X return 0; X} X Xtty_setmodes(fd,tmo) Xint fd; Xstruct ttymodes *tmo; X{ X if (ioc(fd,(unsigned long) TIOCSETD,(generic) &(tmo->di)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCSETP,(generic) &(tmo->sg)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCSETC,(generic) &(tmo->tc)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCLSET,(generic) &(tmo->lb)) == -1) return -1; X if (ioc(fd,(unsigned long) TIOCSLTC,(generic) &(tmo->lt)) == -1) return -1; X#ifdef TTY_WINDOWS X if (ioc(fd,(unsigned long) TIOCSWINSZ,(generic) &(tmo->ws)) == -1) return -1; X#endif X#ifdef TTY_AUXCHARS X if (ioc(fd,(unsigned long) TIOCSAUXC,(generic) &(tmo->au)) == -1) return -1; X#endif X return 0; X} X Xtty_modifymodes(fd,tmonew,tmoold) Xint fd; Xstruct ttymodes *tmonew; Xstruct ttymodes *tmoold; X{ X if (tmonew->di != tmoold->di) X if (ioc(fd,(unsigned long) TIOCSETD,(generic) &(tmonew->di)) == -1) return -1; X if ((tmonew->sg.sg_flags != tmoold->sg.sg_flags) X ||(tmonew->sg.sg_ispeed != tmoold->sg.sg_ispeed) X ||(tmonew->sg.sg_ospeed != tmoold->sg.sg_ospeed) X ||(tmonew->sg.sg_erase != tmoold->sg.sg_erase) X ||(tmonew->sg.sg_kill != tmoold->sg.sg_kill)) X if (ioc(fd,(unsigned long) TIOCSETP,(generic) &(tmonew->sg)) == -1) return -1; X if ((tmonew->tc.t_intrc != tmoold->tc.t_intrc) X ||(tmonew->tc.t_quitc != tmoold->tc.t_quitc) X ||(tmonew->tc.t_startc != tmoold->tc.t_startc) X ||(tmonew->tc.t_stopc != tmoold->tc.t_stopc) X ||(tmonew->tc.t_eofc != tmoold->tc.t_eofc) X ||(tmonew->tc.t_brkc != tmoold->tc.t_brkc)) X if (ioc(fd,(unsigned long) TIOCSETC,(generic) &(tmonew->tc)) == -1) return -1; X if (tmonew->lb != tmoold->lb) X if (ioc(fd,(unsigned long) TIOCLSET,(generic) &(tmonew->lb)) == -1) return -1; X if ((tmonew->lt.t_suspc != tmoold->lt.t_suspc) X ||(tmonew->lt.t_dsuspc != tmoold->lt.t_dsuspc) X ||(tmonew->lt.t_rprntc != tmoold->lt.t_rprntc) X ||(tmonew->lt.t_flushc != tmoold->lt.t_flushc) X ||(tmonew->lt.t_werasc != tmoold->lt.t_werasc) X ||(tmonew->lt.t_lnextc != tmoold->lt.t_lnextc)) X if (ioc(fd,(unsigned long) TIOCSLTC,(generic) &(tmonew->lt)) == -1) return -1; X#ifdef TTY_WINDOWS X if ((tmonew->ws.ws_xpixel != tmoold->ws.ws_xpixel) X ||(tmonew->ws.ws_ypixel != tmoold->ws.ws_ypixel) X ||(tmonew->ws.ws_row != tmoold->ws.ws_row) X ||(tmonew->ws.ws_col != tmoold->ws.ws_col)) X if (ioc(fd,(unsigned long)TIOCSWINSZ,(generic)&(tmonew->ws))==-1) return -1; X#endif X#ifdef TTY_AUXCHARS X if ((tmonew->au.t_usemap != tmoold->au.t_usemap) X ||(tmonew->au.t_usest != tmoold->au.t_usest)) X if (ioc(fd,(unsigned long)TIOCSAUXC,(generic)&(tmonew->au))==-1) return -1; X#endif X return 0; X} X X/* XXX: Should use copy() for this. */ Xvoid tty_copymodes(tmonew,tmoold) Xstruct ttymodes *tmonew; Xstruct ttymodes *tmoold; X{ X tmonew->di = tmoold->di; X tmonew->sg.sg_ispeed = tmoold->sg.sg_ispeed; X tmonew->sg.sg_ospeed = tmoold->sg.sg_ospeed; X tmonew->sg.sg_erase = tmoold->sg.sg_erase; X tmonew->sg.sg_kill = tmoold->sg.sg_kill; X tmonew->sg.sg_flags = tmoold->sg.sg_flags; X tmonew->tc.t_intrc = tmoold->tc.t_intrc; X tmonew->tc.t_quitc = tmoold->tc.t_quitc; X tmonew->tc.t_startc = tmoold->tc.t_startc; X tmonew->tc.t_stopc = tmoold->tc.t_stopc; X tmonew->tc.t_eofc = tmoold->tc.t_eofc; X tmonew->tc.t_brkc = tmoold->tc.t_brkc; X tmonew->lb = tmoold->lb; X tmonew->lt.t_suspc = tmoold->lt.t_suspc; X tmonew->lt.t_dsuspc = tmoold->lt.t_dsuspc; X tmonew->lt.t_rprntc = tmoold->lt.t_rprntc; X tmonew->lt.t_flushc = tmoold->lt.t_flushc; X tmonew->lt.t_werasc = tmoold->lt.t_werasc; X tmonew->lt.t_lnextc = tmoold->lt.t_lnextc; X#ifdef TTY_WINDOWS X tmonew->ws.ws_xpixel = tmoold->ws.ws_xpixel; X tmonew->ws.ws_ypixel = tmoold->ws.ws_ypixel; X tmonew->ws.ws_row = tmoold->ws.ws_row; X tmonew->ws.ws_col = tmoold->ws.ws_col; X#endif X#ifdef TTY_AUXCHARS X tmonew->au.t_usest = tmoold->au.t_usest; X tmonew->au.t_usemap = tmoold->au.t_usemap; X#endif X} X Xvoid tty_copywin(tmonew,tmoold) Xstruct ttymodes *tmonew; Xstruct ttymodes *tmoold; X{ X ; X#ifdef TTY_WINDOWS X tmonew->ws.ws_xpixel = tmoold->ws.ws_xpixel; X tmonew->ws.ws_ypixel = tmoold->ws.ws_ypixel; X tmonew->ws.ws_row = tmoold->ws.ws_row; X tmonew->ws.ws_col = tmoold->ws.ws_col; X#endif X} X Xvoid tty_charmode(tmo) Xstruct ttymodes *tmo; X{ X tty_mungemodes(tmo,3,0,2,0,3,0); X} X Xvoid tty_mungemodes(tmo,cbreak,new,echo,crmod,raw,crt) Xstruct ttymodes *tmo; Xint cbreak; Xint new; Xint echo; Xint crmod; Xint raw; Xint crt; X{ X if (new >= 2) X tmo->di = ((new == 3) ? NTTYDISC : OTTYDISC); X if (crmod >= 2) X tmo->sg.sg_flags = (tmo->sg.sg_flags & ~CRMOD) | (CRMOD * (crmod == 3)); X if (echo >= 2) X tmo->sg.sg_flags = (tmo->sg.sg_flags & ~ECHO) | (ECHO * (echo == 3)); X if (raw >= 2) X tmo->sg.sg_flags = (tmo->sg.sg_flags & ~RAW) | (RAW * (raw == 3)); X if (cbreak >= 2) X tmo->sg.sg_flags = (tmo->sg.sg_flags & ~CBREAK) | (CBREAK * (cbreak == 3)); X if (crt >= 2) X tmo->lb = (tmo->lb & ~(CRTBS | CRTERA | CRTKIL | CTLECH)) X | ((CRTBS | CRTERA | CRTKIL | CTLECH) * (crt == 3)); X} X Xvoid tty_initmodes(tmo,cbreak,new,echo,crmod,raw,crt) Xstruct ttymodes *tmo; Xint cbreak; Xint new; Xint echo; Xint crmod; Xint raw; Xint crt; X{ X /* Here we specify Ye Standard BSD Terminal Settings. */ X X tmo->di = ((new % 2) ? NTTYDISC : OTTYDISC); X tmo->sg.sg_ispeed = EXTB; X tmo->sg.sg_ospeed = EXTB; X tmo->sg.sg_erase = 127; /* del */ X tmo->sg.sg_kill = 21; /* ^U */ X tmo->sg.sg_flags = EVENP | ODDP | XTABS X | (CRMOD * (crmod % 2)) | (ECHO * (echo % 2)) X | (RAW * (raw % 2)) | (CBREAK * (cbreak % 2)); X tmo->tc.t_intrc = 3; /* ^C */ X tmo->tc.t_quitc = 28; /* ^\ */ X tmo->tc.t_startc = 17; /* ^Q */ X tmo->tc.t_stopc = 19; /* ^S */ X tmo->tc.t_eofc = 4; /* ^D */ X tmo->tc.t_brkc = -1; /* undef */ X tmo->lb = ((CRTBS | CRTERA | CRTKIL | CTLECH) * (crt % 2)) | DECCTQ; X tmo->lt.t_suspc = 26; /* ^Z */ X tmo->lt.t_dsuspc = 25; /* ^Y */ X tmo->lt.t_rprntc = 18; /* ^R */ X tmo->lt.t_flushc = 15; /* ^O */ X tmo->lt.t_werasc = 23; /* ^W */ X tmo->lt.t_lnextc = 22; /* ^V */ X#ifdef TTY_WINDOWS X tmo->ws.ws_xpixel = 0; /* Or read from TERMCAP? Hmmm */ X tmo->ws.ws_ypixel = 0; X tmo->ws.ws_row = 0; X tmo->ws.ws_col = 0; X#endif X#ifdef TTY_AUXCHARS X tmo->au.t_usest = 20; /* ^T */ X tmo->au.t_usemap = UST_LOAD1 | UST_LOAD5 | UST_LOAD15 | UST_RAWCPU X | UST_UPTIME | UST_PGRP | UST_CHILDS | UST_PCPU | UST_STATE; X#endif X} END_OF_FILE if test 8009 -ne `wc -c <'tty.c'`; then echo shar: \"'tty.c'\" unpacked with wrong size! fi # end of 'tty.c' fi if test -f 'texts.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'texts.c'\" else echo shar: Extracting \"'texts.c'\" \(3806 characters\) sed "s/^X//" >'texts.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include "texts.h" X Xchar *ptyauthor[] = { X"pty was written by Daniel J. Bernstein." , X"Internet address: brnstnd@acf10.nyu.edu." , X0 } ; X Xchar *ptyversion[] = { X"pty version 3.0, June 1, 1990." , X"Copyright (c) 1990, Daniel J. Bernstein." , X"All rights reserved." , X0 } ; X Xchar *ptycopyright[] = { X"pty version 3.0, June 1, 1990." , X"Copyright (c) 1990, Daniel J. Bernstein." , X"All rights reserved." , X"" , X"I want this program to be distributed freely in original form." , X"" , X"Once you've received a legal copy of this program, you can use it." , X"Forever. Nobody can take that right away from you. You can make changes" , X"and backup copies for your use (or, if you're an organization, for the" , X"use of everyone in the organization). You can distribute patches (though" , X"not patched versions). You'd have all these rights even if I didn't tell" , X"you about them." , X"" , X"I do grant you further rights, as detailed in the source package. Don't" , X"worry about them unless you're planning to distribute further copies." , X"" , X"If you have questions about this program or about this notice, or if you" , X"would like additional rights beyond those granted above, or if you have" , X"a patch that you don't mind sharing, please contact me on the Internet" , X"at brnstnd@acf10.nyu.edu." , X0 } ; X Xchar *ptywarranty[] = { X"Daniel J. Bernstein disclaims all warranties to the extent permitted" , X"by applicable law. He is not and shall not be liable for any damages" , X"arising from the use of this program. This disclaimer shall be governed" , X"by the laws of the state of New York." , X"" , X"In other words, use this program at your own risk." , X"" , X"If you have questions about this program or about this disclaimer of" , X"warranty, please feel free to contact me at brnstnd@acf10.nyu.edu on" , X"the Internet." , X0 } ; X Xchar *ptyusage[] = { X"Usage: pty [ -qQve3EdDjJsStT0FACHUVW ] [ -fn ] [ -p[cCdDeEnNrRsS0] ]" , X" [ -x[cCeEnNoOrRsSuUwWxX ] program [ arg ... ]" , X"Help: pty -H" , X0 } ; X Xchar *ptyhelp[] = { X"pty runs a program under a pseudo-terminal session." , X"pty -ACHUVW: print authorship notice, copyright notice, this notice," , X" short usage summary, version number, disclaimer of warranty" , X"pty [-qQve3EdDjJsStT0F] [-fn] [-p[cCdDeEnNrRsS0]] [-x[cCeEnNoOrRsSuUwWxX]]" , X" program [arg...]: run program under a pseudo-terminal" , X"Options processed l to r. Capitals turn things off. Here + means default." , X"-q: quiet (nothing on stderr) -e: leave fds 2 & 3 0=Tp0 p0=pcrEN" , X"+Q: normal level of verbosity -3: leave fd 3 only d=dJT D=Djt d=>T" , X"-v: complain about everything +E: 2 & 3 both->pty s=sxu S=SxU s=>E" , X"-d: we are detached +j: job control +t: change orig tty to char mode" , X"+D: we have ctrl tty -J: ignore stops -T: leave orig tty alone" , X"-s: session (allow disconnect & reconnect) -fn: pass pty fds up fd n" , X"+S: no session: disconnect will send HUP +F: no -f" , X"-p[cCdDeEnNrRsS]: set pty modes; defaults taken from original tty if -D" , X" c: cbreak, character mode +n: change return to newline +e: echo" , X" +d: new line discipline r: raw, no keyboard signals +s: screen, crt" , X"-x[cCeEnNoOrRsSuUwWxX]: security/experimental/extended, may be restricted" , X" c: change pty owner e: pty's stderr write-only x: set TIOCEXCL" , X" +s: setuid, safer +n: check if anyone has pty open u: use /etc/utmp" , X" +r: pick random pty o: skip if anyone has pty open w: use /etc/wtmp" , X"If you have questions about or suggestions for pty, please feel free" , X"to contact the author, Daniel J. Bernstein, at brnstnd@acf10.nyu.edu" , X"on the Internet." , X0 } ; X/* I still can't believe ptyhelp fits. :-) */ END_OF_FILE if test 3806 -ne `wc -c <'texts.c'`; then echo shar: \"'texts.c'\" unpacked with wrong size! fi # end of 'texts.c' fi if test -f 'sig.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sig.c'\" else echo shar: Extracting \"'sig.c'\" \(4549 characters\) sed "s/^X//" >'sig.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include <sys/time.h> X#include "sig.h" X X/* This is a stripped-down signal library, with automatic critical */ X/* sections around every signal handler. As long as no signal handler */ X/* has to pause waiting for another signal, this works beautifully */ X/* and prevents all races. */ X Xstatic int queued[SIGNUM]; Xstatic int quo[SIGNUM]; Xstatic sig_syshandler orig[SIGNUM]; Xstatic sig_handler handler[SIGNUM]; X Xstatic sig_syshandler signalv(s,h) Xregister sig_num s; Xregister sig_syshandler h; X{ X return signal(s,h); X} X Xstatic int crit = 0; X Xstatic void handle(i) Xregister sig_num i; X{ X if (crit) X queued[i] = 1; X else X { X register int q; X register sig_num j; X X crit = 1; (void) handler[i](i); queued[i] = 0; crit = 0; X do for (j = q = 0;j < SIGNUM;j++) if (queued[j]) X { X crit = q = 1; X if (j != i) (void) handler[j](j); X queued[j] = 0; crit = 0; X } X while (q); X } X} X X/* To see why handle() works: First, crit can be considered a local Xvariable, because handle() is the only routine that modifies it, and Xhandle() always leaves crit the same. Second, crit is 1 while any Xhandler is called, and then any simultaneous handle()s will simply Xqueue. Hence handlers are mutually exclusive. Third, when handle() is Xcalled with crit == 0, it can only exit after going through an entire j Xloop with no queued[]s true. Fourth, if all queued[]s are false through Xthat j loop, then crit is not set by handle() during that loop. Finally, Xif crit is 0, handle() will exit with no queued signals: this is true by Xinduction from the previous observations. */ X X X/* There is unfortunately no guarantee that a signal handler as */ X/* passed to signal() will receive its signal number as its first */ X/* argument. We do make that guarantee. */ X X#define HAN(s,h) SIGRET_TYPE h() { handle(s); } X XHAN(0,han0); HAN(1,han1); HAN(2,han2); HAN(3,han3); XHAN(4,han4); HAN(5,han5); HAN(6,han6); HAN(7,han7); XHAN(8,han8); HAN(9,han9); HAN(10,han10); HAN(11,han11); XHAN(12,han12); HAN(13,han13); HAN(14,han14); HAN(15,han15); XHAN(16,han16); HAN(17,han17); HAN(18,han18); HAN(19,han19); XHAN(20,han20); HAN(21,han21); HAN(22,han22); HAN(23,han23); XHAN(24,han24); HAN(25,han25); HAN(26,han26); HAN(27,han27); XHAN(28,han28); HAN(29,han29); HAN(30,han30); HAN(31,han31); X Xstatic sig_syshandler han[32] = X { han0 ,han1 ,han2 ,han3 ,han4 ,han5 ,han6 ,han7 , X han8 ,han9 ,han10,han11,han12,han13,han14,han15, X han16,han17,han18,han19,han20,han21,han22,han23, X han24,han25,han26,han27,han28,han29,han30,han31 X } ; X X#define QUE(s,h) SIGRET_TYPE h() { quo[s] = 1; } X XQUE(0,que0); QUE(1,que1); QUE(2,que2); QUE(3,que3); XQUE(4,que4); QUE(5,que5); QUE(6,que6); QUE(7,que7); XQUE(8,que8); QUE(9,que9); QUE(10,que10); QUE(11,que11); XQUE(12,que12); QUE(13,que13); QUE(14,que14); QUE(15,que15); XQUE(16,que16); QUE(17,que17); QUE(18,que18); QUE(19,que19); XQUE(20,que20); QUE(21,que21); QUE(22,que22); QUE(23,que23); XQUE(24,que24); QUE(25,que25); QUE(26,que26); QUE(27,que27); XQUE(28,que28); QUE(29,que29); QUE(30,que30); QUE(31,que31); X Xstatic sig_syshandler que[32] = X { que0 ,que1 ,que2 ,que3 ,que4 ,que5 ,que6 ,que7 , X que8 ,que9 ,que10,que11,que12,que13,que14,que15, X que16,que17,que18,que19,que20,que21,que22,que23, X que24,que25,que26,que27,que28,que29,que30,que31 X } ; X X Xvoid sig_init() X{ X sig_num i; X X for (i = 0;i < SIGNUM;i++) X quo[i] = 0; X for (i = 0;i < SIGNUM;i++) X orig[i] = signalv(i,que[i]); X} X Xvoid sig_restore() X{ X sig_num i; X X for (i = 0;i < SIGNUM;i++) X (void) signalv(i,orig[i]); X} X Xvoid sig_handle(s) Xsig_num s; X{ X if (quo[s]) X han[s](); X (void) signalv(s,han[s]); X quo[s] = 0; X} X Xvoid sig_ignore(s) Xsig_num s; X{ X (void) signalv(s,SIG_IGN); X} X Xvoid sig_default(s) Xsig_num s; X{ X (void) signalv(s,SIG_DFL); X} X Xvoid sig_sethandler(s,h) Xsig_num s; Xsig_handler h; X{ X handler[s] = h; X} X X#ifdef SIGINTERRUPT Xvoid sig_interrupt() X{ X register sig_num s; X X for (s = 0;s < SIGNUM;s++) X (void) siginterrupt(s,1); X} X#endif X Xvoid sig_startring() X{ X struct itimerval it; X X it.it_value.tv_sec = it.it_interval.tv_sec = 0; X it.it_value.tv_usec = it.it_interval.tv_usec = 10000; X (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0); X} X Xvoid sig_stopring() X{ X struct itimerval it; X X it.it_value.tv_sec = it.it_interval.tv_sec = 0; X it.it_value.tv_usec = it.it_interval.tv_usec = 0; X (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0); X} X X/*ARGSUSED*/ Xvoid nothing(i) Xsig_num i; X{ X ; /* that's right, absolutely nothing. */ X} END_OF_FILE if test 4549 -ne `wc -c <'sig.c'`; then echo shar: \"'sig.c'\" unpacked with wrong size! fi # end of 'sig.c' fi if test -f 'globals.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'globals.c'\" else echo shar: Extracting \"'globals.c'\" \(958 characters\) sed "s/^X//" >'globals.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include "pty.h" X Xint uid; Xint euid; Xint pid; Xint pgrp; Xlong date; Xchar *username; X Xint fdin = -1; Xint fdout = -1; Xint fdmty = -1; Xint fdsty = -1; Xint fdtty = -1; Xint fdre = -1; Xint fdpass = -1; X Xint flagquiet = 0; Xint flagdetached = 0; Xint flagjobctrl = 1; Xint flagttymodes = 1; Xint flagsameerr = 0; Xint flagfdpass = 0; Xint flagsession = 0; Xint flagverbose = 0; X Xint flagxchown = 0; Xint flagxutmp = 0; Xint flagxwtmp = 0; Xint flagxexcl = 0; /* should be 1, but that would break write & friends */ Xint flagxerrwo = 0; /* should be 1, but that would break csh & more */ Xint flagxchkopen = 1; Xint flagxskipopen = 0; Xint flagxrandom = 1; /* random pty searching: worth the effort */ Xint flagxsetuid = 1; X X#include "tty.h" X Xstruct ttymodes tmotty; /* original tty modes */ Xstruct ttymodes tmochartty; /* tty in character mode */ Xstruct ttymodes tmopty; /* original pty modes */ END_OF_FILE if test 958 -ne `wc -c <'globals.c'`; then echo shar: \"'globals.c'\" unpacked with wrong size! fi # end of 'globals.c' fi if test -f 'logs.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'logs.c'\" else echo shar: Extracting \"'logs.c'\" \(1853 characters\) sed "s/^X//" >'logs.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include "logs.h" X#include "file.h" X#include <utmp.h> X#include <strings.h> X X/* utmp and wtmp are about as sensible as /etc/passwd. */ X X/* We never bother to shrink /etc/utmp. */ X Xextern long time(); X Xlong now() X{ X return time((long *) 0); X} X Xint utmp(line,name,host,date) Xchar *line; Xchar *name; Xchar *host; Xlong date; X{ X struct utmp ut; X struct utmp xt; X int fd; X int i; X int j; X X /* only use of strncpy, ever */ X (void) strncpy(ut.ut_line,line,sizeof(ut.ut_line)); X (void) strncpy(ut.ut_name,name,sizeof(ut.ut_name)); X (void) strncpy(ut.ut_host,host,sizeof(ut.ut_host)); X ut.ut_time = date; X X fd = open(PTYUTMP_FILE,O_RDWR); X if (fd == -1) X return -1; X j = i = 0; X while (read(fd,(char *) &xt,sizeof(xt)) == sizeof(xt)) X { X i++; X if (strncmp(xt.ut_line,ut.ut_line,sizeof(ut.ut_line)) == 0) X { X j = i; X break; X } X } X if (j) X { X if (lseek(fd,(long) ((j - 1) * sizeof(xt)),L_SET) == -1) X { X (void) close(fd); X return -1; X } X } X else X { X /* We have to reopen to avoid a race with other end-of-utmp entries. */ X (void) close(fd); X if ((fd = open(PTYUTMP_FILE,O_RDWR | O_APPEND)) == -1) X return -1; X } X if (write(fd,(char *) &ut,sizeof(ut)) < sizeof(ut)) X { X (void) close(fd); X return -1; X } X (void) close(fd); X return 0; X} X Xint wtmp(line,name,host,date) Xchar *line; Xchar *name; Xchar *host; Xlong date; X{ X struct utmp wt; X int fd; X X (void) strncpy(wt.ut_line,line,sizeof(wt.ut_line)); X (void) strncpy(wt.ut_name,name,sizeof(wt.ut_name)); X (void) strncpy(wt.ut_host,host,sizeof(wt.ut_host)); X wt.ut_time = date; X X fd = open(PTYWTMP_FILE,O_WRONLY | O_APPEND); X if (fd == -1) X return -1; X if (write(fd,(char *) &wt,sizeof(wt)) < sizeof(wt)) X { X (void) close(fd); X return -1; X } X (void) close(fd); X return 0; X} END_OF_FILE if test 1853 -ne `wc -c <'logs.c'`; then echo shar: \"'logs.c'\" unpacked with wrong size! fi # end of 'logs.c' fi if test -f 'sock.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sock.c'\" else echo shar: Extracting \"'sock.c'\" \(5474 characters\) sed "s/^X//" >'sock.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include <sys/types.h> X#include <sys/time.h> X#ifndef NO_UNIXSOCKS X#include <sys/socket.h> X#include <sys/un.h> X#ifndef NO_FDPASSING X#include <sys/uio.h> X#endif X#include <stdio.h> X#include <strings.h> X#endif X#include "sock.h" X#include "tty.h" X#include "err.h" X Xstatic int bufwrite(fd,buf,num) Xint fd; Xchar *buf; Xint num; X{ X int r; X X do X { X r = write(fd,buf,num); X if (r > 0) X { X buf += r; X num -= r; X } X } X while ((num > 0) && ((r != -1) || (errno == EINTR))); X return (r >= 0) ? 0 : -1; X} X Xstatic int bufread(fd,buf,num) Xint fd; Xchar *buf; Xint num; X{ X int r; X X do X { X r = read(fd,buf,num); X if (r > 0) X { X buf += r; X num -= r; X } X } X while ((num > 0) && ((r != -1) || (errno == EINTR))); X /* Note that we ignore EOF. */ X return (r >= 0) ? 0 : -1; X} X Xint pty_readsock(line,path) Xchar *line; Xchar *path; X{ X#ifdef NO_UNIXSOCKS X return -1; X#else X int s; X struct sockaddr_un sa; X X if ((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1) X return -1; X sa.sun_family = AF_UNIX; X (void) sprintf(sa.sun_path,"re.%s",line + sizeof(DEVSTY) - 3); X (void) strcpy(path,sa.sun_path); X (void) unlink(sa.sun_path); X if (bind(s,(struct sockaddr *) &sa,strlen(sa.sun_path) + 2) == -1) X return -1; X if (listen(s,5) == -1) X return -1; X return s; X#endif X} X Xint pty_writesock(line) Xchar *line; X{ X#ifdef NO_UNIXSOCKS X return -1; X#else X int s; X struct sockaddr_un sa; X X if ((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1) X return -1; X sa.sun_family = AF_UNIX; X (void) sprintf(sa.sun_path,"re.%s",line + sizeof(DEVSTY) - 3); X if (connect(s,(struct sockaddr *) &sa,strlen(sa.sun_path) + 2) == -1) X return -1; X (void) unlink(sa.sun_path); X return s; X#endif X} X Xint pty_acceptsock(fd) Xint fd; X{ X#ifdef NO_UNIXSOCKS X return -1; X#else X struct sockaddr_un sa; X int salen = sizeof(sa); X X return accept(fd,(struct sockaddr *) &sa,&salen); X#endif X} X Xint pty_putgetonefd(fd,fp) Xint fd; Xint *fp; X{ X#ifdef NO_FDPASSING X return -1; X#else X struct msghdr msg[2]; X int acc[5]; X struct iovec i[2]; X X msg[0].msg_name = 0; X msg[0].msg_namelen = 0; X msg[0].msg_iov = &i[0]; /* grrrr */ X msg[0].msg_iovlen = 0; X msg[0].msg_accrights = (caddr_t) acc; X msg[0].msg_accrightslen = 5 * sizeof(int); X#ifdef USLEEP X (void) usleep((unsigned) 100000); X#else X (void) sleep(1); /* XXX: work around fd passing bug */ X#endif X if (recvmsg(fd,msg,0) == -1) X return -1; X if (msg[0].msg_accrightslen != sizeof(int)) X return -1; X if (*fp != -1) X (void) close(*fp); X *fp = acc[0]; /* yay! we've passed a file descriptor! */ X return 0; X#endif X} X Xint pty_putgetfd(fd,ch,fp) Xint fd; Xchar ch; Xint *fp; X{ X#ifdef NO_FDPASSING X return -1; X#else X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (pty_putgetonefd(fd,fp) < 0) X return -1; X if (bufwrite(fd,".",1) < 0) X return -1; X return 0; X#endif X} X Xint pty_putgetint(fd,ch,ip) Xint fd; Xchar ch; Xint *ip; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,(char *) ip,sizeof(int)) < 0) X return -1; X if (bufwrite(fd,".",1) < 0) X return -1; X return 0; X} X Xint pty_putgetstr(fd,ch,str) Xint fd; Xchar ch; Xchar str[TTYNAMELEN]; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,str,TTYNAMELEN) < 0) X return -1; X if (bufwrite(fd,".",1) < 0) X return -1; X return 0; X} X Xint pty_putgettty(fd,ch,tmo) Xint fd; Xchar ch; Xstruct ttymodes *tmo; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,(char *) tmo,sizeof(struct ttymodes)) < 0) X return -1; X if (bufwrite(fd,".",1) < 0) X return -1; X return 0; X} X Xint pty_sendonefd(fd,fp) Xint fd; Xint *fp; X{ X#ifdef NO_FDPASSING X return -1; X#else X struct msghdr msg[2]; X int acc[5]; /* or just 5? or just 1? who cares */ X struct iovec i[2]; X X msg[0].msg_name = 0; X msg[0].msg_namelen = 0; X msg[0].msg_iov = i; /* grrrr */ X msg[0].msg_iovlen = 0; X msg[0].msg_accrights = (caddr_t) acc; X msg[0].msg_accrightslen = sizeof(int); X acc[0] = *fp; X if (sendmsg(fd,&msg[0],0) == -1) X return -1; X /* yay! we've passed a file descriptor! */ X return 0; X#endif X} X Xint pty_sendfd(fd,ch,fp) Xint fd; Xchar ch; Xint *fp; X{ X#ifdef NO_FDPASSING X return -1; X#else X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch == ' ') X return 1; X if (pty_sendonefd(fd,fp) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch != '.') X return 1; X return 0; X#endif X} X Xint pty_sendint(fd,ch,ip) Xint fd; Xchar ch; Xint *ip; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch == ' ') X return 1; X if (bufwrite(fd,(char *) ip,sizeof(int)) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch != '.') X return 1; X return 0; X} X Xint pty_sendstr(fd,ch,str) Xint fd; Xchar ch; Xchar str[TTYNAMELEN]; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch == ' ') X return 1; X if (bufwrite(fd,str,TTYNAMELEN) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch != '.') X return 1; X return 0; X} X Xint pty_sendtty(fd,ch,tmo) Xint fd; Xchar ch; Xstruct ttymodes *tmo; X{ X if (bufwrite(fd,&ch,1) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch == ' ') X return 1; X if (bufwrite(fd,(char *) tmo,sizeof(struct ttymodes)) < 0) X return -1; X if (bufread(fd,&ch,1) < 0) X return -1; X if (ch != '.') X return 1; X return 0; X} X Xint pty_putch(fd,ch) Xint fd; Xchar *ch; X{ X return bufwrite(fd,ch,1); X} X Xint pty_getch(fd,ch) Xint fd; Xchar *ch; X{ X return bufread(fd,ch,1); X} END_OF_FILE if test 5474 -ne `wc -c <'sock.c'`; then echo shar: \"'sock.c'\" unpacked with wrong size! fi # end of 'sock.c' fi if test -f 'misc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'misc.c'\" else echo shar: Extracting \"'misc.c'\" \(1442 characters\) sed "s/^X//" >'misc.c' <<'END_OF_FILE' X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */ X X#include "config.h" X#include <strings.h> X#include <stdio.h> X#include <pwd.h> Xextern char *getenv(); /* grrrr */ Xextern char *ttyname(); /* grrrr */ X#include "pty.h" X#include "misc.h" X Xint sessdir() X{ X char suid[10]; X char *home; X X if (flagxsetuid) X { X if (chdir(PTYDIR) == -1) X return -1; X } X else X { X if ((home = getenv("HOME")) == 0) X return -1; X else if (chdir(home) == -1) X return -1; X else if ((chdir(".pty") == -1) X &&((mkdir(".pty",0700) == -1) X ||(chdir(".pty") == -1))) X return -1; X } X X (void) sprintf(suid,"%d",uid); X if ((chdir(suid) == -1) X &&((mkdir(suid,0700) == -1) X ||(chdir(suid) == -1))) X return -1; X return 0; X} X Xchar *real_ttyname(fd) Xint fd; /* first guess; should be /dev/tty */ X{ X char *ttyn; X X if ((ttyn = ttyname(fd)) X &&(strcmp(ttyn,"/dev/tty"))) X { X /* This would actually happen if opening /dev/tty converted into */ X /* opening the actual terminal device---which would be nice. */ X return ttyn; X } X /* So much for nice tries. */ X for (fd = getdtablesize();fd >= 0;fd--) X { X if ((ttyn = ttyname(fd)) X &&(strcmp(ttyn,"/dev/tty"))) X return ttyn; X } X return (char *) 0; X} X Xstatic struct passwd *pw; Xstatic char nopwun[12]; X Xvoid setusername() X{ X if (pw = getpwuid(uid)) X username = pw->pw_name; X else X { X (void) sprintf(nopwun,"%d",uid); X username = nopwun; X } X} END_OF_FILE if test 1442 -ne `wc -c <'misc.c'`; then echo shar: \"'misc.c'\" unpacked with wrong size! fi # end of 'misc.c' fi if test ! -d 'patch' ; then echo shar: Creating directory \"'patch'\" mkdir 'patch' fi if test -f 'patch/README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patch/README'\" else echo shar: Extracting \"'patch/README'\" \(4636 characters\) sed "s/^X//" >'patch/README' <<'END_OF_FILE' XAt the moment, the only thing here is telnetd.patch. It's public domain. X Xtelnetd.patch is a safe patch to the telnetd source in telnet.90.03.01 Xon ucbarpa.berkeley.edu (128.32.130.11). A patched telnetd uses pty to Xmanage sessions, so that users can easily disconnect and reconnect login Xsessions. You *must* have compiled file descriptor passing into pty for Xthis to work; telnetd needs to get the pseudo-terminal descriptors from Xpty. Given fd passing, though, the patched telnetd gives you the *full* Xefficiency of the unpatched version, even for reconnected sessions! X XWhat follows is a description of how to make the patched telnetd. If you Xhave the tiocsti utility installed, try ``make > /dev/null'' in this Xdirectory, rather than reading through these instructions. The usual Xwarning: Like all software, this comes with all warranties disclaimed, Xto the extent permitted by applicable law. X X X1. Get the new telnetd: X X % ftp -n 128.32.130.11 < TELNET.FTP X X2. Unpack it: X X % uncompress < telnet.90.03.01.tar.Z | tar xf - X X3. Apply these patches: X X % cd telnet.90.03.01/telnetd X % patch < ../../telnetd.patch X % unifdef -UUSEPTY sys_term.c | cmp - sys_term.c.orig X % unifdef -UUSEPTY telnetd.c | cmp - telnetd.c.orig X % unifdef -UUSEPTY pathnames.h | cmp - pathnames.h.orig X % cd ../.. X X4. Check the pty pathname and fix it if necessary: X X % grep PATH_PTY telnet.90.03.01/telnetd/pathnames.h X X5. Add <sys/stream.h> to defs.h, if appropriate (telnetd bug fix!): X X % test -f /usr/include/sys/stream.h && \ X ( cd telnet.90.03.01/telnetd; chmod 600 defs.h; \ X echo '#include <sys/stream.h>' >> defs.h ) X X6. Add sock.o and sock.h symlinks: X X % ( cd telnet.90.03.01/telnetd; ln -s ../../../sock.{o,h} . ) X X7. Edit the Makefile. Add sock.o to LIBS, add -DUSEPTY to DEFINES, X and change whatever else necessary to get telnetd to compile. X X8. Compile! X X % ( cd telnet.90.03.01/telnetd; (date;make) >>& Makelog ) & X X9. If you actually survive telnetd's incompatibilities and get it to X compile, keep reading. X X XThe reason this telnetd is so big is that it supports Linemode, a bold Xattempt to cut the Internet load from all the fast typists in the world Xby a third or even a half. If you're bored, compile the telnet client as Xwell. X X XAnyway, the telnetd you just compiled supports pty. You probably don't Xwant to replace your original telnetd, because if something goes wrong Xthen you need to log in again. Also, your telnetd may have special Xfeatures for your machine. Instead, take the gradual upgrade path: Put Xtelnetd into /usr/local/telnetd.pty, add a new ``tpt'' port to X/etc/services, and add this line to /etc/inetd.conf: X Xtpt stream tcp nowait root /usr/local/telnetd.pty telnetd X X(On older machines, ``root'' wouldn't be there; imitate the telnet line Xto figure out the right format. If you have the better interface of Xattachport, use it instead.) kill -HUP the inetd process so that it will Xreread inetd.conf. X X XNow try connecting to the tpt port rather than 23 (instead of telnet X, Xdo telnet X tpt). You should end up talking to the new telnetd, a clean Xlogin, and (best of all) a pty session. (The most common problem: Lines Xdon't appear until after you press return. This means that your old Xtelnet doesn't support Linemode; type ``^]mo ch'' to fix it.) If the Xconnection hangs for more than a second or produces weird results, Xyou're probably out of luck; pty's author would appreciate hearing about Xyour experiences. X XIf all goes well (whew!) you can log in normally, just as if you're Xconnected to the old telnetd. Once you're in a shell, try using the pty Xutilities, as if you were under sess. You should find that the commands Xfail unless you're root, because that session was started by root rather Xthan you. (This points out a failure in the telnetd-login-utmp model.) XTo gain control of the session, type ``sessuser'' and pray. If nothing Xgoes wrong, you can then disconnect the session, set up a reconnect to Xanother one, use sessname and sesslist, etc. Yahoo! Tell your users! XReplace your old telnetd with the new one! Add sessuser to the default X.cshrc! And please send a note to brnstnd@kramden.acf.nyu.edu about your Xsuccess. X X XNote that many popular versions of login use an annoying heuristic to Xsave a few microseconds in updating /etc/utmp. The result is, first, Xthat there's a race condition that can break down /etc/utmp on heavily Xloaded machines; and second, that login and pty have different views of X/etc/utmp. For this reason, pty is invoked with -xR to search for ptys Xin order. You may even want to make -xR default (by setting flagxrandom Xto 0 in pty's globals.c). Sigh. END_OF_FILE if test 4636 -ne `wc -c <'patch/README'`; then echo shar: \"'patch/README'\" unpacked with wrong size! fi # end of 'patch/README' fi if test -f 'patch/TELNET.FTP' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patch/TELNET.FTP'\" else echo shar: Extracting \"'patch/TELNET.FTP'\" \(69 characters\) sed "s/^X//" >'patch/TELNET.FTP' <<'END_OF_FILE' Xuser anonymous pty Xcd pub Xtype binary Xget telnet.90.03.01.tar.Z Xquit END_OF_FILE if test 69 -ne `wc -c <'patch/TELNET.FTP'`; then echo shar: \"'patch/TELNET.FTP'\" unpacked with wrong size! fi # end of 'patch/TELNET.FTP' fi if test -f 'patch/Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patch/Makefile'\" else echo shar: Extracting \"'patch/Makefile'\" \(3062 characters\) sed "s/^X//" >'patch/Makefile' <<'END_OF_FILE' Xtpat: tpat1 X Xtpat1: igntt X echo '1. Get the new telnetd from ucbarpa.berkeley.edu:' 1>&2 X echo 'This command will retrieve telnet.90.03.01.tar.Z.' 1>&2 X echo 'To continue, just make tpat2 > /dev/null.' 1>&2 X ./igntt tiocsti 'ftp -n 128.32.130.11 < TELNET.FTP' X Xtpat2: igntt X echo '2. Unpack telnetd:' 1>&2 X echo 'This is self-explanatory. You may want tar xvf to see a file list.' 1>&2 X echo 'To continue, just make tpat3 > /dev/null.' 1>&2 X if test -f telnet.90.03.01.tar.Z ;\ X then ./igntt tiocsti 'uncompress < telnet.90.03.01.tar.Z | tar xf -' ;\ X else echo 'Aack! telnet.90.03.01.tar.Z doesn'\''t exist!' ;\ X fi X Xtpat3: igntt X echo '3. Apply these patches:' 1>&2 X echo 'If all goes well, you should see a flood of successful patch output.' 1>&2 X echo 'The unifdefs are to check the safety of the patches;' 1>&2 X echo 'as you will see, the patches do nothing with USEPTY undefined.' 1>&2 X echo 'To continue, just make tpat4 > /dev/null.' 1>&2 X if test -d telnet.90.03.01 ;\ X then ./igntt tiocsti 'cd telnet.90.03.01/telnetd; ' \ X 'patch < ../../telnetd.patch; ' \ X 'unifdef -UUSEPTY sys_term.c | cmp - sys_term.c.orig; ' \ X 'unifdef -UUSEPTY telnetd.c | cmp - telnetd.c.orig; ' \ X 'unifdef -UUSEPTY pathnames.h | cmp - pathnames.h.orig; ' \ X 'cd ../..' ;\ X fi X Xtpat4: igntt X echo '4. Check the pty pathname and fix it if necessary:' 1>&2 X echo 'Since there is no standard like /inst for where programs go,' 1>&2 X echo 'you had better make sure telnetd's pathnames.h is correct.' 1>&2 X echo 'To continue, just make tpat5 > /dev/null.' 1>&2 X ./igntt tiocsti 'grep PATH_PTY telnet.90.03.01/telnetd/pathnames.h' X Xtpat5: igntt X echo '5. Add <sys/stream.h> to defs.h, if necessary:' 1>&2 X echo 'On many machines---particularly a Sun 4---this version of' 1>&2 X echo 'telnetd will not compile without #include <sys/stream.h>.' 1>&2 X echo 'To continue, just make tpat6 > /dev/null.' 1>&2 X if test -f /usr/include/sys/stream.h ;\ X then ./igntt tiocsti '( cd telnet.90.03.01/telnetd; chmod 600 defs.h; echo "#include <sys/stream.h>" >> defs.h )' ;\ X else echo 'You don'\''t have <sys/stream.h>, so just go on.' ;\ X fi X Xtpat6: igntt X echo '6. Add sock.o and sock.h symlinks:' 1>&2 X echo 'This is self-explanatory.' 1>&2 X echo 'To continue, just make tpat7 > /dev/null.' 1>&2 X ./igntt tiocsti 'ln -s ../../../sock.{h,o} telnet.90.03.01/telnetd' X Xtpat7: igntt X echo '7. Edit the Makefile.' 1>&2 X echo 'You must at least add -DUSEPTY to DEFINES and sock.o to LIBS.' 1>&2 X echo 'telnetd isn'\''t too high on the portability scale, so you'\''ll' 1>&2 X echo 'probably have to make quite a few more changes. For example,' 1>&2 X echo 'on one Sun 4, DEFINES=-DNO_GETTYTAB -DTS_EXTPROC=0400 -DUSEPTY' 1>&2 X echo 'and empty VPATH, GETTYTAB, GETTYSRC were all needed.' 1>&2 X echo 'Anyway, make tpat8 > /dev/null to continue.' 1>&2 X Xtpat8: igntt X echo '8. Compile!' 1>&2 X echo 'When and if this succeeds, go to step 9 of README.' 1>&2 X ./igntt tiocsti '( cd telnet.90.03.01/telnetd; (date;make) >>&Makelog ) &' X Xigntt: igntt.c X cc -o igntt igntt.c END_OF_FILE if test 3062 -ne `wc -c <'patch/Makefile'`; then echo shar: \"'patch/Makefile'\" unpacked with wrong size! fi # end of 'patch/Makefile' fi if test -f 'patch/telnetd.patch' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patch/telnetd.patch'\" else echo shar: Extracting \"'patch/telnetd.patch'\" \(13120 characters\) sed "s/^X//" >'patch/telnetd.patch' <<'END_OF_FILE' X*** ../../telnet.orig/telnetd/pathnames.h Thu Mar 1 11:28:54 1990 X--- pathnames.h Mon Jun 11 15:14:33 1990 X*************** X*** 29,31 **** X--- 29,35 ---- X # define _PATH_LOGIN "/bin/login" X X #endif X+ #ifdef USEPTY X+ X+ #define _PATH_PTY "/usr/local/pty" X+ #endif USEPTY X*** ../../telnet.orig/telnetd/sys_term.c Thu Mar 1 12:37:57 1990 X--- sys_term.c Mon Jun 11 15:24:49 1990 X*************** X*** 22,27 **** X--- 22,32 ---- X #include "telnetd.h" X #include "pathnames.h" X X+ #ifdef USEPTY X+ #include "sock.h" X+ extern char *ttyname(); /* grrrr */ X+ X+ #endif USEPTY X #ifdef NEWINIT X #include <initreq.h> X #else /* NEWINIT*/ X*************** X*** 343,348 **** X--- 348,354 ---- X X getpty() X { X+ #ifndef USEPTY X register int p; X #ifndef CRAY X register char c, *p1, *p2; X*************** X*** 387,392 **** X--- 393,406 ---- X } X #endif /* CRAY */ X return(-1); X+ #else USEPTY X+ /* In this new 'n' improved pty-based telnetd, we've already called */ X+ /* startslave, which in turn waited to grab pseudo-terminals from the */ X+ /* pty program. Global variable pty (grrrr) already has our tty. line */ X+ /* has already been filled in by startslave. In fact, there's no point */ X+ /* in calling getpty() at all. */ X+ return pty; X+ #endif USEPTY X } X X #ifdef LINEMODE X*************** X*** 665,673 **** X--- 679,690 ---- X */ X getptyslave() X { X+ #ifndef USEPTY X register int t = -1; X+ #endif X X #ifndef CRAY X+ #ifndef USEPTY X /* X * Disassociate self from control terminal and open ttyp side. X * Set important flags on ttyp and ptyp. X*************** X*** 690,695 **** X--- 707,715 ---- X if (t < 0) X fatalperror(net, line); X X+ #else USEPTY X+ /* In a pty-based world, this function is rendered similarly useless. */ X+ #endif USEPTY X init_termbuf(); X #ifndef USE_TERMIO X termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; X*************** X*** 707,716 **** X--- 727,742 ---- X #endif X set_termbuf(); X #else /* CRAY */ X+ #ifndef USEPTY X (void) chown(line, 0, 0); X (void) chmod(line, 0600); X+ #endif X #endif /* CRAY */ X+ #ifndef USEPTY X return(t); X+ #else USEPTY X+ /* What's there to return? We've thrown away fdsty. */ X+ #endif USEPTY X } X X #ifdef NEWINIT X*************** X*** 724,737 **** X--- 750,788 ---- X * is necessary to startup the login process on the slave side of the pty. X */ X X+ #ifdef USEPTY X+ int fdpass[2]; X+ int siglerpid; X+ X+ #endif USEPTY X /* ARGSUSED */ X+ #ifndef USEPTY X startslave(t, host) X int t; X+ #else USEPTY X+ startslave(host) X+ #endif USEPTY X char *host; X { X register int i; X long time(); X+ #ifdef USEPTY X+ char strfdpass[10]; X+ char ch; X+ int fdmty; X+ int fdsty; X+ char *ttyn; X+ int pgrp; X+ int fd; X+ #endif USEPTY X X+ #ifdef USEPTY X+ /* Prepare for receiving pseudo-terminal descriptors from pty. */ X+ if (socketpair(AF_UNIX,SOCK_STREAM,0,fdpass) == -1) /* virtually impossible */ X+ fatalperror(net,"pty-socketpair"); X+ (void) sprintf(strfdpass,"%d",fdpass[1]); X+ X+ #endif USEPTY X #ifndef NEWINIT X # ifdef CRAY X utmp_sig_init(); X*************** X*** 740,745 **** X--- 791,799 ---- X if ((i = fork()) < 0) X fatalperror(net, "fork"); X if (i) { X+ # ifdef USEPTY X+ siglerpid = i; X+ # endif X # ifdef CRAY X /* X * Cray parent will create utmp entry for child and send X*************** X*** 768,776 **** X--- 822,869 ---- X } X utmp_sig_notify(pid); X # endif /* CRAY */ X+ #ifndef USEPTY X (void) close(t); X+ #else USEPTY X+ /* Now we'll receive the descriptors. */ X+ (void) close(fdpass[1]); X+ (void) sprintf(line,"/NONexistent"); X+ fdmty = fdsty = -1; X+ if (pty_getch(fdpass[0],&ch) == -1) X+ cleanup(); /* XXX: telnetd's finishup handling really sucks. */ X+ if (pty_putgetint(fdpass[0],'G',&siglerpid) == -1) X+ cleanup(); X+ if (pty_getch(fdpass[0],&ch) == -1) X+ cleanup(); X+ if (pty_putgetfd(fdpass[0],'m',&fdmty) == -1) X+ cleanup(); X+ if (pty_getch(fdpass[0],&ch) == -1) X+ cleanup(); X+ if (pty_putgetfd(fdpass[0],'s',&fdsty) == -1) X+ cleanup(); X+ if (!(ttyn = ttyname(fdsty))) X+ cleanup(); X+ (void) strncpy(line,ttyn,14); X+ if ((fd = open("/dev/tty",O_RDWR)) != -1) X+ { X+ (void) ioctl(fd,TIOCNOTTY,0); /* detach */ X+ (void) close(fd); X+ } X+ (void) close(open(ttyn,O_RDWR)); /* attach ourselves to tty */ X+ pty = fdmty; X+ (void) ioctl(fdsty,TIOCGPGRP,&pgrp); X+ (void) setpgrp(0,pgrp); X+ (void) close(fdsty); /* we won't need it for anything else */ X+ (void) fcntl(fdpass[0],F_SETFL,FNDELAY); /* XXX */ X+ X+ #endif USEPTY X } else { X+ #ifndef USEPTY X start_login(t, host); X+ #else USEPTY X+ (void) close(fdpass[0]); X+ start_login(host,strfdpass); X+ #endif USEPTY X /*NOTREACHED*/ X } X #else /* NEWINIT */ X*************** X*** 836,844 **** X--- 929,947 ---- X * function will turn us into the login process. X */ X X+ #ifndef USEPTY X start_login(t, host) X int t; X+ #else USEPTY X+ /* Added parameter, for dealing with pty: strfdpass, a printable version */ X+ /* of the file descriptor for passing more descriptors up to us. */ X+ X+ start_login(host,strfdpass) X+ #endif USEPTY X char *host; X+ #ifdef USEPTY X+ char *strfdpass; X+ #endif USEPTY X { X extern char *getenv(); X char **envp; X*************** X*** 845,850 **** X--- 948,954 ---- X X #ifdef CRAY X utmp_sig_wait(); X+ #ifndef USEPTY X # ifndef TCVHUP X setpgrp(); X # endif X*************** X*** 877,887 **** X--- 981,993 ---- X termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; X termbuf.c_cflag = EXTB|HUPCL|CS8; X set_termbuf(); X+ #endif USEPTY X #endif /* CRAY */ X X /* X * set up standard paths before forking to login X */ X+ #ifndef USEPTY X #if BSD >43 X if (setsid() < 0) X fatalperror(net, "setsid"); X*************** X*** 888,899 **** X--- 994,1016 ---- X if (ioctl(t, TIOCSCTTY, (char *)0) < 0) X fatalperror(net, "ioctl(sctty)"); X #endif X+ #else USEPTY X+ X+ /* The POSIXish setsid() handling that was here is pty's responsibility. */ X+ #endif USEPTY X (void) close(net); X (void) close(pty); X+ #ifndef USEPTY X (void) dup2(t, 0); X (void) dup2(t, 1); X (void) dup2(t, 2); X (void) close(t); X+ #else USEPTY X+ (void) close(0); /* ahhh, life is easy */ X+ (void) close(1); X+ (void) close(2); X+ /* but absolutely don't close fdpass. */ X+ #endif USEPTY X envp = envinit; X *envp++ = terminaltype; X if (*envp = getenv("TZ")) X*************** X*** 910,916 **** X--- 1027,1038 ---- X * getuid() == 0. X * -p : don't clobber the environment (so terminal type stays set). X */ X+ #ifndef USEPTY X execl(_PATH_LOGIN, "login", "-h", host, X+ #else USEPTY X+ execl(_PATH_PTY, "pty", "-sdf", strfdpass, "-xCURWX", X+ _PATH_LOGIN, "-h", host, X+ #endif USEPTY X #ifndef CRAY X terminaltype ? "-p" : 0, X #endif X*************** X*** 937,950 **** X--- 1059,1084 ---- X p = line + sizeof("/dev/") - 1; X if (logout(p)) X logwtmp(p, "", ""); X+ #ifndef USEPTY X (void)chmod(line, 0666); X+ #else USEPTY X+ (void)chmod(line, 0600); /* banish these security holes! */ X+ #endif USEPTY X (void)chown(line, 0, 0); X *p = 'p'; X+ #ifndef USEPTY X (void)chmod(line, 0666); X+ #else USEPTY X+ (void)chmod(line, 0600); X+ #endif USEPTY X (void)chown(line, 0, 0); X # else X rmut(); X+ #ifndef USEPTY X vhangup(); /* XXX */ X+ #else X+ /* vhangup(); XXX: This is also pty's responsibility. */ X+ #endif USEPTY X # endif X (void) shutdown(net, 2); X #else /* CRAY */ X*************** X*** 951,957 **** X--- 1085,1096 ---- X # ifndef NEWINIT X rmut(line); X (void) shutdown(net, 2); X+ #ifndef USEPTY X kill(0, SIGHUP); X+ #else USEPTY X+ /* kill(0, SIGHUP); The pty master makes this decision. */ X+ (void) kill(siglerpid,SIGHUP); /* but we do have to tell the sigler. */ X+ #endif USEPTY X # else /* NEWINIT */ X (void) shutdown(net, 2); X sleep(2); X*************** X*** 1064,1073 **** X--- 1203,1220 ---- X (void) close(f); X } X } X+ #ifndef USEPTY X (void) chmod(line, 0666); X+ #else USEPTY X+ (void) chmod(line, 0600); X+ #endif USEPTY X (void) chown(line, 0, 0); X line[strlen("/dev/")] = 'p'; X+ #ifndef USEPTY X (void) chmod(line, 0666); X+ #else USEPTY X+ (void) chmod(line, 0600); X+ #endif USEPTY X (void) chown(line, 0, 0); X } /* end of rmut */ X #endif /* CRAY */ X*** ../../telnet.orig/telnetd/telnetd.c Thu Mar 1 11:30:31 1990 X--- telnetd.c Mon Jun 11 15:12:24 1990 X*************** X*** 27,32 **** X--- 27,37 ---- X X #include "telnetd.h" X X+ #ifdef USEPTY X+ #include "sock.h" X+ extern char *ttyname(); X+ X+ #endif USEPTY X /* X * I/O data buffers, X * pointers, and counters. X*************** X*** 346,351 **** X--- 351,357 ---- X int t; X struct hostent *hp; X X+ #ifndef USEPTY X /* X * Find an available pty to use. X */ X*************** X*** 355,360 **** X--- 361,367 ---- X X t = getptyslave(); X X+ #endif USEPTY X /* get name of connected client */ X hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), X who->sin_family); X*************** X*** 373,384 **** X--- 380,418 ---- X /* X * Start up the login process on the slave side of the terminal X */ X+ #ifndef USEPTY X startslave(t, host); X+ #else USEPTY X+ startslave(host); X+ #endif USEPTY X X+ #ifdef USEPTY X+ /* In this pty-based version of the world, life is easy. startslave() */ X+ /* actually dumps its process under the pty program, which handles all */ X+ /* those nasty details of finding a pty and getting it started. pty */ X+ /* then passes its pty master side up to telnetd (the ``controller'') */ X+ /* so that we have full control. Joy to the world. */ X+ X+ /* X+ * Find an available pty to use. X+ */ X+ pty = getpty(); /* noop */ X+ if (pty < 0) /* impossible */ X+ fatal(net, "All network ports in use"); X+ X+ getptyslave(); X+ X+ #endif USEPTY X telnet(net, pty); /* begin server processing */ X /*NOTREACHED*/ X } /* end of doit */ X X+ #ifdef USEPTY X+ extern int fdpass[2]; X+ extern int siglerpid; X+ char ch; X+ X+ #endif USEPTY X #ifndef MAXHOSTNAMELEN X #define MAXHOSTNAMELEN 64 X #endif MAXHOSTNAMELEN X*************** X*** 626,631 **** X--- 660,671 ---- X if (!SYNCHing) { X FD_SET(f, &xbits); X } X+ #ifdef USEPTY X+ FD_SET(fdpass[0],&ibits); X+ /* When we get input on fdpass, we're switching ttys. */ X+ /* This FD_SET seems logical, but it invites bugs under many */ X+ /* implementations. XXX. */ X+ #endif USEPTY X if ((c = select(16, &ibits, &obits, &xbits, X (struct timeval *)0)) < 1) { X if (c == -1) { X*************** X*** 637,642 **** X--- 677,749 ---- X continue; X } X X+ #ifdef USEPTY X+ if (pty_getch(fdpass[0],&ch) != -1) X+ { X+ /* reconnect... */ X+ int fdmty; X+ int fdsty; X+ char *ttyn; X+ int pgrp; X+ int fd; X+ X+ fdmty = fdsty = -1; X+ (void) fcntl(fdpass[0],F_SETFL,0); /*XXX*/ X+ if (ch != 'G') X+ if (pty_getch(fdpass[0],&ch) == -1) X+ ; X+ if (pty_putgetint(fdpass[0],'G',&siglerpid) == -1) X+ cleanup(); X+ if (pty_getch(fdpass[0],&ch) == -1) X+ cleanup(); X+ if (pty_putgetfd(fdpass[0],'m',&fdmty) == -1) X+ cleanup(); X+ if (pty_getch(fdpass[0],&ch) == -1) X+ cleanup(); X+ if (pty_putgetfd(fdpass[0],'s',&fdsty) == -1) X+ cleanup(); X+ if (!(ttyn = ttyname(fdsty))) X+ cleanup(); X+ /* It's impossible to do anything sensible with utmp; */ X+ /* this shows very clearly why utmp's remote field */ X+ /* is inconsistent. So we stick to the same ``line.'' */ X+ X+ fd = 0; X+ (void) ioctl(p, TIOCPKT, (char *)&fd); X+ /* Packet mode, like NDELAY, is not a polite thing to */ X+ /* give someone else. One of those unstated conventions */ X+ /* that people should learn... */ X+ X+ if ((fd = open("/dev/tty",O_RDWR)) != -1) X+ { X+ (void) ioctl(fd,TIOCNOTTY,0); /* detach */ X+ (void) close(fd); X+ } X+ (void) close(open(ttyn,O_RDWR)); /* attach */ X+ X+ (void) close(pty); X+ pty = fdmty; X+ p = pty; /* XXX: Blame DAB for this. Whither modularity? */ X+ X+ (void) ioctl(p, TIOCPKT, (char *)&on); X+ /* If we didn't do this, telnetd would become very */ X+ /* confused, because it's expecting packet-mode stuff. */ X+ X+ (void) fcntl(fdpass[0],F_SETFL,FNDELAY); /* XXX */ X+ (void) ioctl(fdsty,TIOCGPGRP,&pgrp); X+ (void) setpgrp(0,pgrp); X+ X+ /* XXX: If this messes up Linemode, TOUGH LUCK! */ X+ init_termbuf(); /* XXX */ X+ localstat(); /* XXX */ X+ X+ FD_ZERO(&xbits); X+ FD_ZERO(&ibits); /* XXX */ X+ FD_ZERO(&obits); X+ X+ /* XXX: anything else to do? */ X+ } X+ #endif USEPTY X /* X * Any urgent data? X */ X*************** X*** 723,730 **** X--- 830,845 ---- X if (pcc < 0 && errno == EWOULDBLOCK) X pcc = 0; X else { X+ #ifndef USEPTY X if (pcc <= 0) X break; X+ #else USEPTY X+ if (pcc <= 0) X+ { X+ pcc = 0; X+ goto wtfiseofpty; X+ } X+ #endif USEPTY X #if !defined(CRAY2) || !defined(UNICOS5) X #ifdef LINEMODE X /* X*************** X*** 774,779 **** X--- 889,897 ---- X ptyip = ptyibuf; X #endif /* defined(CRAY2) && defined(UNICOS5) */ X } X+ #ifdef USEPTY X+ wtfiseofpty: ; X+ #endif X } X X while (pcc > 0) { END_OF_FILE if test 13120 -ne `wc -c <'patch/telnetd.patch'`; then echo shar: \"'patch/telnetd.patch'\" unpacked with wrong size! fi # end of 'patch/telnetd.patch' fi if test -f 'patch/igntt.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patch/igntt.c'\" else echo shar: Extracting \"'patch/igntt.c'\" \(285 characters\) sed "s/^X//" >'patch/igntt.c' <<'END_OF_FILE' X#include <signal.h> X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X switch (fork()) X { X case -1: exit(1); X case 0: break; X default: exit(0); X } X (void) signal(SIGTTOU,SIG_IGN); X (void) signal(SIGTTIN,SIG_IGN); X (void) sleep(1); X (void) execvp(argv[1],argv + 1); X (void) exit(1); X} END_OF_FILE if test 285 -ne `wc -c <'patch/igntt.c'`; then echo shar: \"'patch/igntt.c'\" unpacked with wrong size! fi # end of 'patch/igntt.c' fi if test ! -d 'util' ; then echo shar: Creating directory \"'util'\" mkdir 'util' fi if test -f 'util/Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/Makefile'\" else echo shar: Extracting \"'util/Makefile'\" \(4658 characters\) sed "s/^X//" >'util/Makefile' <<'END_OF_FILE' XCC=cc XCCOPTS=-O2 -s X XNROFF=nroff XNROFFOPTS=-man X X# This Makefile is exceedingly boring. Then again, it's portable. X Xdefault: all X Xall: progs mans X Xprogs: excloff exclon lock tiocsti who u biff mesg tty write wall sessuser sessname sesskill sesslist disconnect reconnect xsessuser xsessname xsesskill xsesslist xdisconnect xreconnect sessutil.o xsessutil.o X Xmans: excloff.man exclon.man lock.man tiocsti.man who.man u.man biff.man mesg.man tty.man write.man wall.man sessuser.man sessname.man sesskill.man sesslist.man disconnect.man reconnect.man script.man script.tidy.man condom.man sess.man X Xexcloff: excloff.c Makefile X cc $(CCOPTS) -o excloff excloff.c X Xexclon: exclon.c Makefile X cc $(CCOPTS) -o exclon exclon.c X Xlock: lock.c Makefile X cc $(CCOPTS) -o lock lock.c -lcurses -ltermcap X Xtiocsti: tiocsti.c Makefile X cc $(CCOPTS) -o tiocsti tiocsti.c X Xwho: who.c Makefile X cc $(CCOPTS) -o who who.c X Xu: u.c Makefile X cc $(CCOPTS) -o u u.c X Xtty: tty.c Makefile X cc $(CCOPTS) -o tty tty.c X Xwrite: write.c Makefile X cc $(CCOPTS) -o write write.c X Xwall: wall.c Makefile X cc $(CCOPTS) -o wall wall.c X Xbiff: biff.o sessutil.o Makefile X cc $(CCOPTS) -o biff biff.o sessutil.o X Xmesg: mesg.o sessutil.o Makefile X cc $(CCOPTS) -o mesg mesg.o sessutil.o X Xsessuser: sessuser.o sessutil.o Makefile X cc $(CCOPTS) -o sessuser sessuser.o sessutil.o X Xxsessuser: sessuser.o xsessutil.o Makefile X cc $(CCOPTS) -o xsessuser sessuser.o xsessutil.o X Xsessname: sessname.o sessutil.o Makefile X cc $(CCOPTS) -o sessname sessname.o sessutil.o X Xxsessname: sessname.o xsessutil.o Makefile X cc $(CCOPTS) -o xsessname sessname.o xsessutil.o X Xsesskill: sesskill.o sessutil.o Makefile X cc $(CCOPTS) -o sesskill sesskill.o sessutil.o X Xxsesskill: sesskill.o xsessutil.o Makefile X cc $(CCOPTS) -o xsesskill sesskill.o xsessutil.o X Xsesslist: sesslist.o sessutil.o Makefile X cc $(CCOPTS) -o sesslist sesslist.o sessutil.o X Xxsesslist: sesslist.o xsessutil.o Makefile X cc $(CCOPTS) -o xsesslist sesslist.o xsessutil.o X Xreconnect: reconnect.o sessutil.o Makefile X cc $(CCOPTS) -o reconnect reconnect.o sessutil.o X Xxreconnect: reconnect.o xsessutil.o Makefile X cc $(CCOPTS) -o xreconnect reconnect.o xsessutil.o X Xdisconnect: disconnect.o sessutil.o Makefile X cc $(CCOPTS) -o disconnect disconnect.o sessutil.o X Xxdisconnect: disconnect.o xsessutil.o Makefile X cc $(CCOPTS) -o xdisconnect disconnect.o xsessutil.o X Xbiff.o: biff.c sessutil.h Makefile X cc $(CCOPTS) -c biff.c X Xmesg.o: mesg.c sessutil.h Makefile X cc $(CCOPTS) -c mesg.c X Xsessuser.o: sessuser.c sessutil.h Makefile X cc $(CCOPTS) -c sessuser.c X Xsessname.o: sessname.c sessutil.h Makefile X cc $(CCOPTS) -c sessname.c X Xsesskill.o: sesskill.c sessutil.h Makefile X cc $(CCOPTS) -c sesskill.c X Xsesslist.o: sesslist.c sessutil.h Makefile X cc $(CCOPTS) -c sesslist.c X Xdisconnect.o: disconnect.c sessutil.h Makefile X cc $(CCOPTS) -c disconnect.c X Xreconnect.o: reconnect.c sessutil.h Makefile X cc $(CCOPTS) -c reconnect.c X Xxsessutil.o: xsessutil.c sessutil.h Makefile X cc $(CCOPTS) -c xsessutil.c X Xsessutil.o: sessutil.c sessutil.h Makefile X cc $(CCOPTS) -c sessutil.c X Xexcloff.man: excloff.1 Makefile X nroff $(NROFFOPTS) < excloff.1 > excloff.man X Xexclon.man: excloff.1 Makefile X nroff $(NROFFOPTS) < excloff.1 > exclon.man X Xlock.man: lock.1 Makefile X nroff $(NROFFOPTS) < lock.1 > lock.man X Xtiocsti.man: tiocsti.1 Makefile X nroff $(NROFFOPTS) < tiocsti.1 > tiocsti.man X Xwho.man: who.1 Makefile X nroff $(NROFFOPTS) < who.1 > who.man X Xbiff.man: biff.1 Makefile X nroff $(NROFFOPTS) < biff.1 > biff.man X Xmesg.man: mesg.1 Makefile X nroff $(NROFFOPTS) < mesg.1 > mesg.man X Xu.man: u.1 Makefile X nroff $(NROFFOPTS) < u.1 > u.man X Xtty.man: tty.1 Makefile X nroff $(NROFFOPTS) < tty.1 > tty.man X Xwrite.man: write.1 Makefile X nroff $(NROFFOPTS) < write.1 > write.man X Xwall.man: wall.1 Makefile X nroff $(NROFFOPTS) < wall.1 > wall.man X Xsess.man: sess.1 Makefile X nroff $(NROFFOPTS) < sess.1 > sess.man X Xsessuser.man: sessuser.1 Makefile X nroff $(NROFFOPTS) < sessuser.1 > sessuser.man X Xsessname.man: sessname.1 Makefile X nroff $(NROFFOPTS) < sessname.1 > sessname.man X Xsesskill.man: sesskill.1 Makefile X nroff $(NROFFOPTS) < sesskill.1 > sesskill.man X Xsesslist.man: sesslist.1 Makefile X nroff $(NROFFOPTS) < sesslist.1 > sesslist.man X Xcondom.man: condom.1 Makefile X nroff $(NROFFOPTS) < condom.1 > condom.man X Xscript.man: script.1 Makefile X nroff $(NROFFOPTS) < script.1 > script.man X Xscript.tidy.man: script.tidy.1 Makefile X nroff $(NROFFOPTS) < script.tidy.1 > script.tidy.man X Xreconnect.man: reconnect.1 Makefile X nroff $(NROFFOPTS) < reconnect.1 > reconnect.man X Xdisconnect.man: disconnect.1 Makefile X nroff $(NROFFOPTS) < disconnect.1 > disconnect.man END_OF_FILE if test 4658 -ne `wc -c <'util/Makefile'`; then echo shar: \"'util/Makefile'\" unpacked with wrong size! fi # end of 'util/Makefile' fi if test -f 'util/biff.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/biff.1'\" else echo shar: Extracting \"'util/biff.1'\" \(897 characters\) sed "s/^X//" >'util/biff.1' <<'END_OF_FILE' X.TH biff 1 X.SH NAME Xbiff \- be notified if mail arrives and who it is from X.SH SYNOPSIS X.B biff X[ X.B n X] [ X.B y X] X.SH DESCRIPTION XAfter X.I biff y, Xthe system will display Xthe top of incoming mail on your screen, Xas described in X.I comsat(8). X.I biff n Xturns this off. X.I biff Xapplies to your current terminal session; Xtypically you'd put a X.I biff y Xinto your X.I \&.login, X.I \&.cshrc, Xor X.I \&.profile. X.PP X.I biff Xwithout an argument tells you your current X``biffing'' status. X.PP X.I biff Xoperates asynchronously. XFor synchronous notification use the MAIL variable of X.IR sh (1) Xor the X.I mail Xvariable of X.IR csh (1). X.PP X.I biff Xrequires its input to be your terminal session X(or at least a session you own). All it really Xdoes is handle the owner-execute bit on the terminal. X.PP X.I biff Xonly looks at the first letter of its first argument. X.SH "SEE ALSO" Xcsh(1), Xsh(1), Xmail(1), Xcomsat(8C) END_OF_FILE if test 897 -ne `wc -c <'util/biff.1'`; then echo shar: \"'util/biff.1'\" unpacked with wrong size! fi # end of 'util/biff.1' fi if test -f 'util/biff.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/biff.c'\" else echo shar: Extracting \"'util/biff.c'\" \(1038 characters\) sed "s/^X//" >'util/biff.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include "sessutil.h" Xextern unsigned short getuid(); X X#define FOO (void) fstat(0,&st); \ Xif (which) (void) fchmod(0,(int) (st.st_mode | 0100)); \ Xelse (void) fchmod(0,(int) (st.st_mode & ~0100)); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X struct stat st; X struct pty_session ps; X int uid; X int which; X X uid = getuid(); X X if (argc == 1) X { X (void) setreuid(uid,uid); X (void) fstat(0,&st); X if (st.st_mode & 0100) X (void) printf("is y\n"); X else X (void) printf("is n\n"); X } X else X { X switch(argv[1][0]) X { X case 'y': which = 1; break; X case 'n': which = 0; break; X default: (void) fprintf(stderr,"usage: biff [y] [n]\n"); exit(1); X } X if (pty_get_sess(0,uid,&ps) == -1) X { X (void) setreuid(uid,uid); X FOO X } X else X if (ps.uid != uid) X (void) fprintf(stderr,"not your session\n"); X else X { X FOO X (void) setreuid(uid,uid); X FOO X } X } X (void) exit(0); X} END_OF_FILE if test 1038 -ne `wc -c <'util/biff.c'`; then echo shar: \"'util/biff.c'\" unpacked with wrong size! fi # end of 'util/biff.c' fi if test -f 'util/condom' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/condom'\" else echo shar: Extracting \"'util/condom'\" \(44 characters\) sed "s/^X//" >'util/condom' <<'END_OF_FILE' X#!/bin/sh X# Public domain. Xexec pty -0 "$@" END_OF_FILE if test 44 -ne `wc -c <'util/condom'`; then echo shar: \"'util/condom'\" unpacked with wrong size! fi chmod +x 'util/condom' # end of 'util/condom' fi if test -f 'util/condom.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/condom.1'\" else echo shar: Extracting \"'util/condom.1'\" \(1176 characters\) sed "s/^X//" >'util/condom.1' <<'END_OF_FILE' X.TH condom 1 X.SH NAME Xcondom \- wrap a pseudo-terminal around a program X.SH SYNOPSIS X.B condom X.I program X.B [ X.I arg ... X.B ] X.SH DESCRIPTION X.I condom, Xwhich is really just X.I pty -0, Xruns a program under a pseudo-terminal Xwith as little interpretation as possible. XThe following are the most noticeable effects of X.I condom: X.PP X1. The X.I stdio(3) Xroutines Xwill, by default, Xline-buffer their output rather than Xwaiting for a big block of output Xto accumulate. X.PP X2. X.I condom Xalways exits with exit code 0, Xprovided nothing unusual happens. X.PP X3. XThe original terminal Xis always in line-by-line, Xecho mode (or whatever mode it is Xin originally). X.PP X4. XEnd-of-file Xis ignored in the input. X(This is an inherent and unfortunate restriction Xof the current pseudo-terminal design, and may not Xbe true under other versions of X.I pty.) XIf the input really does end, X.I pty Xwill just sit around wasting CPU time. X.PP X5. XIf X.I program Xrefers to /dev/tty, Xit will see the pseudo-terminal X(i.e., its input and output) Xrather than its original terminal. X.PP X6. XVarious signals Xto X.I condom Xwill be forwarded to X.I program Xas HUPs instead. X.SH "SEE ALSO" Xpty(1), Xsetbuf(3), Xpty(4) END_OF_FILE if test 1176 -ne `wc -c <'util/condom.1'`; then echo shar: \"'util/condom.1'\" unpacked with wrong size! fi # end of 'util/condom.1' fi if test -f 'util/disconnect.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/disconnect.1'\" else echo shar: Extracting \"'util/disconnect.1'\" \(1184 characters\) sed "s/^X//" >'util/disconnect.1' <<'END_OF_FILE' X.TH disconnect 1 X.SH NAME Xdisconnect \- disconnect current pty session X.SH SYNOPSIS X.B disconnect X.SH DESCRIPTION X.I disconnect Xdisconnects the current Xsession, Xas if the connection Xhad been hung up. XLater you can use X.I reconnect Xto get back to the session Xexactly where you left off. X.PP XBecause X.I disconnect Xdoesn't actually hang up the connection, Xbut merely severs its association with Xthe current session, Xa X.I reconnect Xon that connection will take effect Ximmediately. XThis leads to a useful trick: Xyou can switch rapidly from session X.I xx Xto disconnected session X.I yy Xby typing X.EX Xreconnect yy;disconnect X.EE XThen X.I xx Xand X.I yy Xwill be reversed, Xand you can switch back and forth without trouble. X.PP X.I xdisconnect Xworks just like X.I disconnect Xbut applies to session started with X.B\-xS. X.SH DIAGNOSTICS X.TP X.I cannot find session X.TP X.I you don't own session XThe standard input to X.I reconnect Xis not a pty session you own. X.TP X.I not child of session slave XThe parent to X.I reconnect Xis not the head process in the session. X.TP X.I cannot disconnect X.I reconnect Xis unable to communicate with Xthe Xpseudo-terminal Xmanager. X.SH "SEE ALSO" Xpty(1), Xsess(1), Xreconnect(1) END_OF_FILE if test 1184 -ne `wc -c <'util/disconnect.1'`; then echo shar: \"'util/disconnect.1'\" unpacked with wrong size! fi # end of 'util/disconnect.1' fi if test -f 'util/disconnect.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/disconnect.c'\" else echo shar: Extracting \"'util/disconnect.c'\" \(694 characters\) sed "s/^X//" >'util/disconnect.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include "sessutil.h" Xextern unsigned short getuid(); X Xmain() X{ X struct pty_session ps; X int uid; X X uid = getuid(); X X if (pty_get_sess(0,uid,&ps) == -1) X { X (void) fputs("sessname: fatal: cannot find session\n",stderr); X (void) exit(1); X } X if (ps.uid != uid) X { X (void) fputs("sessname: fatal: you don't own session\n",stderr); X (void) exit(1); X } X if (ps.slavepid != getppid()) X { X (void) fputs("sessname: fatal: not child of session slave\n",stderr); X (void) exit(1); X } X if (kill(ps.siglerpid,SIGUSR1) == -1) X { X (void) perror("sessname: fatal: cannot disconnect"); X (void) exit(1); X } X (void) exit(0); X} END_OF_FILE if test 694 -ne `wc -c <'util/disconnect.c'`; then echo shar: \"'util/disconnect.c'\" unpacked with wrong size! fi # end of 'util/disconnect.c' fi if test -f 'util/excloff.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/excloff.1'\" else echo shar: Extracting \"'util/excloff.1'\" \(692 characters\) sed "s/^X//" >'util/excloff.1' <<'END_OF_FILE' X.TH excl 1 X.SH NAME Xexclon, excloff \- reserve tty for exclusive use X.SH SYNOPSIS X.B exclon X.PP X.B excloff X.SH DESCRIPTION XThe X.B exclon Xcommand reserves your terminal for exclusive use with Xthe TIOCEXCL ioctl. XThis means that any process attempting to open your terminal Xwill fail, including other users attempting to write or cat, Xand including reference to /dev/tty by yourself. X.PP XTalk is not affected at all by exclon. X.PP XTo unreserve your terminal, use excloff. XIt is polite to do this at the end of a session, Xthough it is automatic when the terminal is closed. X.SH AUTHOR XI hesitate to claim authorship for a one-statement program. X.SH "SEE ALSO" Xtalk(1), stty(1), ioctl(2), tty(4) END_OF_FILE if test 692 -ne `wc -c <'util/excloff.1'`; then echo shar: \"'util/excloff.1'\" unpacked with wrong size! fi # end of 'util/excloff.1' fi if test -f 'util/excloff.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/excloff.c'\" else echo shar: Extracting \"'util/excloff.c'\" \(110 characters\) sed "s/^X//" >'util/excloff.c' <<'END_OF_FILE' X/* Public domain. */ X#include <sys/ioctl.h> X Xmain() X{ X (void) ioctl(1,(unsigned long) TIOCNXCL,(char *) 0); X} END_OF_FILE if test 110 -ne `wc -c <'util/excloff.c'`; then echo shar: \"'util/excloff.c'\" unpacked with wrong size! fi # end of 'util/excloff.c' fi if test -f 'util/exclon.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/exclon.1'\" else echo shar: Extracting \"'util/exclon.1'\" \(18 characters\) sed "s/^X//" >'util/exclon.1' <<'END_OF_FILE' X.so man1/exclon.1 END_OF_FILE if test 18 -ne `wc -c <'util/exclon.1'`; then echo shar: \"'util/exclon.1'\" unpacked with wrong size! fi # end of 'util/exclon.1' fi if test -f 'util/exclon.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/exclon.c'\" else echo shar: Extracting \"'util/exclon.c'\" \(110 characters\) sed "s/^X//" >'util/exclon.c' <<'END_OF_FILE' X/* Public domain. */ X#include <sys/ioctl.h> X Xmain() X{ X (void) ioctl(1,(unsigned long) TIOCEXCL,(char *) 0); X} END_OF_FILE if test 110 -ne `wc -c <'util/exclon.c'`; then echo shar: \"'util/exclon.c'\" unpacked with wrong size! fi # end of 'util/exclon.c' fi if test -f 'util/lock.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/lock.1'\" else echo shar: Extracting \"'util/lock.1'\" \(485 characters\) sed "s/^X//" >'util/lock.1' <<'END_OF_FILE' X.TH lock 1 X.SH NAME Xlock \- reserve a terminal X.SH SYNOPSIS X.B lock X.SH DESCRIPTION X.I lock Xrequests a password from the user, reads it again for verification, Xand then sits around doing nothing until the password is typed a third Xtime. X.PP XThis clone version of X.I lock Xcorrects several of the original's failings, by being much simpler: Xit doesn't accept X.I hasta la vista; Xit doesn't accept Xthe root password; Xit doesn't time out; Xand it does print a message for each bad password. END_OF_FILE if test 485 -ne `wc -c <'util/lock.1'`; then echo shar: \"'util/lock.1'\" unpacked with wrong size! fi # end of 'util/lock.1' fi if test -f 'util/lock.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/lock.c'\" else echo shar: Extracting \"'util/lock.c'\" \(852 characters\) sed "s/^X//" >'util/lock.c' <<'END_OF_FILE' X/* Public domain. */ X#include <curses.h> X#include <signal.h> X#include <strings.h> X Xmain() X{ X char key[100]; X char key2[100]; X struct sigvec vec; X int i; X X vec.sv_handler = SIG_IGN; X vec.sv_mask = 0; X for (i = 31;i > 0;i--) X (void) sigvec(i,&vec,(struct sigvec *) 0); X (void) savetty(); X (void) crmode(); X (void) noecho(); X (void) printf("Key: "); X if (fgets(key,sizeof(key) - 2,stdin)) X { X (void) printf("\nAgain: "); X if (fgets(key2,sizeof(key2) - 2,stdin)) X { X if (!strcmp(key,key2)) X { X (void) printf("\n"); X while ((fgets(key2,sizeof(key2),stdin) == NULL) || strcmp(key,key2)) X { X (void) printf("Bad password!\n"); X (void) sleep(1); X } X } X else (void) printf("\n"); X } X else (void) printf("\n"); X } X else (void) printf("\n"); X (void) resetty(); X (void) exit(0); X /*NOTREACHED*/ X} END_OF_FILE echo shar: 20 control characters may be missing from \"'util/lock.c'\" if test 852 -ne `wc -c <'util/lock.c'`; then echo shar: \"'util/lock.c'\" unpacked with wrong size! fi # end of 'util/lock.c' fi if test -f 'util/mesg.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/mesg.1'\" else echo shar: Extracting \"'util/mesg.1'\" \(814 characters\) sed "s/^X//" >'util/mesg.1' <<'END_OF_FILE' X.TH mesg 1 X.SH NAME Xmesg \- permit or deny messages X.SH SYNOPSIS X.B mesg X[ X.B n X] [ X.B y X] X.SH DESCRIPTION XAfter X.I mesg y, Xother users can Xuse X.I write(1) Xor X.I talk(1) Xto send messages to your terminal. X.I mesg n Xturns this off. X.I mesg Xapplies to your current terminal session; Xtypically you'd put a X.I mesg y Xinto your X.I \&.login, X.I \&.cshrc, Xor X.I \&.profile. X.PP X.I mesg Xwithout an argument tells you your current Xmessaging status. X.PP X.I mesg Xrequires its input to be your terminal session X(or at least a session you own). All it really Xdoes is handle the group-write bit on the terminal. X.PP X.I mesg Xonly looks at the first letter of its first argument. X.SH "EXIT VALUE" XWithout an argument, X.I mesg Xexits with value 0 if messages are receivable, X1 if not, X2 upon error. X.SH "SEE ALSO" Xwrite(1), talk(1) END_OF_FILE if test 814 -ne `wc -c <'util/mesg.1'`; then echo shar: \"'util/mesg.1'\" unpacked with wrong size! fi # end of 'util/mesg.1' fi if test -f 'util/mesg.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/mesg.c'\" else echo shar: Extracting \"'util/mesg.c'\" \(1079 characters\) sed "s/^X//" >'util/mesg.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include "sessutil.h" Xextern unsigned short getuid(); X X#define FOO (void) fstat(0,&st); \ Xif (which) (void) fchmod(0,(int) (st.st_mode | 0020)); \ Xelse (void) fchmod(0,(int) (st.st_mode & ~0020)); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X struct stat st; X struct pty_session ps; X int uid; X int which; X X uid = getuid(); X X if (argc == 1) X { X (void) setreuid(uid,uid); X (void) fstat(0,&st); X if (which = st.st_mode & 0020) X (void) printf("is y\n"); X else X (void) printf("is n\n"); X (void) exit(1 - which); X } X else X { X switch(argv[1][0]) X { X case 'y': which = 1; break; X case 'n': which = 0; break; X default: (void) fprintf(stderr,"mesg: usage: mesg [y] [n]\n"); exit(1); X } X if (pty_get_sess(0,uid,&ps) == -1) X { X (void) setreuid(uid,uid); X FOO X } X else X if (ps.uid != uid) X (void) fprintf(stderr,"not your session\n"); X else X { X FOO X (void) setreuid(uid,uid); X FOO X } X } X (void) exit(0); X} END_OF_FILE if test 1079 -ne `wc -c <'util/mesg.c'`; then echo shar: \"'util/mesg.c'\" unpacked with wrong size! fi # end of 'util/mesg.c' fi if test -f 'util/reconnect.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/reconnect.1'\" else echo shar: Extracting \"'util/reconnect.1'\" \(1604 characters\) sed "s/^X//" >'util/reconnect.1' <<'END_OF_FILE' X.TH reconnect 1 X.SH NAME Xreconnect \- reconnect to a pty session X.SH SYNOPSIS X.B reconnect X.I sn X.SH DESCRIPTION X.I reconnect X.I sn Xmeans X``When this session finishes or is disconnected, Xreconnect to the session named X.I sn.'' XThis lets you get back to a Xsession abandoned by X.I disconnect Xor by a previously hung up connection. X.PP XIf you are not currently Xunder a X.I pty Xsession, Xor if you would like to reconnect Ximmediately without waiting for Xthe current session to finish, Xyou can use X.EX Xsess reconnect xx X.EE X.PP XIf you leave out Xa session name, Xthe connection will simply drop Xwhen its association with the Xcurrent session is severed. XThis is the normal behavior. X.PP XA X.I reconnect Xreplaces each that came before it. X.PP XOn systems Xwhere X.I pty Xuses Xfile descriptor passing, Xa reconnected session is Xhandled just as efficiently as Xa session connected for the first time. X.PP X.I xreconnect Xworks just like X.I reconnect Xbut handles sessions Xstarted with X.B\-xS. X.SH DIAGNOSTICS X.TP X.I cannot find session X.TP X.I you don't own session XThe standard input to X.I reconnect Xis not a pty session you own. X.TP X.I cannot find reconnector X.I sn Xis not the name of a currently disconnected session Xthat you own. X.TP X.I cannot set reconnector X.TP X.I cannot unset reconnector X.I reconnect Xis unable to change this connection's Xreconnection status. X.TP X.I xx set, will reconnect after yy drops X.TP X.I xx unset, won't reconnect after yy drops XSuccess: you are using session X.I yy, Xand will reconnect to session X.I xx X(or to no session at all) Xwhen this one drops. X.SH "SEE ALSO" Xpty(1), Xsess(1), Xdisconnect(1) END_OF_FILE if test 1604 -ne `wc -c <'util/reconnect.1'`; then echo shar: \"'util/reconnect.1'\" unpacked with wrong size! fi # end of 'util/reconnect.1' fi if test -f 'util/reconnect.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/reconnect.c'\" else echo shar: Extracting \"'util/reconnect.c'\" \(1244 characters\) sed "s/^X//" >'util/reconnect.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include <errno.h> Xextern int errno; X#include "sessutil.h" Xextern unsigned short getuid(); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X struct pty_session ps; X int uid; X X uid = getuid(); X X if (pty_get_sess(0,uid,&ps) == -1) X { X (void) fputs("reconnect: fatal: cannot find session\n",stderr); X (void) exit(1); X } X if (ps.uid != uid) X { X (void) fputs("reconnect: fatal: you don't own session\n",stderr); X (void) exit(1); X } X if (argv[1]) X if (pty_get_rebyext(argv[1][0],argv[1][1],uid) == -1) X { X (void) fputs("reconnect: fatal: cannot find reconnector\n",stderr); X (void) exit(1); X } X if (argv[1]) X { X if (pty_set_sig(argv[1][0],argv[1][1],uid,&ps) == -1) X { X (void) fputs("reconnect: fatal: cannot set reconnector\n",stderr); X (void) exit(1); X } X (void) printf("reconnect: %c%c set, will reconnect after %c%c drops\n", X argv[1][0],argv[1][1],ps.ext1,ps.ext2); X } X else X { X if (pty_unset_sig(uid,&ps) == -1) X { X (void) fputs("reconnect: fatal: cannot unset reconnector\n",stderr); X (void) exit(1); X } X (void) printf("reconnect: unset, won't reconnect after %c%c drops\n", X ps.ext1,ps.ext2); X } X (void) exit(0); X} END_OF_FILE if test 1244 -ne `wc -c <'util/reconnect.c'`; then echo shar: \"'util/reconnect.c'\" unpacked with wrong size! fi # end of 'util/reconnect.c' fi if test -f 'util/script' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/script'\" else echo shar: Extracting \"'util/script'\" \(299 characters\) sed "s/^X//" >'util/script' <<'END_OF_FILE' X#!/bin/sh X# Public domain. Xcase "$@" in X"") extra=typescript ;; X"-a") extra=typescript ;; X"-i") extra=typescript ;; X"-a -i") extra=typescript ;; Xesac Xecho "Script started, teeing $@" "$extra" X( echo 'Script started on '`date`; X pty -s "$SHELL"; X echo 'Script done on '`date` ) | tee "$@" "$extra" END_OF_FILE if test 299 -ne `wc -c <'util/script'`; then echo shar: \"'util/script'\" unpacked with wrong size! fi chmod +x 'util/script' # end of 'util/script' fi if test -f 'util/script.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/script.1'\" else echo shar: Extracting \"'util/script.1'\" \(1100 characters\) sed "s/^X//" >'util/script.1' <<'END_OF_FILE' X.TH script 1 X.SH NAME Xscript \- make typescript of terminal session X.SH SYNOPSIS X.B script X[ X.B \-a X] [ X.I file X] X.SH DESCRIPTION X.I script Xmakes a typescript of everything printed on your terminal. XThe typescript is written to X.I file, Xor appended to X.I file Xif the X.B \-a Xoption is given. XYou can print it later with X.I lpr. XIf you don't give a filename, Xthe typescript is saved in the file X.I typescript. X.PP XActually, Xthis clone version of X.I script Xjust passes its options through to X.I tee, Xso you can list multiple files if you want. X.PP X.I script Xruns whatever shell is in environment variable X.I SHELL. XThis clone version uses X.I pty Xto allocate a pseudo-terminal, Xso the session is listed in X.I /etc/utmp, Xand lots of things work right that Xdidn't work in the original. XFor example: X.I talk(1) Xworks; X.I mail(1) Xworks; Xyou can stop and restart the shell, Xwith X.B control-Z Xin X.I sh Xor X.B suspend Xin X.I csh; Xand so on. X.PP XThe script ends when the forked shell exits. XThis clone version is more careful than the original to Xlet every last bit of output appear. X.SH "SEE ALSO" Xscript.tidy(1) END_OF_FILE if test 1100 -ne `wc -c <'util/script.1'`; then echo shar: \"'util/script.1'\" unpacked with wrong size! fi # end of 'util/script.1' fi if test -f 'util/script.tidy' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/script.tidy'\" else echo shar: Extracting \"'util/script.tidy'\" \(64 characters\) sed "s/^X//" >'util/script.tidy' <<'END_OF_FILE' X#!/bin/sh X# Public domain. Xexec sed -e 's/ END_OF_FILE echo shar: 2 control characters may be missing from \"'util/script.tidy'\" if test 64 -ne `wc -c <'util/script.tidy'`; then echo shar: \"'util/script.tidy'\" unpacked with wrong size! fi chmod +x 'util/script.tidy' # end of 'util/script.tidy' fi if test -f 'util/script.tidy.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/script.tidy.1'\" else echo shar: Extracting \"'util/script.tidy.1'\" \(319 characters\) sed "s/^X//" >'util/script.tidy.1' <<'END_OF_FILE' X.TH script.tidy 1 X.SH NAME Xscript.tidy \- remove extra carriage returns and backspaces X.SH SYNOPSIS X.B script.tidy X.SH DESCRIPTION XThis filter Xdoes a bit of postprocessing, Xtypically on a typescript produced by X.I script. XIt removes extra carriage returns, Xand wipes out backspaced characters. X.SH "SEE ALSO" Xscript(1) END_OF_FILE if test 319 -ne `wc -c <'util/script.tidy.1'`; then echo shar: \"'util/script.tidy.1'\" unpacked with wrong size! fi # end of 'util/script.tidy.1' fi if test -f 'util/sess' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sess'\" else echo shar: Extracting \"'util/sess'\" \(44 characters\) sed "s/^X//" >'util/sess' <<'END_OF_FILE' X#!/bin/sh X# Public domain. Xexec pty -s "$@" END_OF_FILE if test 44 -ne `wc -c <'util/sess'`; then echo shar: \"'util/sess'\" unpacked with wrong size! fi chmod +x 'util/sess' # end of 'util/sess' fi if test -f 'util/sess.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sess.1'\" else echo shar: Extracting \"'util/sess.1'\" \(1184 characters\) sed "s/^X//" >'util/sess.1' <<'END_OF_FILE' X.TH sess 1 X.SH NAME Xsess \- run a program under a disconnectable pty session X.SH SYNOPSIS X.B sess X.I program X.B [ X.I arg ... X.B ] X.SH DESCRIPTION X.I sess, Xwhich is really just X.I pty -s, Xruns a program under a pseudo-terminal Xsession that can be easily disconnected and reconnected, Xeven into the middle of a pipe. X.PP X.I sess Xhas all the effects of X.I condom, Xbut sets the original Xterminal to character mode Xand lets the pseudo-terminal do Xtty processing. XThis means that changes to the terminal modes by X.I program Xwill take effect, Xthough Xthe original modes will be Xrestored when X.I program Xstops or exits. X.PP X.I sess Xalso enters the user into X/etc/utmp by default; Xthis can be turned off with X.B\-xU. X.PP XWhen X.I sess Xreceives a HUP signal, Xi.e., when the connection is manually hung up, Xit disconnects X.I program Xfrom the outside world Xand waits for a X.I reconnect. X.PP XSessions are named by Xtheir pseudo-terminal extension: Xfor example, Xa session under /dev/ttyp5 Xhas name p5. XYou can use X.I sessname Xto give them more descriptive names, Xprinted by X.I sesslist. X.SH "SEE ALSO" Xpty(1), Xcondom(1), Xdisconnect(1), Xreconnect(1), Xsessname(1), Xsesskill(1), Xsesslist(1), Xsessuser(1) END_OF_FILE if test 1184 -ne `wc -c <'util/sess.1'`; then echo shar: \"'util/sess.1'\" unpacked with wrong size! fi # end of 'util/sess.1' fi if test -f 'util/sesskill.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sesskill.1'\" else echo shar: Extracting \"'util/sesskill.1'\" \(817 characters\) sed "s/^X//" >'util/sesskill.1' <<'END_OF_FILE' X.TH sesskill 1 X.SH NAME Xsesskill \- kill an outdated pty session X.SH SYNOPSIS X.B sesskill X.I sn X.SH DESCRIPTION XNormally, Xa disconnected pty session will Xwait to be connected X(and in the foreground) Xbefore it produces output. XEven if the program run under the session dies, X.I pty Xwill stick around, saving any last output. X.I sesskill X.I sn Xkills the session named X.I sn, Xforcing it to throw away the output and exit. X.PP X.I xsesskill Xis just like X.I sesskill Xbut applies to sessions started Xwith X.B\-xS. X.SH DIAGNOSTICS X.TP X.I cannot find session X.TP X.I you don't own session X.I sn Xis not a session that you own. X.TP X.I session slave still alive XSelf-explanatory. X.TP X.I cannot kill session master X.I sesskill Xis unable to communicate with Xthe Xpseudo-terminal Xmanager. X.SH "SEE ALSO" Xpty(1), Xsess(1), Xreconnect(1) END_OF_FILE if test 817 -ne `wc -c <'util/sesskill.1'`; then echo shar: \"'util/sesskill.1'\" unpacked with wrong size! fi # end of 'util/sesskill.1' fi if test -f 'util/sesskill.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sesskill.c'\" else echo shar: Extracting \"'util/sesskill.c'\" \(1004 characters\) sed "s/^X//" >'util/sesskill.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <signal.h> X#include <errno.h> Xextern int errno; X#include "sessutil.h" Xextern unsigned short getuid(); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X struct pty_session ps; X int uid; X X uid = getuid(); X X if (!argv[1]) X { X (void) fputs("Usage: sesskill ext\n",stderr); X (void) exit(1); X } X if (pty_get_sessbyext(argv[1][0],argv[1][1],uid,&ps) == -1) X { X (void) fputs("sesskill: fatal: cannot find session\n",stderr); X (void) exit(1); X } X if (ps.uid != uid) X { X (void) fputs("sesskill: fatal: you don't own session\n",stderr); X (void) exit(1); X } X if ((kill(ps.slavepid,0) != -1) || (errno != ESRCH)) X { X /* If all goes wrong, this test could allow a denial of service */ X /* attack. Sigh. */ X (void) fputs("sesskill: fatal: session slave still alive\n",stderr); X (void) exit(1); X } X if (kill(ps.masterpid,SIGXCPU) == -1) X { X (void) perror("sesskill: fatal: cannot kill session master"); X (void) exit(1); X } X (void) exit(0); X} END_OF_FILE if test 1004 -ne `wc -c <'util/sesskill.c'`; then echo shar: \"'util/sesskill.c'\" unpacked with wrong size! fi # end of 'util/sesskill.c' fi if test -f 'util/sesslist.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sesslist.1'\" else echo shar: Extracting \"'util/sesslist.1'\" \(1155 characters\) sed "s/^X//" >'util/sesslist.1' <<'END_OF_FILE' X.TH sesslist 1 X.SH NAME Xsesslist \- list all pty sessions you own X.SH SYNOPSIS X.B sesslist X.SH DESCRIPTION X.I sesslist Xinterprets each file in the X.I pty Xsession directory, Xprinting the results on the standard output. X.PP X``session sn disconnected'' Xmeans that the named session is waiting for a X.I reconnect. X.PP X``session sn sigler p1 master p2 slave p3'' Xmeans that there is a X.I pty Xsession named X.I sn. XThe session program is X``slave'' process p3, Xcontrolled by a X``master'' process p2, Xcurrently connected to a X``signaller'' process p1. X(If the session is disconnected, Xthe signaller is not meaningful.) XAt the end of the line, X.I sesslist Xwill print any name provided by X.I sessname. X.PP X``session xx will drop into session yy'' Xmeans that the current connection to session xx Xwill reconnect to session yy when it is severed. X.PP X.I xsesslist Xis just like X.I sesslist Xbut applies to sessions started Xwith X.B\-xS. X.SH DIAGNOSTICS X.TP X.I unknown file type X.I sesslist Xdoes not understand a file name. X.TP X.I cannot open X.TP X.I cannot open XThese should not happen. XReport them to your system administrator. X.SH "SEE ALSO" Xpty(1), Xsess(1), Xsessname(1) END_OF_FILE if test 1155 -ne `wc -c <'util/sesslist.1'`; then echo shar: \"'util/sesslist.1'\" unpacked with wrong size! fi # end of 'util/sesslist.1' fi if test -f 'util/sesslist.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sesslist.c'\" else echo shar: Extracting \"'util/sesslist.c'\" \(2102 characters\) sed "s/^X//" >'util/sesslist.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <sys/dir.h> X#include <stdio.h> X#include "sessutil.h" Xextern unsigned short getuid(); X Xmain() X{ X int uid; X DIR *dirp; X struct direct *dp; X int fd; X int intbuf[100]; X char *buf = (char *) intbuf; X int r; X X uid = getuid(); X X if (pty_sessdir(uid) == -1) X { X (void) fputs("sesslist: fatal: cannot change to session directory\n",stderr); X (void) exit(1); X } X dirp = opendir("."); X while (dp = readdir(dirp)) X { X if (!strncmp(dp->d_name,"re.",3)) X (void) printf("session %s disconnected\n",dp->d_name + 3); X else if (!strncmp(dp->d_name,"sess.",5)) X { X if ((fd = open(dp->d_name,O_RDONLY)) == -1) X (void) fprintf(stderr, X "sesslist: warning: cannot open %s\n",dp->d_name); X else X { X r = read(fd,buf,99); /* anything up to sizeof(intbuf) - 1 */ X if (r < 4 * sizeof(int)) X (void) fprintf(stderr, X "sesslist: warning: cannot read %s\n",dp->d_name); X else if (r == 4 * sizeof(int)) X (void) printf("session %s sigler %d master %d slave %d\n", X dp->d_name + 5,intbuf[1],intbuf[2],intbuf[3]); X else X { X buf[r] = '\0'; X (void) printf("session %s sigler %d master %d slave %d: %s\n", X dp->d_name + 5,intbuf[1],intbuf[2],intbuf[3], X buf + 4 * sizeof(int)); X } X (void) close(fd); X } X } X else if (!strncmp(dp->d_name,"sig.",4)) X { X if ((fd = open(dp->d_name,O_RDONLY)) == -1) X (void) fprintf(stderr, X "sesslist: warning: cannot open %s\n",dp->d_name); X else X { X r = read(fd,buf,99); X if (r < 9) X (void) fprintf(stderr, X "sesslist: warning: cannot read %s\n",dp->d_name); X else X { X buf[r] = '\0'; X (void) printf("session %s will drop into session %s\n", X dp->d_name + 4,buf + 8); X } X (void) close(fd); X } X } X else if (!strncmp(dp->d_name,".",1)) X ; X else if (!strncmp(dp->d_name,"..",2)) X ; X else X (void) fprintf(stderr, X "sesslist: warning: unknown file type %s\n",dp->d_name); X } X (void) exit(0); X} END_OF_FILE if test 2102 -ne `wc -c <'util/sesslist.c'`; then echo shar: \"'util/sesslist.c'\" unpacked with wrong size! fi # end of 'util/sesslist.c' fi if test -f 'util/sessname.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessname.1'\" else echo shar: Extracting \"'util/sessname.1'\" \(1007 characters\) sed "s/^X//" >'util/sessname.1' <<'END_OF_FILE' X.TH sessname 1 X.SH NAME Xsessname \- provide a more descriptive name for the current pty session X.SH SYNOPSIS X.B sessname X[ X.I name X] X.SH DESCRIPTION X.I sessname Xadds X.I name Xto the information listed by X.I sesslist. XDon't confuse this extra name with Xthe base name of the session, Xwhich is the last two characters of the Xpseudo-terminal filename. X.PP XWithout an argument, X.I sessname Xprints the name of the current session. X.PP X.I xsessname Xis just like X.I sessname Xbut applies to sessions started Xwith X.B\-xS. X.SH DIAGNOSTICS X.TP X.I cannot find session X.TP X.I you don't own session XThe standard input to X.I sessname Xis not a session that you own. X.TP X.I not child of session slave XThe parent to X.I sessname Xis not the head process in the session. X.TP X.I renaming failed X.I sessname Xis unable to set the long name of the session. XThis shouldn't happen. X.SH RESTRICTIONS X.I name Xis limited to 100 characters. X.PP XAt the moment, Xthere's no way to unname a session. X.SH "SEE ALSO" Xpty(1), Xsess(1), Xsesslist(1) END_OF_FILE if test 1007 -ne `wc -c <'util/sessname.1'`; then echo shar: \"'util/sessname.1'\" unpacked with wrong size! fi # end of 'util/sessname.1' fi if test -f 'util/sessname.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessname.c'\" else echo shar: Extracting \"'util/sessname.c'\" \(1052 characters\) sed "s/^X//" >'util/sessname.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <stdio.h> X#include <strings.h> X#include "sessutil.h" Xextern unsigned short getuid(); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X struct pty_session ps; X int uid; X char buf[100]; X int s; X X uid = getuid(); X X if (pty_get_sess(0,uid,&ps) == -1) X { X (void) fputs("sessname: fatal: cannot find session\n",stderr); X (void) exit(1); X } X if (ps.uid != uid) X { X (void) fputs("sessname: fatal: you don't own session\n",stderr); X (void) exit(1); X } X if (ps.slavepid != getppid()) X { X (void) fputs("sessname: fatal: not child of session slave\n",stderr); X (void) exit(1); X } X if (argc == 1) X if (pty_get_sessname(0,uid,buf,sizeof(buf)) == -1) X (void) printf("session %c%c unnamed\n",ps.ext1,ps.ext2); X else X (void) printf("session %c%c: %s\n",ps.ext1,ps.ext2,buf); X else X { X s = strlen(argv[1]) + 1; X if (s > 100) X s = 100; X if (pty_set_sessname(0,uid,argv[1],s) == -1) X { X (void) fputs("sessname: fatal: renaming failed\n",stderr); X (void) exit(1); X } X } X X (void) exit(0); X} END_OF_FILE if test 1052 -ne `wc -c <'util/sessname.c'`; then echo shar: \"'util/sessname.c'\" unpacked with wrong size! fi # end of 'util/sessname.c' fi if test -f 'util/sessuser.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessuser.1'\" else echo shar: Extracting \"'util/sessuser.1'\" \(1943 characters\) sed "s/^X//" >'util/sessuser.1' <<'END_OF_FILE' X.TH sessuser 1 X.SH NAME Xsessuser \- rectify ownership of current pty session X.SH SYNOPSIS X.B sessuser X.SH DESCRIPTION X.I sessuser Xtransfers ownership of the current pty session Xto the user who owns the actual pseudo-terminal file. X.PP X.I sessuser Xis useful in adapting X.I pty Xto old, inconsistent models of logging in. XAn incoming user should be forced to type Xa correct password before the login process Xeven touches a terminal; Xhe should be given a tty Xonly after his identity is established. XInstead, Xusers are given terminals X(as root) Xfirst. XWhen Xthese obsolete programs are modified Xto let X.I pty Xallocate a pseudo-terminal, Xtheir order of operations is still the same, Xand X.I pty Xstill thinks the session is owned by root. XHowever, Xthe login program Xdoes change the ownership of the Xpseudo-terminal file to the user. X.I sessuser Xcommunicates this change to Xthe X.I pty Xmanager, Xso that X.I sesslist, X.I sessname, X.I sesskill, X.I disconnect, Xand X.I reconnect Xwill work. X.PP X.I xsessuser Xis just like X.I sessuser Xbut applies to sessions started with X.B\-xS. X.SH DIAGNOSTICS X.TP X.I cannot find session XThe standard input to X.I sessuser Xis not a session owned by Xthe effective uid of X.I sessuser. X.TP X.I cannot stat tty XThis cannot happen. X.TP X.I you don't own tty XYou don't own the pseudo-terminal file. X.TP X.I not child of session slave XThe parent to X.I sessuser Xis not the head process in the session. X.TP X.I cannot find your username XYou're not listed in X/etc/passwd. X.TP X.I cannot open /etc/utmp XSelf-explanatory. X.TP X.I cannot set session X.I sessuser Xis unable to record the Xchange in session ownership. X.TP X.I cannot communicate new user X.I sessuser Xis unable to communicate with Xthe pseudo-terminal Xmanager. X.SH RESTRICTIONS X.I sessuser Xrequires that the session be Xlisted in X/etc/utmp. XIf it is not, Xor if it is not listed under Xthe correct username, X.I sessuser Xwill exit silently Xwith exit code 1. X.SH "SEE ALSO" Xpty(1), Xsess(1) END_OF_FILE if test 1943 -ne `wc -c <'util/sessuser.1'`; then echo shar: \"'util/sessuser.1'\" unpacked with wrong size! fi # end of 'util/sessuser.1' fi if test -f 'util/sessuser.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessuser.c'\" else echo shar: Extracting \"'util/sessuser.c'\" \(1872 characters\) sed "s/^X//" >'util/sessuser.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/stat.h> X#include <stdio.h> X#include <utmp.h> X#include <pwd.h> X#include <signal.h> X#include <strings.h> X#include "sessutil.h" Xextern unsigned short getuid(); Xextern unsigned short geteuid(); X#define PTYUTMP_FILE "/etc/utmp" X Xmain() X{ X struct pty_session ps; X int uid; X int euid; X register FILE *fi; X struct utmp ut; X struct stat st; X struct passwd *pw; X char *username; X X uid = getuid(); X euid = geteuid(); X X if (pty_get_sess(0,euid,&ps) == -1) /* i.e., as root */ X { X (void) fputs("sessuser: fatal: cannot find session\n",stderr); X (void) exit(1); X } X if (fstat(0,&st) == -1) X { X (void) fputs("sessuser: fatal: cannot stat tty\n",stderr); X (void) exit(1); X } X if (st.st_uid != uid) X { X (void) fputs("sessuser: fatal: you don't own tty\n",stderr); X (void) exit(1); X } X if (ps.slavepid != getppid()) X { X (void) fputs("sessuser: fatal: not child of session slave\n",stderr); X (void) exit(1); X } X if (!(pw = getpwuid(uid))) X { X (void) fputs("sessuser: fatal: cannot find your username\n",stderr); X (void) exit(1); X } X username = pw->pw_name; X if (!(fi = fopen(PTYUTMP_FILE,"r"))) X { X (void) perror("sessuser: fatal: cannot open /etc/utmp"); X (void) exit(1); X } X else X while (fread((char *) &ut,sizeof(ut),1,fi)) X if ((ut.ut_line[3] == ps.ext1) && (ut.ut_line[4] == ps.ext2) X &&(ut.ut_line[5] == '\0')) X if (!strncmp(ut.ut_name,username,8)) X { X ps.uid = uid; X if (pty_set_sess(0,uid,&ps) == -1) X { X (void) fputs("sessuser: fatal: cannot set session\n",stderr); X (void) exit(1); X } X if (kill(ps.masterpid,SIGUSR2) == -1) X { X (void) fputs("sessuser: fatal: cannot communicate new user\n",stderr); X (void) exit(1); X } X (void) fchown(0,euid,-1); X (void) exit(0); X } X else X (void) exit(1); X (void) exit(1); X} END_OF_FILE if test 1872 -ne `wc -c <'util/sessuser.c'`; then echo shar: \"'util/sessuser.c'\" unpacked with wrong size! fi # end of 'util/sessuser.c' fi if test -f 'util/sessutil.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessutil.c'\" else echo shar: Extracting \"'util/sessutil.c'\" \(3786 characters\) sed "s/^X//" >'util/sessutil.c' <<'END_OF_FILE' X/* Public domain. */ X X#include "sessutil.h" X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <stdio.h> Xextern char *ttyname(); Xextern long lseek(); X X/* Will have to change ttyn indices if DEVSTY changes. */ X Xint pty_sessdir(uid) Xint uid; X{ X char foo[50]; X X (void) sprintf(foo,"/usr/etc/pty/%d",uid); X return chdir(foo); X} X Xint pty_get_sess(fd,uid,ps) Xint fd; Xint uid; Xstruct pty_session *ps; X{ X char *ttyn; X X if (ttyn = ttyname(fd)) X return pty_get_sessbyext(ttyn[8],ttyn[9],uid,ps); X return -1; X} X Xint pty_get_sessbyext(ext1,ext2,uid,ps) Xchar ext1; Xchar ext2; Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X int fdsess; X X ps->ext1 = ext1; X ps->ext2 = ext2; X (void) sprintf(foo,"/usr/etc/pty/%d/sess.%c%c",uid,ps->ext1,ps->ext2); X if ((fdsess = open(foo,O_RDONLY)) != -1) X { X if ((read(fdsess,(char *) &ps->uid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->siglerpid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->masterpid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->slavepid,sizeof(int)) == sizeof(int))) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X return -1; X} X Xint pty_set_sess(fd,uid,ps) Xint fd; Xint uid; Xstruct pty_session *ps; X{ X char *ttyn; X char foo[50]; X int fdsess; X X if (ttyn = ttyname(fd)) X { X ps->ext1 = ttyn[8]; X ps->ext2 = ttyn[9]; X (void) sprintf(foo,"/usr/etc/pty/%d/sess.%c%c",uid,ps->ext1,ps->ext2); X if ((fdsess = open(foo,O_WRONLY)) != -1) X { X if ((write(fdsess,(char *) &ps->uid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->siglerpid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->masterpid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->slavepid,sizeof(int)) == sizeof(int))) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_get_sessname(fd,uid,buf,len) Xint fd; Xint uid; Xchar *buf; Xint len; X{ X char *ttyn; X char foo[50]; X int fdsess; X int r; X X if (ttyn = ttyname(fd)) X { X (void) sprintf(foo,"/usr/etc/pty/%d/sess.%c%c",uid,ttyn[8],ttyn[9]); X if ((fdsess = open(foo,O_RDONLY)) != -1) X { X if (lseek(fdsess,(long) (4 * sizeof(int)),0) != (long) -1) X if ((r = read(fdsess,buf,len - 1)) > 0) X { /* could make that != -1. This way, default session is unnamed. */ X buf[r] = '\0'; X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_set_sessname(fd,uid,buf,len) Xint fd; Xint uid; Xchar *buf; Xint len; X{ X char *ttyn; X char foo[50]; X int fdsess; X X if (ttyn = ttyname(fd)) X { X (void) sprintf(foo,"/usr/etc/pty/%d/sess.%c%c",uid,ttyn[8],ttyn[9]); X if ((fdsess = open(foo,O_WRONLY)) != -1) X { X if (lseek(fdsess,(long) (4 * sizeof(int)),0) != (long) -1) X if (write(fdsess,buf,len) != -1) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_get_rebyext(ext1,ext2,uid) Xchar ext1; Xchar ext2; Xint uid; X{ X char foo[50]; X struct stat st; X X (void) sprintf(foo,"/usr/etc/pty/%d/re.%c%c",uid,ext1,ext2); X return stat(foo,&st); X} X Xint pty_set_sig(ext1,ext2,uid,ps) Xchar ext1; Xchar ext2; Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X int fdsig; X X (void) sprintf(foo,"/usr/etc/pty/%d/sig.%c%c",uid,ps->ext1,ps->ext2); X if ((fdsig = open(foo,O_WRONLY | O_CREAT | O_TRUNC,0600)) != -1) X { X (void) sprintf(foo,"/dev/tty%c%c",ext1,ext2); X if (write(fdsig,foo,11) == 11) X { X (void) close(fdsig); X return 0; X } X (void) close(fdsig); X } X return -1; X} X Xint pty_unset_sig(uid,ps) Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X X (void) sprintf(foo,"/usr/etc/pty/%d/sig.%c%c",uid,ps->ext1,ps->ext2); X return unlink(foo); X} END_OF_FILE if test 3786 -ne `wc -c <'util/sessutil.c'`; then echo shar: \"'util/sessutil.c'\" unpacked with wrong size! fi # end of 'util/sessutil.c' fi if test -f 'util/sessutil.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/sessutil.h'\" else echo shar: Extracting \"'util/sessutil.h'\" \(357 characters\) sed "s/^X//" >'util/sessutil.h' <<'END_OF_FILE' X#ifndef SESSUTIL_H X#define SESSUTIL_H X Xstruct pty_session X { X char ext1; X char ext2; X int uid; X int siglerpid; X int masterpid; X int slavepid; X } X; X Xint pty_sessdir(); Xint pty_get_sess(); Xint pty_get_sessbyext(); Xint pty_set_sess(); Xint pty_get_sessname(); Xint pty_set_sessname(); Xint pty_get_rebyext(); Xint pty_set_sig(); Xint pty_unset_sig(); X X#endif END_OF_FILE if test 357 -ne `wc -c <'util/sessutil.h'`; then echo shar: \"'util/sessutil.h'\" unpacked with wrong size! fi # end of 'util/sessutil.h' fi if test -f 'util/tiocsti.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/tiocsti.1'\" else echo shar: Extracting \"'util/tiocsti.1'\" \(250 characters\) sed "s/^X//" >'util/tiocsti.1' <<'END_OF_FILE' X.TH tiocsti 1 X.SH NAME Xtiocsti \- simulate terminal input X.SH SYNOPSIS X.B tiocsti X[ X.I arg X] ... X.SH DESCRIPTION X.I tiocsti X``types'' each of its arguments on the Xcurrent terminal, separated by spaces, Xas if you had typed them. X.SH "SEE ALSO" Xtty(4) END_OF_FILE if test 250 -ne `wc -c <'util/tiocsti.1'`; then echo shar: \"'util/tiocsti.1'\" unpacked with wrong size! fi # end of 'util/tiocsti.1' fi if test -f 'util/tiocsti.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/tiocsti.c'\" else echo shar: Extracting \"'util/tiocsti.c'\" \(322 characters\) sed "s/^X//" >'util/tiocsti.c' <<'END_OF_FILE' X/* Public domain. */ X#include <sys/ioctl.h> X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X int j; X char *s; X X if (ioctl(3,TIOCGPGRP,(char *) &j) == -1) X (void) dup2(0,3); X X for (j = 1;j < argc;j++) X { X for (s = argv[j];*s;s++) X (void) ioctl(3,TIOCSTI,s); X if (j < argc - 1) X (void) ioctl(3,TIOCSTI," "); X } X} END_OF_FILE if test 322 -ne `wc -c <'util/tiocsti.c'`; then echo shar: \"'util/tiocsti.c'\" unpacked with wrong size! fi # end of 'util/tiocsti.c' fi if test -f 'util/tty.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/tty.1'\" else echo shar: Extracting \"'util/tty.1'\" \(279 characters\) sed "s/^X//" >'util/tty.1' <<'END_OF_FILE' X.TH tty 1 X.SH NAME Xtty \- get terminal name X.SH SYNOPSIS X.B tty X[ X.B\-s X] X.SH DESCRIPTION X.I tty Xprints the pathname of the user's terminal, Xor X``not a tty'' Xif its input is not a terminal. X.I tty \-s Xprints nothing. X.SH "EXIT VALUE" X0 if the input is a terminal, X1 if it isn't. END_OF_FILE if test 279 -ne `wc -c <'util/tty.1'`; then echo shar: \"'util/tty.1'\" unpacked with wrong size! fi # end of 'util/tty.1' fi if test -f 'util/tty.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/tty.c'\" else echo shar: Extracting \"'util/tty.c'\" \(298 characters\) sed "s/^X//" >'util/tty.c' <<'END_OF_FILE' X/* Public domain. */ X Xextern char *ttyname(); X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X char *s; X X s = ttyname(0); X X if ((argc != 2) X ||(argv[1][0] != '-') || (argv[1][1] != 's') || (argv[1][2] != '\0')) X if (s) X (void) puts(s); X else X (void) puts("not a tty"); X (void) exit(!s); X} END_OF_FILE if test 298 -ne `wc -c <'util/tty.c'`; then echo shar: \"'util/tty.c'\" unpacked with wrong size! fi # end of 'util/tty.c' fi if test -f 'util/u.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/u.1'\" else echo shar: Extracting \"'util/u.1'\" \(434 characters\) sed "s/^X//" >'util/u.1' <<'END_OF_FILE' X.TH u 1 X.SH NAME Xu \- compact list of users who are on the system X.SH SYNOPSIS X.B u X.SH DESCRIPTION X.I u Xlists the login names of the users currently on the system in a compact, Xsorted, Xone-line format. X.PP XThe original version of this program eliminates duplicates. X(Actually, it doesn't even do that correctly.) XThis clone version prints exactly each user as many times Xas /etc/utmp lists. X.SH FILES X/etc/utmp X.SH "SEE ALSO" Xwho(1) END_OF_FILE if test 434 -ne `wc -c <'util/u.1'`; then echo shar: \"'util/u.1'\" unpacked with wrong size! fi # end of 'util/u.1' fi if test -f 'util/u.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/u.c'\" else echo shar: Extracting \"'util/u.c'\" \(933 characters\) sed "s/^X//" >'util/u.c' <<'END_OF_FILE' X/* Public domain. */ X#include <stdio.h> X#include <utmp.h> X#include <strings.h> Xextern char *malloc(); X#define PTYUTMP_FILE "/etc/utmp" X Xint compar(s,t) Xchar (**s)[8]; Xchar (**t)[8]; X{ X return -strncmp(&(**s)[0],&(**t)[0],8); X} X Xmain() X{ X register FILE *fi; X struct utmp ut; X char (*us)[8]; X char (**up)[8]; X int lines = 0; X int i = 0; X X if (fi = fopen(PTYUTMP_FILE,"r")) X while (fread((char *) &ut,sizeof(ut),1,fi)) X if (ut.ut_name[0]) X lines++; X (void) fclose(fi); X us = malloc(sizeof(*us) * (lines + 50)); X up = malloc(sizeof(*up) * (lines + 50)); X if (fi = fopen(PTYUTMP_FILE,"r")) X while (fread((char *) &ut,sizeof(ut),1,fi)) X if ((ut.ut_name[0]) && (i < lines + 50)) X { X (void) strncpy(us[i],ut.ut_name,8); X up[i] = us + i; X i++; X } X (void) fclose(fi); X (void) qsort(up,i,sizeof(*up),compar); X X while (i-- > 0) X (void) printf((i ? "%.8s " : "%.8s\n"),up[i]); X X (void) exit(0); X} END_OF_FILE if test 933 -ne `wc -c <'util/u.c'`; then echo shar: \"'util/u.c'\" unpacked with wrong size! fi # end of 'util/u.c' fi if test -f 'util/wall.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/wall.1'\" else echo shar: Extracting \"'util/wall.1'\" \(412 characters\) sed "s/^X//" >'util/wall.1' <<'END_OF_FILE' X.TH wall 1 X.SH NAME Xwall \- write to all users X.SH SYNOPSIS X.B wall X.SH DESCRIPTION X.I wall Xprompts for a message with X``Broadcast Message,'' Xthen reads its standard input until end-of-file. XIt then sends this message Xto all logged in users. X.PP XNaturally, X.I mesg Xprotections apply. X.SH FILES X/dev/tty* X.br X/etc/utmp X.SH RESTRICTIONS XThe message is limited to 10000 characters. X.SH "SEE ALSO" Xmesg(1), Xwrite(1) END_OF_FILE if test 412 -ne `wc -c <'util/wall.1'`; then echo shar: \"'util/wall.1'\" unpacked with wrong size! fi # end of 'util/wall.1' fi if test -f 'util/wall.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/wall.c'\" else echo shar: Extracting \"'util/wall.c'\" \(1577 characters\) sed "s/^X//" >'util/wall.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/timeb.h> X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <stdio.h> X#include <strings.h> X#include <utmp.h> X#include <pwd.h> X#include <time.h> X#include <ctype.h> Xextern unsigned short getuid(); Xextern char *ttyname(); Xextern long time(); X#define PTYUTMP_FILE "/etc/utmp" X Xmain() X{ X register FILE *fi; X struct utmp ut; X char fntty[30]; X int fd; X char buf[10000]; X char *username; X struct passwd *pw; X char hostname[64]; X char *ttyn; X long t; X struct tm *tm; X int r; X int pos; X X if (!(pw = getpwuid((int) getuid()))) X { X (void) fprintf(stderr,"write: who are you?\n"); X (void) exit(1); X } X username = pw->pw_name; X X (void) gethostname(hostname,sizeof(hostname)); X X if (!(ttyn = ttyname(2))) X { X (void) fprintf(stderr,"wall: Can't find your tty\n"); X (void) exit(1); X } X X t = time((long *) 0); X tm = localtime(&t); X X (void) sprintf(buf,"\nBroadcast message from %s@%s on %s at %d:%02d ...\n\n", X username,hostname,ttyn + 5,tm->tm_hour,tm->tm_min); X pos = strlen(buf); X (void) write(1,buf + 1,pos - 1); X while ((pos < 10000) && ((r = read(0,buf + pos,10000 - pos)) > 0)) X pos += r; X X if (fi = fopen(PTYUTMP_FILE,"r")) X while (fread((char *) &ut,sizeof(ut),1,fi)) X if (ut.ut_name[0]) X { X (void) sprintf(fntty,"/dev/%.8s",ut.ut_line); X if ((fd = open(fntty,O_WRONLY)) == -1) X (void) fprintf(stderr,"wall: cannot write to %.8s\n",ut.ut_line); X else X { X (void) write(fd,buf,pos); X (void) close(fd); X } X } X X (void) exit(0); X} END_OF_FILE echo shar: 1 control character may be missing from \"'util/wall.c'\" if test 1577 -ne `wc -c <'util/wall.c'`; then echo shar: \"'util/wall.c'\" unpacked with wrong size! fi # end of 'util/wall.c' fi if test -f 'util/who.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/who.1'\" else echo shar: Extracting \"'util/who.1'\" \(693 characters\) sed "s/^X//" >'util/who.1' <<'END_OF_FILE' X.TH who 1 X.SH NAME Xwho \- who is on the system X.SH SYNOPSIS X.B who X[ X.I file X] [ X.B "am I" X] X.SH DESCRIPTION X.I who Xlists the login name, terminal name, and login time Xfor each current user. XNormally X.I who Xuses X.I /etc/utmp, Xand omits Xlogged-out terminals. XIf you give it a X.I file, Xsuch as X/usr/adm/wtmp, Xit will use that file instead, Xand include logouts as lines with a blank username. XReboots have an x in place of X.I ttyxx. X.PP XWith two arguments, Xas in X.I who am I X(and also X.I who are you), X.I who Xtells who you are logged in as. XNote that in this clone version, X.I who Xgives a meaningful error when its input is not a terminal. X.SH FILES X/etc/utmp X.SH "SEE ALSO" Xgetuid(2), utmp(5) END_OF_FILE if test 693 -ne `wc -c <'util/who.1'`; then echo shar: \"'util/who.1'\" unpacked with wrong size! fi # end of 'util/who.1' fi if test -f 'util/who.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/who.c'\" else echo shar: Extracting \"'util/who.c'\" \(1208 characters\) sed "s/^X//" >'util/who.c' <<'END_OF_FILE' X/* Public domain. */ X#include <stdio.h> X#include <utmp.h> X#include <strings.h> X#include <time.h> Xextern char *ttyname(); X#define PTYUTMP_FILE "/etc/utmp" X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X FILE *fi; X struct utmp ut; X char fn[100]; X char ttyn[100]; X char hostname[120]; X int whoami = 0; X X if (argc > 3) exit(0); X if (argc > 2) X { X whoami = 1; X if (!isatty(0)) X { X fprintf(stderr,"who: Not a tty\n"); X exit(1); X } X (void) strcpy(ttyn,ttyname(0) + 5); X (void) gethostname(hostname,120); X } X if (argc == 2) X (void) strncpy(fn,argv[1],100); X else X (void) strncpy(fn,PTYUTMP_FILE,100); X X if ((fi = fopen(fn,"r")) == NULL) X { X (void) sprintf(hostname,"who: %s",fn); X (void) perror(hostname); X (void) exit(1); X } X X while (fread((char *) &ut,sizeof(ut),1,fi)) X { X if (ut.ut_name[0] || (argc > 1)) X if ((!whoami) || (!strncmp(ut.ut_line,ttyn,8))) X { X if (whoami) X (void) printf("%s!",hostname); X (void) printf("%-8.8s %-8.8s%-12.12s",ut.ut_name,ut.ut_line, X asctime(localtime(&ut.ut_time)) + 4); X if (ut.ut_host[0]) X (void) printf(" (%.16s)",ut.ut_host); X (void) printf("\n"); X } X } X (void) fclose(fi); X (void) exit(0); X} END_OF_FILE if test 1208 -ne `wc -c <'util/who.c'`; then echo shar: \"'util/who.c'\" unpacked with wrong size! fi # end of 'util/who.c' fi if test -f 'util/write.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/write.1'\" else echo shar: Extracting \"'util/write.1'\" \(1761 characters\) sed "s/^X//" >'util/write.1' <<'END_OF_FILE' X.TH write 1 X.SH NAME Xwrite \- write to another user X.SH SYNOPSIS X.B write X.I user X[ X.I ttyname X] X.SH DESCRIPTION X.I write Xcopies lines from your terminal to that of Xanother user. XThis clone version Xstarts and (if you don't kill the process) Xends your message with identifying lines. XIt also Xprecedes each line with your username. X.PP XIf you want to write to a user who is logged in more than once, Xinclude the terminal name as X.I ttyname. X(This reflects several failures in the pseudo-terminal model.) X.PP XThe other user can use X.I mesg Xto allow or deny X.I write Xpermission. XIn many recent versions of X.I write, Xyou cannot write to a user unless you are also allowing messages; Xunfortunately, this adds absolutely no security, because you can Xturn messages right back off after starting X.I write. XThis clone version exhibits the right behavior: Xit checks X.I mesg Xpermission on the other end Xbefore writing each line. X.PP XThis version of X.I write Xdoes not Xallow shell escapes. X.PP XThere are many popular ways of using X.I write X(perhaps the most common being to Xuse X.I talk Xinstead). XTypically each user ends each series of lines Xwith a distinctive signal, such as X``ga'' (go ahead), Xso that the other user can type without Xworrying about messed-up output. XThe number of abbreviations used in Xscreen conversation is immense. X.PP XThis clone version of X.I write Xuses the standard input, Xrather than the standard error, Xto determine your tty. X.PP X.I write Xreplaces unprintable characters by Xcarets upon output. X.SH RESTRICTIONS XLines longer than 500 characters Xwill be split in two. X.PP X.I write Xsleeps for a second after sending each line. XThis restriction means that you can't Xflood someone else's screen with a large text. X.SH "SEE ALSO" Xmesg(1), Xwho(1), Xmail(1) END_OF_FILE if test 1761 -ne `wc -c <'util/write.1'`; then echo shar: \"'util/write.1'\" unpacked with wrong size! fi # end of 'util/write.1' fi if test -f 'util/write.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/write.c'\" else echo shar: Extracting \"'util/write.c'\" \(2728 characters\) sed "s/^X//" >'util/write.c' <<'END_OF_FILE' X/* Public domain. */ X X#include <sys/types.h> X#include <sys/timeb.h> X#include <sys/stat.h> X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <stdio.h> X#include <strings.h> X#include <utmp.h> X#include <pwd.h> X#include <time.h> X#include <ctype.h> Xextern unsigned short getuid(); Xextern char *ttyname(); Xextern long time(); X#define PTYUTMP_FILE "/etc/utmp" X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X register FILE *fi; X struct utmp ut; X char line[9]; X int lines = 0; X char fntty[30]; X int fd; X struct stat st; X char buf[500]; X int offset; X char *username; X struct passwd *pw; X char hostname[64]; X char *ttyn; X long t; X struct tm *tm; X char *s; X X if (argc < 2) X { X (void) fprintf(stderr,"Usage: write user [ttyname]\n"); X (void) exit(1); X } X X if (!(pw = getpwuid((int) getuid()))) X { X (void) fprintf(stderr,"write: who are you?\n"); X (void) exit(1); X } X username = pw->pw_name; X X (void) gethostname(hostname,sizeof(hostname)); X X if (!(ttyn = ttyname(2))) X { X (void) fprintf(stderr,"write: Can't find your tty\n"); X (void) exit(1); X } X X t = time((long *) 0); X tm = localtime(&t); X X if (fi = fopen(PTYUTMP_FILE,"r")) X while (fread((char *) &ut,sizeof(ut),1,fi)) X if (!strncmp(ut.ut_name,argv[1],8)) X if ((argc == 2) || (!strncmp(ut.ut_line,argv[2],8))) X if (!lines) X { X (void) strncpy(line,ut.ut_line,8); X line[8] = '\0'; X lines = 1; X } X else X lines++; X if (!lines) X { X if (argc == 2) X (void) fprintf(stderr,"write: %s not logged in\n",argv[1]); X else X (void) fprintf(stderr,"write: %s not logged in on tty %s\n", X argv[1],argv[2]); X (void) exit(1); X } X if (lines > 1) X (void) fprintf(stderr, X "write: %s logged in more than once ... writing to %s\n", X argv[1],line); X X (void) sprintf(fntty,"/dev/%s",line); X if ((fd = open(fntty,O_WRONLY)) == -1) X { X (void) fprintf(stderr,"write: Permission denied\n"); X (void) exit(1); X } X X (void) sprintf(buf,"\nMessage from %s@%s on %s at %d:%02d ...\n", X username,hostname,ttyn + 5,tm->tm_hour,tm->tm_min); X (void) write(fd,buf,strlen(buf)); X X (void) sprintf(buf,"%s: ",username); X offset = strlen(buf); X X while (fgets(buf + offset,sizeof(buf) - offset,stdin)) X { X (void) fstat(fd,&st); X if (!(st.st_mode & 0020)) X { X (void) fprintf(stderr,"write: Permission denied\n"); X (void) exit(1); X } X for (s = buf;*s;s++) X if (((!isascii(*s) || !isprint(*s))) && (*s != '\n')) X *s = '^'; X (void) write(fd,buf,strlen(buf)); X (void) sleep(1); X } X X t = time((long *) 0); X tm = localtime(&t); X (void) sprintf(buf,"End of message from %s@%s on %s at %d:%02d\n", X username,hostname,ttyn + 5,tm->tm_hour,tm->tm_min); X (void) write(fd,buf,strlen(buf)); X X (void) exit(0); X} END_OF_FILE echo shar: 1 control character may be missing from \"'util/write.c'\" if test 2728 -ne `wc -c <'util/write.c'`; then echo shar: \"'util/write.c'\" unpacked with wrong size! fi # end of 'util/write.c' fi if test -f 'util/xsessutil.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util/xsessutil.c'\" else echo shar: Extracting \"'util/xsessutil.c'\" \(4232 characters\) sed "s/^X//" >'util/xsessutil.c' <<'END_OF_FILE' X/* Public domain. */ X X#include "sessutil.h" X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/file.h> X#ifdef BSD X#include <limits.h> X#endif X#include <stdio.h> Xextern char *ttyname(); Xextern long lseek(); Xextern char *getenv(); X X/* Will have to change ttyn indices if DEVSTY changes. */ X Xint pty_sessdir(uid) Xint uid; X{ X char foo[50]; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X (void) sprintf(foo,"%s/.pty/%d",ho,uid); X return chdir(foo); X} X Xint pty_get_sess(fd,uid,ps) Xint fd; Xint uid; Xstruct pty_session *ps; X{ X char *ttyn; X X if (ttyn = ttyname(fd)) X return pty_get_sessbyext(ttyn[8],ttyn[9],uid,ps); X return -1; X} X Xint pty_get_sessbyext(ext1,ext2,uid,ps) Xchar ext1; Xchar ext2; Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X int fdsess; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X ps->ext1 = ext1; X ps->ext2 = ext2; X (void) sprintf(foo,"%s/.pty/%d/sess.%c%c",ho,uid,ps->ext1,ps->ext2); X if ((fdsess = open(foo,O_RDONLY)) != -1) X { X if ((read(fdsess,(char *) &ps->uid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->siglerpid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->masterpid,sizeof(int)) == sizeof(int)) X &&(read(fdsess,(char *) &ps->slavepid,sizeof(int)) == sizeof(int))) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X return -1; X} X Xint pty_set_sess(fd,uid,ps) Xint fd; Xint uid; Xstruct pty_session *ps; X{ X char *ttyn; X char foo[50]; X int fdsess; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X if (ttyn = ttyname(fd)) X { X ps->ext1 = ttyn[8]; X ps->ext2 = ttyn[9]; X (void) sprintf(foo,"%s/.pty/%d/sess.%c%c",ho,uid,ps->ext1,ps->ext2); X if ((fdsess = open(foo,O_WRONLY)) != -1) X { X if ((write(fdsess,(char *) &ps->uid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->siglerpid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->masterpid,sizeof(int)) == sizeof(int)) X &&(write(fdsess,(char *) &ps->slavepid,sizeof(int)) == sizeof(int))) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_get_sessname(fd,uid,buf,len) Xint fd; Xint uid; Xchar *buf; Xint len; X{ X char *ttyn; X char foo[50]; X int fdsess; X int r; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X if (ttyn = ttyname(fd)) X { X (void) sprintf(foo,"%s/.pty/%d/sess.%c%c",ho,uid,ttyn[8],ttyn[9]); X if ((fdsess = open(foo,O_RDONLY)) != -1) X { X if (lseek(fdsess,(long) (4 * sizeof(int)),0) != (long) -1) X if ((r = read(fdsess,buf,len - 1)) > 0) X { /* could make that != -1. This way, default session is unnamed. */ X buf[r] = '\0'; X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_set_sessname(fd,uid,buf,len) Xint fd; Xint uid; Xchar *buf; Xint len; X{ X char *ttyn; X char foo[50]; X int fdsess; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X if (ttyn = ttyname(fd)) X { X (void) sprintf(foo,"%s/.pty/%d/sess.%c%c",ho,uid,ttyn[8],ttyn[9]); X if ((fdsess = open(foo,O_WRONLY)) != -1) X { X if (lseek(fdsess,(long) (4 * sizeof(int)),0) != (long) -1) X if (write(fdsess,buf,len) != -1) X { X (void) close(fdsess); X return 0; X } X (void) close(fdsess); X } X } X return -1; X} X Xint pty_get_rebyext(ext1,ext2,uid) Xchar ext1; Xchar ext2; Xint uid; X{ X char foo[50]; X struct stat st; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X (void) sprintf(foo,"%s/.pty/%d/re.%c%c",ho,uid,ext1,ext2); X return stat(foo,&st); X} X Xint pty_set_sig(ext1,ext2,uid,ps) Xchar ext1; Xchar ext2; Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X int fdsig; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X (void) sprintf(foo,"%s/.pty/%d/sig.%c%c",ho,uid,ps->ext1,ps->ext2); X if ((fdsig = open(foo,O_WRONLY | O_CREAT | O_TRUNC,0600)) != -1) X { X (void) sprintf(foo,"/dev/tty%c%c",ext1,ext2); X if (write(fdsig,foo,11) == 11) X { X (void) close(fdsig); X return 0; X } X (void) close(fdsig); X } X return -1; X} X Xint pty_unset_sig(uid,ps) Xint uid; Xstruct pty_session *ps; X{ X char foo[50]; X char *ho; X X if (!(ho = getenv("HOME"))) X return -1; X X (void) sprintf(foo,"%s/.pty/%d/sig.%c%c",ho,uid,ps->ext1,ps->ext2); X return unlink(foo); X} END_OF_FILE if test 4232 -ne `wc -c <'util/xsessutil.c'`; then echo shar: \"'util/xsessutil.c'\" unpacked with wrong size! fi # end of 'util/xsessutil.c' fi echo shar: End of shell archive. exit 0