[alt.sources] pty 3.0 - the ultimate pseudo-terminal interface

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