[comp.sources.misc] v10i001: modem callback program, part 1 of 2

howard@hasse.ericsson.se (Howard Gayle) (01/07/90)

Posting-number: Volume 10, Issue 1
Submitted-by: howard@hasse.ericsson.se (Howard Gayle)
Archive-name: callback_hg/part01

Suggested archive name: callback.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  README Makefile callback.c callback.h callback0.c
# Wrapped by howard@hasse on Fri Dec 29 10:22:51 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(10750 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X$Header: README,v 1.2 89/12/29 10:17:06 howard Exp $
X
X      DESCRIPTION
X
XThe callback system implements callback for dialup login
Xsessions.  A user dials in and enters a symbolic name for a
Xstored telephone number.  The line is dropped, and a new call is
Xplaced to the corresponding number.  On connection a normal
Xlogin session is established.  Unlike callback firmware in some
Xmodems, the list of phone numbers may be arbitrarily long.
XDifferent lines may use different lists, or any number of lines
Xmay share the same list.
X
XThe system has two purposes: to charge (almost all of) the cost
Xof the telephone calls to the host, and to improve security.  I
Xdo no guarantee that callback is secure, but at least you can
Xlook at the source code; it's not firmware burned into a PROM.
X
XCallback has been tested on an ALM port on a Sun 3/280 running
XSunOS 4.0.3, and on a CPU serial port on a Sun 3/60 runing first
XSunOS 4.0.1, then SunOS 4.0.3.  Callback may work on other UNIX
Xversions that use termio and /dev/cua* special files for dialing
Xout.  It will not work on SunOS 3.x.  It has been tested with
XOctocom OSI8596 modems and Alfanet ANC 8x224TT modems, both of
Xwhich are Hayes-compatible.  Callback is designed to be general
Xenough to operate with most autodial modems.  Extensive logging
Xshows all traffic to and from the modem, with timestamps.
X
XCallback works with the normal getty and login functionality,
Xincluding line speed selection.  It allows different modem
Xcommands for each line speed, if necessary.
X
XAs distributed, callback needs verbose text responses from
Xmodems, e.g. "CONNECT 2400" instead of "10."  This makes it
Xincompatible with some UUCPs, since they require numeric
Xresponse codes.  It should be easy to modify callback to handle
Xnumeric response codes, however.
X
X
X      LICENSE
X
XCallback is free software under the GNU general public license.
X
X
X      HOW IT WORKS (KLUDGEOLOGISTS TAKE NOTE)
X
XThe /etc/ttytab file is modified for the dialup line.  Instead
Xof running the usual getty, callback is run.  Callback
Ximmediately execs a special getty, named getty-cb.  Getty-cb
Xworks exactly like the ordinary getty, except that after it has
Xthe tty set up correctly and has read the login name, it does
Xnot exec login but instead execs callback0.  Callback0 does the
Xactual callback.  It prompts the user for the symbolic name for
Xthe telephone number, hangs up, sends the dialing command to the
Xmodem, waits for answer, turns off the hangup-on-close (HUPCL)
Xbit, and then simply exits.  Since the getty process has died,
Xinit starts up another one, so another callback is started.  But
Xthis time, callback does not exec getty-cb, it execs login, so
Xthe user can finish logging in.  Callback execs either getty-cb
Xor login, depending on a state file that callback0 modifies.
XAnd getty-cb is really just the vendor-supplied getty
Xexecutable, with "/bin/login" changed to a path to callback0.
X
X
X      PREREQUISITES
X
XYou must have my library of C functions.  It was posted to
Xcomp.sources.misc on 1 October 1989 (volume 8, issues 80-87) and
X28 October 1989 (volume 8, issue 96).  The archive name was
Xlibhoward.
X
XYou also need some way to change a string in an executable, or
Xyou need source for a getty that will work on your system.  GNU
XEmacs is an excellent tool for editing executables; adb will
Xalso work.
X
XThe libhoward package itself has a number of prerequisites.
XANSI-C float.h and limits.h include files must be on the C
Xinclude file search path.  If you don't have them, you can use
XSteven Pemberton's config program to make them.  Config was
Xposted to comp.sources.misc on 30 April 1989 as volume 6 issue
X96, archive name config2.
X
XThe makefiles use features that may only be in GNU make, version
X3.54 or later.  (There are a lot of different make commands out
Xthere, and I haven't tested them all, so I don't know for sure
Xwhich will work and which won't.  GNU make 3.54 will definitely
Xwork.  The make supplied with SunOS 4.0.3 will not work.)
X
XChris Tweed's sets command must be on the command search path.
XIt was posted to comp.sources.unix on 24 February 1988 as volume
X13, issue 68, archive name sets.
X
XSome of the documentation is in LaTeX.  You do not *have* to
Xread it, but it may be helpful.  If you want to read it, you
Xwill need LaTeX to format it.
X
X
X      INSTALLATION
X
XIt will be easier to understand the installation if you first
Xread my report "Sharing software in a network of heterogeneous
XUNIX hosts."  This comes with libhoward.
X
XInstall the dialup lines in the normal way, including the
X/dev/cua* files.  For Suns, see section 11.4, "Adding a Modem to
XYour System," in the System & Network Administration Manual.
XMake sure the dialup lines are working in the normal manner,
Xwithout callback, before installing callback.
X
XFigure out where you want to install various files.  Here I'll
Xassume a separate directory for everything, but it's easy to
Xmake a different choice.  I discuss an alternative example
Xbelow.
X
XI'll call the master source directory $DD.
X
XCreate a directory for compiling on this machine type.  I'll
Xcall it /usr/local/free/callback.
X
XFind out the version.  Look at the Header line (typically the
Xfirst line) of the FREEZE file.  The version is the first
Xnumber, and ends at the period.  The first externally released
Xversion was version 2.
X
XCreate a subdirectory for this version. e.g.
X/usr/local/free/callback/2.
X
XCreate the following subdirectories for results:
X   bin       Installed executables.
X   man/man1  Manual entries.
X   man/cat1  On-line formatted manual entries.
X
XFor example:
X   % cd /usr/local/free/callback/2
X   % mkdir bin man man/{cat,man}1
X   
XCreate a subdirectory for doing the actual compilations.  I'll
Xcall it sun34:
X   % mkdir sun34
X   % cd sun34
X
XCreate an mk shell file.  Make it executable.  Here's a starting
Xpoint for SunOS 4.x:
X  
XDD=/usr/local/free-dist/callback/2/dist
XSRCS=`cd $DD; echo *`
XRM='rm -f'
Xexport DD SRCS RM
Xmake -f uMakefile
Xexec make \
X   CATMAN=catman \
X   FMTMAN=mkManPS \
X   $*
X
XIf you don't have a catman command, drop that line.  FMTMAN
Xshould be the name of a command to turn manual entries into
Xprintable form.  Add any local changes you need here.  If your C
Xcompiler doesn't understand different optimization levels, add
XCFLAGS=-O as an argument to the last make.
X
XHere is an alternate mk file for a system using GCC, and with
Xresults installed in more conventional places.
X  
XDD=/usr/local/free-dist/callback/2/dist
XSRCS=`cd $DD; echo *`
XCC=gcc
XCFLAGS='-g -O -traditional'
XINCLUDES=-I/usr/local/include
XRM='rm -f'
Xexport DD SRCS CC CFLAGS INCLUDES RM
Xmake -f uMakefile
Xexec make \
X   CATMAN=catman \
X   CID=/usr/local/bin \
X   LIBPATH='-L/usr/local/lib' \
X   MID=/usr/local/man \
X   $*
X
XGet MakeCommon and uMakefile from the master source directory,
Xe.g.
X   % mkDistI MakeCommon $DD
X   % mkDistI uMakefile  $DD
X
XMake any changes you need to make to callback.h.  Near the top
Xare some absolute path names that you may want to change.
X
XIf your telephone line has double clearing, and if you're
Xconcerned about security, you should increase the value of the
XDROPSEC macro defined near the top of callback.h.  Double
Xclearing is a telephony feature.  A calls B and B answers.  With
Xdouble clearing, B can hang up for a short time and then pick up
Xthe call again.  This is convenient, for example, to walk to a
Xphone in a different room.  However, double clearing also makes
Xspoofing possible.  The bad guy calls up and initiates the
Xcallback, but does not hang up.  Instead, the bad guy supplies
Xdial tone.  The host modem hangs up the line for a while, and
Xwhen it goes off hook again it detects the dial tone supplied by
Xthe bad guy.  It dials the number and then waits for answer.
XThe bad guy removes the dial tone and answers.
X
XTo avoid spoofing, DROPSEC must be longer than the double
Xclearing timeout.  This is typically about 90 seconds, but you
Xcan test for yourself by having a partner call you, hanging up,
Xand timing how long you need to stay on hook before you can go
Xoff hook and get dial tone instead of your partner.
XUnfortunately, DROPSEC is the time between when the callback
Xrequest is accepted and when it is processed, so making it
Xlonger than a few seconds will annoy your users and lead them to
Xsuspect that the callbacks they request have failed.  It's much
Xbetter to get a line with single clearing, if possible.  Most
XPABX lines have single clearing.
X
XCallback as distributed assumes verbose responses from the
Xmodem.  With Hayes-compatible modems, the command "atv1" puts
Xthem in verbose mode.  If you prefer numeric response codes, you
Xwill need to modify the mrctab[] table in callback.h to handle
Xthem.  You will probably also need to modify the code in main()
Xin callback0.c to do exact matching on the result codes instead
Xof prefix matching.
X
XWhen you have the modem programmed with the configuration you
Xwish, save the configuration in non-volatile memory.  With
XHayes-compatible modems, the command "at&w" does this.
X
XRun mk.
X
XRun "mk install".
X
XMake a getty that execs callback0 instead of login.  If you have
Xsource for getty, you can use that.  If you don't, make a copy
Xof your vendor-supplied getty executable, and edit it.  I call
Xthe result getty-cb.  It should go where callback and callback0
Xgo.  I used GNU Emacs to edit a copy of /usr/etc/getty.  I used
Xincremental search to find /bin/login, then I changed it to
X/bin/.cb0 followed by an extra NUL (Ctl-Q 000) so the new string
Xtakes up exactly the same space as the old.  I then made
X/bin/.cb0 a symbolic link to where callback0 really is:
X   # mount -o remount,rw /usr  (If /usr is mounted read-only)
X   # ln -s /usr/local/free/callback/0/bin/callback0 /bin/.cb0
X
XSet up control, log, and state files.
XI put these in /etc/local/callback.  In this example there's
Xonly one callback line, ttyd1:
X   # cd /etc
X   # mkdir local
X   # chmod g+s local
X   # cd local
X   # mkdir callback
X   # chmod o= callback
X   # cd callback
X   # mkdir control log state
X   # chmod o= *
X   # touch {control,log}/ttyd1
X   # chmod o= control/* log/*
X
XEdit the control files.  See callback(1) for an example.
X
XThe log files grow without bound, so you should include them in
Xyour periodic log file trimming scripts.
X
XEdit /etc/ttytab.  The lines that use callback should have
X/usr/etc/getty replaced by a full path to callback, e.g.:
X
X   ttyd1   "/usr/local/free/callback/0/bin/callback 2400-baud" dialup          on
X
XMake the newly installed version of callback version 0:
X   # cd /usr/local/free/callback
X   # /bin/rm -f 0; ls -s 2 0
X
XHave init read the changed /etc/ttytab:
X   # kill -1 1
X--
XHoward Gayle
XTN/ETX/TT/HL
XEricsson Telecom AB
XS-126 25 Stockholm
XSweden
Xhoward@ericsson.se
Xuunet!ericsson.se!howard
XPhone: +46 8 719 5565
XFAX  : +46 8 719 8439
END_OF_FILE
if test 10750 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(6396 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile - main GNU makefile for callback
X#
X# $Header: Makefile,v 1.2 89/12/28 10:37:29 howard Exp $
X#
X# Copyright 1990 Howard Lee Gayle
X# This file is written in the ISO 8859/1 character set.
X# 
X# This program is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License version 1,
X# as published by the Free Software Foundation.
X# 
X# This program is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X# GNU General Public License for more details.
X# 
X# You should have received a copy of the GNU General Public License
X# along with this program; if not, write to the Free Software
X# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X# 
X
Xinclude MakeCommon
X
X# Commands:
X
X# Copy stdin to stdout.
XCAT=cat
X
X# Put a file into the distribution directory.
XDISTO=mkDistO
X
X# Echo arguments.
XECHO=echo
X
X# Elisp compiler.
XELCMP=emacs -batch -q -f batch-byte-compile
X
X# Install a non-executable file.
XINSTF=mkInstF
X
X# Install an executable file.
XINSTX=mkInstX
X
X# Lint command.
XLINT=lint
X
X# Make shar files.
XSHAR=trshar shar
X
X
X# Command options:
X
X# Set commands at start of Bourne shell files, usually absolute paths.
XBDEFS=
X
X# C compiler options.
XCFLAGS=-O4
X
X# C library search path.
XLIBPATH=-L/usr/local/free/howard/0
X
X# C libraries to link in.
XLIBES=-lhoward
X
X# Options to lint.
XLINTFLAGS=-abchux
X
X# Lint libraries.
XLL=/usr/local/free/howard/0/llib-howard.ln
X
X
X# Directories:
X
X# Install Bourne shell files here.
XBID=../bin
X
X# Install C executables here.
XCID=../bin
X
X# Install compiled elisp here.
XELID=../el
X
X# Install man entries here.
XMID=../man
X
X
X# Sources.  Comment out all that don't apply.
X
X# Bourne shell files.
XBS := $(filter %.b,$(SRCS))
X
X# BibTeX bibliography file.
XBIBS := $(filter %.bib,$(SRCS))
X
X# C programs.
XCS := $(filter %.c,$(SRCS))
X
X# Elisp source.
XELS := $(filter %.el,$(SRCS))
X
X# C include files.
XHS := $(filter %.h,$(SRCS))
X
X# LaTeX source.
XLATEXS := $(filter %.tex,$(SRCS))
X
X# Manuals.
XM1S := $(filter %.1,$(SRCS))# Section 1.
XM5S := $(filter %.5,$(SRCS))# Section 5.
XM8S := $(filter %.8,$(SRCS))# Section 8.
Xms  := $(M1S) $(M5S) $(M8S)# All sections.
X
X# All ordinary source files.  Removed by make clobber.
Xsrc := $(BS) $(BIBS) $(CS) $(ELS) $(HS) $(LATEXS) $(ms)
X
X# Absolutely all source files.
Xallsrc := README FREEZE MakeCommon Makefile uMakefile $(src)
X
X
X# Targets and installs:
X
X# Bourne shell files.
Xifdef BS
Xbt := $(subst .b,,$(BS))
Xbi := $(patsubst %,$(BID)/%,$(bt))
Xendif
X
X# C programs.
Xifdef CS
Xct := $(subst .c,,$(CS))
Xci := $(patsubst %,$(CID)/%,$(ct))
Xlt := $(patsubst %.c,%-l,$(CS))# Phony lint targets for C programs.
Xst := $(subst .c,.s,$(CS))# Assembler targets for C programs.
Xendif
X
X# Compiled elisp.
Xifdef ELS
Xelt := $(subst .el,.elc,$(ELS))
Xeli := $(patsubst %,$(ELID)/%,$(elt))
Xendif
X
X# LaTeX.
Xifdef FMTLATEX
Xlatext := $(subst .tex,.texf,$(LATEXS))
Xspellt := $(patsubst %.tex,%-s,$(LATEXS))#Phony targets for spelling checking.
Xendif
X
X# Manual entries.
Xifdef M1S
Xcat1i := $(patsubst %,$(MID)/cat1/%,$(M1S))
Xm1i   := $(patsubst %,$(MID)/man1/%,$(M1S))
Xendif
Xifdef M5S
Xcat5i := $(patsubst %,$(MID)/cat5/%,$(M5S))
Xm5i   := $(patsubst %,$(MID)/man5/%,$(M5S))
Xendif
Xifdef M8S
Xcat8i := $(patsubst %,$(MID)/cat8/%,$(M8S))
Xm8i   := $(patsubst %,$(MID)/man8/%,$(M8S))
Xendif
Xcati := $(cat1i) $(cat5i) $(cat8i)
Xmi   := $(m1i) $(m5i) $(m8i)
X
Xifdef FMTMAN
Xft := $(patsubst %,%.f,$(ms))#Formatted manual entries, e.g. PostScript.
Xendif
X
Xtargets := $(bt) $(ct) $(elt) $(ft) $(latext)
X
X# Distribution:
Xdist := $(patsubst %,$(DD)/%,$(allsrc) $(ft) $(latext))
X
X# Don't use built-in rules.
X.SUFFIXES:
X.PHONY: clean clobber default dist install shar vars $(lt) $(spellt)
X
Xdefault: $(targets)
X
X# Display values of various variables.  Mostly for debugging.
Xvars:
X	@$(ECHO) 'SRCS:' $(SRCS)
Xifdef BS
X	@$(ECHO) 'BS:' $(BS)
X	@$(ECHO) 'bt:' $(bt)
X	@$(ECHO) 'bi:' $(bi)
Xendif
Xifdef CS
X	@$(ECHO) 'CS:' $(CS)
X	@$(ECHO) 'ct:' $(ct)
X	@$(ECHO) 'ci:' $(ci)
X	@$(ECHO) 'lt:' $(lt)
X	@$(ECHO) 'st:' $(st)
X	@$(ECHO) 'CC:' $(CC)
X	@$(ECHO) 'CFLAGS:' $(CFLAGS)
X	@$(ECHO) 'DEFS:' $(DEFS)
X	@$(ECHO) 'LIBPATH:' $(LIBPATH)
X	@$(ECHO) 'LIBES:' $(LIBES)
Xendif
Xifdef ELS
X	@$(ECHO) 'ELS:' $(ELS)
X	@$(ECHO) 'elt:' $(elt)
X	@$(ECHO) 'eli:' $(eli)
Xendif
Xifdef HS
X	@$(ECHO) 'HS:' $(HS)
Xendif
Xifdef FMTLATEX
X	@$(ECHO) 'LATEXS:' $(LATEXS)
X	@$(ECHO) 'BIBS:' $(BIBS)
X	@$(ECHO) 'latext:' $(latext)
X	@$(ECHO) 'spellt:' $(spellt)
Xendif
Xifdef M1S
X	@$(ECHO) 'M1S:' $(M1S)
X	@$(ECHO) 'cat1i:' $(cat1i)
X	@$(ECHO) 'm1i:' $(m1i)
Xendif
Xifdef M5S
X	@$(ECHO) 'M5S:' $(M5S)
X	@$(ECHO) 'cat5i:' $(cat5i)
X	@$(ECHO) 'm5i:' $(m5i)
Xendif
Xifdef M8S
X	@$(ECHO) 'M8S:' $(M8S)
X	@$(ECHO) 'cat8i:' $(cat8i)
X	@$(ECHO) 'm8i:' $(m8i)
Xendif
X	@$(ECHO) 'mi:' $(mi)
Xifdef FMTMAN
X	@$(ECHO) 'ft:' $(ft)
Xendif
X	@$(ECHO) 'allsrc:' $(allsrc)
X	@$(ECHO) 'dist:' $(dist)
X
Xclean:
X	-$(RM) depend $(st)
X
Xclobber: clean
X	-$(RM) $(src) $(targets) FREEZE FREEZE.*
X
Xifdef BS
X$(bt): %: %.b
X	$(RM) $@
X	$(ECHO) '  $(BDEFS)' > $@
X	$(CAT) $< >> $@
X	$(CHMOD) +x $@
Xendif
X
Xifdef CS
X$(ct): %: %.c
X	$(CC) -o $@ $(CFLAGS) $(DEFS) $(INCLUDES) $(LIBPATH) $*.c $(LIBES)
X
X$(lt): %-l: %.c $(LL)
X	$(LINT) $(LINTFLAGS) $(DEFS) $(INCLUDES) $< $(LL)
Xendif
X
Xifdef ELS
X$(elt): %.elc: %.el
X	$(ELCMP) $<
Xendif
X
Xifdef FMTMAN
X$(ft): %.f: %
X	$(RM) $@
X	$(FMTMAN) $< > $@
Xendif
X
Xifdef FMTLATEX
X$(latext): %.texf: %.tex $(BIBS)
X	$(FMTLATEX) $* $@
X
X$(spellt): %-s: %.tex
X	$(SPELL_LATEX) $<
Xendif
X
Xshar: $(allsrc)
X	$(SHAR) $(allsrc)
X
Xifdef CS
X$(st): %.s: %.c
X	$(CC) -S $(DEFS) $(INCLUDES) $<
Xendif
X
Xifdef WORK
X
Xdist: $(dist)
X
X$(dist): $(DD)/%: %
X	$(DISTO) $< $(DD)
X
X.DEFAULT:
X	$(UNCMPRS) $@
X
Xelse # WORK
X
Xinstall: $(bi) $(ci) $(cati) $(eli) $(mi)
X
Xifdef BS
X$(bi): $(BID)/%: %
X	$(INSTF) $< $(BID)
Xendif
X
Xifdef CS
X$(ci): $(CID)/%: %
X	$(INSTX) $< $(CID)
Xendif
X
Xifdef ELS
X$(eli): $(ELID)/%: %
X	$(INSTF) $< $(ELID)
Xendif
X
Xifdef M1S
X$(m1i): $(MID)/man1/%: %
X	$(INSTF) $< $(MID)/man1
X
Xifdef CATMAN
X$(cat1i): $(m1i)
X	$(CATMAN) -M $(MID) 1
Xendif
Xendif
X
Xifdef M5S
X$(m5i): $(MID)/man5/%: %
X	$(INSTF) $< $(MID)/man5
X
Xifdef CATMAN
X$(cat5i): $(m5i)
X	$(CATMAN) -M $(MID) 5
Xendif
Xendif
X
Xifdef M8S
X$(m8i): $(MID)/man8/%: %
X	$(INSTF) $< $(MID)/man8
X
Xifdef CATMAN
X$(cat8i): $(m8i)
X	$(CATMAN) -M $(MID) 8
Xendif
Xendif
X
X$(allsrc): %: $(DD)/%
X	$(DISTI) $@ $(DD)
X
X.DEFAULT:
X	$(DISTI) $@ $(DD)
X
Xendif # WORK
X
Xifdef CS
Xinclude depend
Xendif
END_OF_FILE
if test 6396 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'callback.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'callback.c'\"
else
echo shar: Extracting \"'callback.c'\" \(3822 characters\)
sed "s/^X//" >'callback.c' <<'END_OF_FILE'
X/*
X * callback - forked from init process for dialup lines
X */
X
X#ifndef lint
Xstatic char _cpyrgt[] = "Copyright 1990 Howard Lee Gayle";
X#endif lint
X
X/*
X * This program is free software; you can redistribute it and/or modify
X * it under the terms of the GNU General Public License version 1,
X * as published by the Free Software Foundation.
X *
X * This program is distributed in the hope that it will be useful,
X * but WITHOUT ANY WARRANTY; without even the implied warranty of
X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X * GNU General Public License for more details.
X *
X * You should have received a copy of the GNU General Public License
X * along with this program; if not, write to the Free Software
X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X */
X
X#include <stdio.h>
X#include <howard/port.h>
X#include <howard/version.h>
X#include <howard/usage.h>
X
XMAINVER ("@(#)$Header: callback.c,v 1.11 89/12/28 10:34:24 howard Exp $");
XUSAGE ("speed ttyd*");
X
X#include <fcntl.h>
X#include "callback.h"
X
X/* close01 - close fd 0 & 1 & report errors */
X
XPRIVATE void close01()
X
X/* Function:
X *    Close standard input and standard output.  Call malf1() on error.
X * Algorithm:
X *    
X * Notes:
X *    
X */
X{
Xif (close (0)) malf1 ("Can not close 0");
Xif (close (1)) malf1 ("Can not close 1");
X}
X
X/* main - main function							*/
X
XPUBLIC int main (argc, argv)
X   int    argc; /* Number of arguments.*/
XR1 bStrT *argv; /* Points to array of argument strings.*/
X
X/* Function:
X *    Exec either the special getty or login.
X * Algorithm:
X *    Set real & effective group IDs to 0.  Close standard input,
X *    output, and error.  Get suffix of ttyd* argument and call
X *    initTty() to append it to the tty-dependent file names.
X *    Open log file and make standard error go to it.  Write
X *    initial log file message.  Try to open state file.  If there
X *    is no state file, exec the special getty.  But if there is
X *    a state file, read it in and remove it.  Open standard input
X *    and output as the tty.  Set the tty to the
X *    correct modes for login.  Make standard error correspond to
X *    the tty.  Exec login.
X * Notes:
X *	
X */
X
X{
XR2 streamT         sis; /* State file input stream.*/
X   stateT          st;  /* State file contents.*/
X   struct termios  ts;  /* TTY status.*/
X
Xif (-1 == setregid (0, 0)) exit (3);
X(void) close (0);
X(void) close (1);
X(void) close (2);
Xif (argc < 3) exit (7);
XinitTty (S("ttyd"), argv[2]);
Xif (0 != open (lfn, O_APPEND | O_CREAT, 0640)) exit (4);
Xif (1 != dup (0)) exit (5);
Xif (2 != dup (0)) exit (6);
XinitLog ("callback");
Xsis = fopen (statefn, "r");
Xif (NULSTRM == sis)
X   {
X   FPRINTF (ls, "No %s\n", statefn);
X   FFLUSH (ls);
X   close01();
X   if (fclose (stderr))
X      {
X      FPRINTF (ls, "Can not close stderr\n");
X      exit (1);
X      }
X   logArgs ((bStrT) getty, argv);
X   execv (getty, argv);
X   FPRINTF (ls, "%s: can not exec\n", getty);
X   exit (1);
X   }
Xif ((1 != fread ((cStrT) &st, sizeof (stateT), 1, sis)) || ferror (sis))
X   malf1 ("%s: Read error", statefn);
Xmfclose (sis, statefn);
Xif (unlink (statefn)) malf1 ("%s: Can not unlink", statefn);
XFPRINTF (ls, "Rm %s\n", statefn);
XFFLUSH (ls);
Xclose01();
Xif (0 != open (tn, O_RDWR)) malf1 ("%s: Can not open read/write", tn);
Xif (1 != dup (0)) malf1 ("Can not dup 1");
Xgts (&ts);
Xts.c_iflag = (IGNPAR | IMAXBEL);
Xts.c_cflag = st.stCflag;
Xts.c_lflag |= (ECHO | ECHOE | ECHOK | TOSTOP | ECHOCTL | ECHOKE);
Xsts (&ts);
Xif (fclose (stderr))
X   {
X   FPRINTF (ls, "Can not close stderr\n");
X   exit (1);
X   }
Xif (2 != dup (0))
X   {
X   FPRINTF (ls, "Can not dup 2\n");
X   exit (1);
X   }
XFPRINTF (ls, "%s -p %s\n", login, st.stName);
XFFLUSH (ls);
Xexecl (login, "login", "-p", st.stName, NULCSTR);
XFPRINTF (ls, "%s: can not exec\n", login);
Xexit (1);
X#ifdef lint
Xreturn (SUCCESS);
X#endif
X}
END_OF_FILE
if test 3822 -ne `wc -c <'callback.c'`; then
    echo shar: \"'callback.c'\" unpacked with wrong size!
fi
# end of 'callback.c'
fi
if test -f 'callback.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'callback.h'\"
else
echo shar: Extracting \"'callback.h'\" \(6921 characters\)
sed "s/^X//" >'callback.h' <<'END_OF_FILE'
X/*
X * callback.h - Common include file for callback system.
X *
X * Copyright 1990 Howard Lee Gayle
X *
X * $Header: callback.h,v 1.6 89/12/28 16:43:44 howard Exp $
X *
X * This program is free software; you can redistribute it and/or modify
X * it under the terms of the GNU General Public License version 1,
X * as published by the Free Software Foundation.
X *
X * This program is distributed in the hope that it will be useful,
X * but WITHOUT ANY WARRANTY; without even the implied warranty of
X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X * GNU General Public License for more details.
X *
X * You should have received a copy of the GNU General Public License
X * along with this program; if not, write to the Free Software
X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X *
X * Prerequisites: howard/port.h.
X */
X
X#include <errno.h>
X#include <string.h>
X#include <time.h>
X#include <sys/termios.h>
X#include <howard/malf.h>
X#include <howard/registers.i>
X
X#define ABORTSEC 120 /* Abort connection attempt after this many seconds.*/
X#define DROPSEC   10 /* Time to leave line hung up (seconds).*/
X
Xtypedef struct /* One entry in the modem reply code table mrctab[].*/
X   {
X   bStrT mrcStr;   /* Prefix of string from modem.*/
X   boolT mrcRetry; /* True iff retry on this string.*/
X   } mrcT;
X
Xtypedef struct /* Contents of a state file. */
X   {
X   ulongT stCflag;   /* c_cflag as set by getty(8).*/
X   char   stName[9]; /* Login name.*/
X   } stateT;
X
X/* Full path names of tty-specific files.  A suffix is added by initTty().*/
XPRIVATE char ctlfn  [MFILE] = "/etc/local/callback/control/ttyd"; /* Control.*/
XPRIVATE char lfn    [MFILE] = "/etc/local/callback/log/ttyd";     /* Log.*/
XPRIVATE char statefn[MFILE] = "/etc/local/callback/state/ttyd";   /* State.*/
XPRIVATE char cun    [MFILE] = "/dev/cua"; /* Dial-out device.*/
XPRIVATE char tn     [MFILE] = "/dev/ttyd"; /* TTY name.*/
X
X/* Executable of getty that execs callback0 instead of login: */
XPRIVATE char getty[] = "/usr/local/free/callback/0/bin/getty-cb";
X
XPRIVATE char    login[] = "/bin/login"; /* Login executable.*/
XPRIVATE streamT ls;                     /* Log file stream.*/
X
XPRIVATE mrcT mrctab[] = /* Modem reply code table.*/
X   {
X   S("BUSY"),        TRUE,
X   S("CONNECT"),     FALSE,
X   S("NO ANSWER"),   TRUE,
X   S("NO CARRIER"),  TRUE,
X   S("NO DIALTONE"), TRUE,
X   NULBSTR,          FALSE,
X   };
X
X/* User messages: */
XPRIVATE char umBye[] = "BYE bye\n"; /* Correct symbolic phone #.*/
XPRIVATE char umNo [] = "NO no\n";   /* Unknown symbolic phone #.*/
X
XPRIVATE cStrT speeds[] = /* Line speed as string.*/
X   {
X   "0",
X   "50",
X   "75",
X   "110",
X   "134.5",
X   "150",
X   "200",
X   "300",
X   "600",
X   "1200",
X   "1800",
X   "2400",
X   "4800",
X   "9600",
X   "19200",
X   "38400"
X   };
X
Xextern long time(); /* (3C).*/
X
XPRIVATE void gts();
XPRIVATE void initLog();
XPRIVATE void initTty();
XPRIVATE void logArgs();
XPRIVATE void logMMSS();
XPRIVATE void sts();
X
X/* gts - get tty status */
X
XPRIVATE void gts (tp)
XR1 struct termios *tp; /* Points to place to store result.*/
X
X/* Function:
X *    Call the TCGETS ioctl.  Log the results and return them.
X * Algorithm:
X *    
X * Notes:
X *    
X */
X{
Xint i;   /* Process group.*/
Xint mcl; /* Modem control lines.*/
X
XlogMMSS();
Xif (-1 == ioctl (0, TCGETS, tp)) malf1 ("TCGETS failed");
Xif (-1 == ioctl (0, TIOCMGET, &mcl)) malf1 ("TIOCMGET failed");
Xif (-1 == ioctl (0, TIOCGPGRP, &i)) malf1 ("TIOCGPGRP failed");
XFPRINTF (ls, "i: %5lo o: %6lo c: %7lo l: %6lo line: %d m: %3o pgrp: %5d\n",
X         tp->c_iflag, tp->c_oflag, tp->c_cflag, tp->c_lflag, tp->c_line,
X         mcl, i);
XFFLUSH (ls);
X}
X
X/* initLog - start logging */
X
XPRIVATE void initLog (cn)
XbStrT cn; /* Command name.*/
X
X/* Function:
X *    Open log file.  Write initial message.  Make standard error
X *    go to log file.
X * Algorithm:
X *    Open the log file for appending to the end, and exit on error.
X *    Print an initial message.  Flush.  Close file descriptor 2,
X *    then dup the log file's descriptor to be file descriptor 2.
X *    Test malf0().  Set the close-on-exec bit.
X * Notes:
X *    1) Once stderr corresponds to the log file, malf can be used.
X *    2) The close-on-exec bit is set on the file descriptor from
X *       the original open on the log file, but not on file descriptor 2.
X */
X{
X   long       ut;  /* Current system time.*/
XR1 struct tm *tmp; /* Returned by localtime().*/
X
Xls = fopen (lfn, "a+");
Xif (NULSTRM == ls) exit (2);
Xut = time ((long *) NULL);
Xtmp = localtime (&ut);
XFPRINTF (ls,
X         "\n\f\n%d-%02d-%02d %02d:%02d:%02d %s pid: %d pgrp: %d\n",
X         1900 + tmp->tm_year, 1 + tmp->tm_mon, tmp->tm_mday,
X         tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
X         cn, getpid(), getpgrp (0));
XFFLUSH (ls);
Xif (close (2))
X   {
X   FPRINTF (ls, "Can not close 2");
X   exit (1);
X   }
Xif (2 != dup (fileno (ls)))
X   {
X   FPRINTF (ls, "Can not dup %d", fileno (ls));
X   exit (1);
X   }
Xerrno = 0;
Xmalf0 ("stderr OK");
Xif (-1 == fcntl (fileno (ls), F_SETFD, 1)) malf1 ("Can not SETFD");
X}
X
X/* initTty - initialize tty-dependent file names */
X
XPRIVATE void initTty (pfx, ftn)
XbStrT pfx; /* Expected prefix of tty name.*/
XbStrT ftn; /* Full tty name.*/
X
X/* Function:
X *    Append argument to the tty-dependent file names.
X * Algorithm:
X *    
X * Notes:
X *    1) There is no checking for overflow.
X */
X{
XR1 bStrT s = prefix (pfx, ftn); /* Suffix of tty name.*/
X
Xif ((NULBSTR == s) || (EOS == B(*s))) exit (8);
XSTRCAT (ctlfn,   (cStrT) s);
XSTRCAT (cun,     (cStrT) s);
XSTRCAT (lfn,     (cStrT) s);
XSTRCAT (statefn, (cStrT) s);
XSTRCAT (tn,      (cStrT) s);
X}
X
X/* logArgs - write vector of command arguments to log file */
X
XPRIVATE void logArgs (cn, v)
XbStrT  cn; /* Command name.*/
XbStrT *v;  /* Argument vector.*/
X
X/* Function:
X *    Write command name followed by arguments to log file.
X * Algorithm:
X *    
X * Notes:
X *    
X */
X{
XFPUTS (cn, ls);
Xfor (; NULBSTR != *v; ++v)
X   {
X   PUTC (' ', ls);
X   FPUTS (*v, ls);
X   }
XPUTC ('\n', ls);
XFFLUSH (ls);
X}
X
X/* logMMSS - log minutes & seconds */
X
XPRIVATE void logMMSS()
X
X/* Function:
X *    Write current minutes and seconds to log file.
X * Algorithm:
X *    
X * Notes:
X *    1) This function is typically called to start each line written
X *       to the log file.  The initial log message gives the complete
X *       date and time, so it's only necessary to log minutes & seconds.
X */
X{
X   long       ut;  /* Current system time.*/
XR1 struct tm *tmp; /* Returned by localtime().*/
X
Xut = time ((long *) NULL);
Xtmp = localtime (&ut);
XFPRINTF (ls, "%02d%02d ", tmp->tm_min, tmp->tm_sec);
X}
X
X/* sts - set tty status */
X
XPRIVATE void sts (tp)
Xstruct termios *tp;
X
X/* Function:
X *    Call TCSETS ioctl.  Flush.  Call gts().
X * Algorithm:
X *    
X * Notes:
X *    1) Calling gts() at the end verifies and logs what was set.
X */
X{
Xif (-1 == ioctl (0, TCSETS, tp)) malf1 ("TCSETS failed");
Xif (-1 == ioctl (0, TCFLSH, 2)) malf1 ("TCFLSH failed");
Xgts (tp);
X}
END_OF_FILE
if test 6921 -ne `wc -c <'callback.h'`; then
    echo shar: \"'callback.h'\" unpacked with wrong size!
fi
# end of 'callback.h'
fi
if test -f 'callback0.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'callback0.c'\"
else
echo shar: Extracting \"'callback0.c'\" \(8757 characters\)
sed "s/^X//" >'callback0.c' <<'END_OF_FILE'
X/*
X * callback0 - get symbolic phone # from user & dial modem
X */
X
X#ifndef lint
Xstatic char _cpyrgt[] = "Copyright 1990 Howard Lee Gayle";
X#endif lint
X
X/*
X * This program is free software; you can redistribute it and/or modify
X * it under the terms of the GNU General Public License version 1,
X * as published by the Free Software Foundation.
X *
X * This program is distributed in the hope that it will be useful,
X * but WITHOUT ANY WARRANTY; without even the implied warranty of
X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X * GNU General Public License for more details.
X *
X * You should have received a copy of the GNU General Public License
X * along with this program; if not, write to the Free Software
X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
X */
X
X#include <stdio.h>
X#include <howard/port.h>
X#include <howard/version.h>
X#include <howard/usage.h>
X
XMAINVER ("@(#)$Header: callback0.c,v 1.6 89/12/28 10:34:27 howard Exp $");
XUSAGE ("flag name");
X
X#include <fcntl.h>
X#include <signal.h>
X#include "callback.h"
X
X#define MLINE 80 /* Max input line length.*/
X#define MCTLL 1024 /* Max control file line.*/
X
XPRIVATE byteT lb[MLINE]; /* Input line.*/
XPRIVATE boolT talking; /* Talking to user.*/
X
XPRIVATE void ws();
X
X/* getmcs - look up modem control line */
X
XPRIVATE void getmcs (ks, mcs)
XR4 cStrT ks;  /* Key string.  Must end in space.*/
XR5 cStrT mcs; /* Store modem control string here.*/
X
X/* Function:
X *    Search the control file for a line starting with the given key.
X * Algorithm:
X *    Linear search.  Call getlic() to read each line and strip comments.
X * Returns:
X *    On success, mcs points to the string to send to the modem to dial
X *    the number.  On error, mcs points to the empty string.
X * Notes:
X *    1) There is no overflow checking when copying into mcs.
X */
X{
XR2     streamT  cs;        /* Modem control file stream.*/
X       unsigned ln = 0;    /* Input line number.*/
XR3     boolT    m;         /* Loop control.*/
XR1     bStrT    p;         /* Returned by prefix (3 -lhoward).*/
X       byteT    ib[MCTLL]; /* Input line buffer.*/
Xextern bStrT    getlic() ; /* (3 -lhoward).*/
X
Xcs = mfopen (ctlfn, "r");
Xfor (m = TRUE; m;)
X   {
X   if (NULBSTR == getlic (ib, MCTLL, cs, ctlfn, &ln, 1, ';'))
X      {
X      mcs[0] = EOS;
X      m = FALSE;
X      }
X   else if (NULBSTR != (p = prefix (ks, ib)))
X      {
X      while ((EOS != *p) && (' ' == *p))
X         ++p;
X      if (EOS == *p) malf1 ("%s: %u: No control string", ctlfn, ln);
X      STRCPY (mcs, p);
X      STRCAT (mcs, "\n");
X      m = FALSE;
X      }
X   }
Xmfclose (cs, ctlfn);
X}
X
X/* rs - read string from fd 0 */
X
XPRIVATE void rs()
X
X/* Function:
X *    Read a line from standard input and log it.
X * Algorithm:
X *    Call read().  On error call malf1().  If characters are read,
X *    NUL-terminate the result.  Treat EOF as an empty string.
X *    Write the result on the log file.
X * Returns:
X *    The result is stored in lb[].
X * Notes:
X *    
X */
X{
XR1 int i; /* Number of bytes read.*/
X
Xi = read (0, lb, MLINE);
Xif (-1 == i) malf1 ("Read error on fd 0");
Xif (i > 0)
X   lb[i - 1] = EOS;
Xelse if (0 == i)
X   lb[0] = EOS;
XlogMMSS();
XFPUTS (lb, ls);
XPUTC ('\n', ls);
XFFLUSH (ls);
X}
X
X/* sigalrm - print message if signal arrives */
X
XPRIVATE void sigalrm ()
X
X/* Function:
X *    This function is called on timeout.  Write a message to the user and
X *    the log file, then exit.
X * Algorithm:
X *    
X * Notes:
X *    1) The sleep() is to give the output tty time to flush before closing.
X */
X{
XlogMMSS();
XFPUTS ("timeout\n", ls);
XFFLUSH (ls);
Xif (talking) ws ("TIMEOUT timeout\n");
Xsleep (1);
Xexit (3);
X}
X
X/* ws - write a string on fd 1 */
X
XPRIVATE void ws (s)
XR1 cStrT s; /* String to write.*/
X
X/* Function:
X *    Write string s on standard output and to the log file.
X * Algorithm:
X *    
X * Notes:
X *    1) If s does not end in a newline, an extra newline is written
X *       to the logfile.
X */
X{
XR2 int i; /* Length of string.*/
X
Xi = strlen (s);
Xif (i != write (1, s, i)) malf1 ("Write error on fd 1");
XPUTC ('>', ls);
XFPUTS (s, ls);
Xif ('\n' != s[i - 1]) PUTC ('\n', ls);
XFFLUSH (ls);
X}
X
X/* main - main function							*/
X
XPUBLIC int main (argc, argv)
X   int    argc; /* Number of arguments.*/
XR6 bStrT *argv; /* Points to array of argument strings.*/
X
X/* Function:
X *    Do callback.
X * Algorithm:
X *    Set real and effective group ID to 0.  Initialize.  Set tty
X *    for user interaction.  Prompt user and get symbolic phone number.
X *    Look it up in modem control file.  On failure, write error message,
X *    sleep a little to discourage brute force attacks, and repeat until
X *    success or timeout.  On success, hang up the line.  Sleep long
X *    enough to make sure the line is really hung up.  Open the dial-out
X *    (cua) device.  Write the string from the control file, read
X *    the responses, and throw them away until one matches a prefix
X *    in the modem reply code table.  Retry if the table says to,
X *    until timeout.  On success, turn off the hang-up-on-close bit,
X *    create the state file, and exit.
X * Notes:
X *    1) This program is exec'ed by the speciall getty instead of login,
X *       so the tty is already set up correctly (speed, parity, case, etc.)
X *    2) Turning off the HUPCL bit just before exiting means the line
X *       does not get dropped on exit.  On exit, init starts up a new
X *       callback process, which, because the state file is present,
X *       execs a normal login.
X */
X
X{
XR2     boolT          m;              /* Loop control.*/
XR1     mrcT          *rp;             /* Steps through mrctab[].*/
XR4     streamT        ss;             /* State file stream.*/
X       ulongT         sp;             /* Line speed as code.*/
XR5     cStrT          sps;            /* Line speed as string.*/
XR3     unsigned       tries = 0;      /* Callback tries.*/
X       stateT         st;             /* Contents of state file.*/
X       struct termios ts;             /* TTY status.*/
X       char           prompt[12];     /* Prompt for user.*/
X       char           mcs[MCTLL];     /* Modem control string.*/
X       char           key[2 * MLINE]; /* Key in control file.*/
Xextern cStrT          ttyname();      /* (3).*/
X
Xif (-1 == setregid (0, 0)) exit (3);
XinitTty ((bStrT) tn, (bStrT) ttyname (0));
XinitLog (S("callback0"));
XlogArgs (S("argv:"), argv);
Xif ((argc < 3) || (strlen ((cStrT) argv[2]) > 8)) usage();
Xgts (&ts);
Xts.c_iflag |= (IGNPAR | IMAXBEL);
Xts.c_lflag |= (ECHO | ECHOE | ECHOK | TOSTOP | ECHOCTL | ECHOKE);
Xsts (&ts);
Xsp = CBAUD & ts.c_cflag;
Xif ((sp < B50) || (sp > B38400)) malf1 ("Bad line speed: %lu", sp);
Xsps = speeds[sp];
XFPRINTF (ls, "Speed %lu = %s\n", sp, sps);
XFFLUSH (ls);
XSPRINTF (prompt, "%s %s", sps, sps);
Xif (-1 == (int) signal (SIGALRM, sigalrm)) malf1 ("Can not signal");
Xtalking = TRUE;
X(void) alarm (ABORTSEC);
Xfor (m = TRUE; m;)
X   {
X   ws (prompt);
X   rs();
X   if (EOS != lb[0])
X      {
X      SPRINTF (key, "%s@%s ", lb, sps);
X      getmcs (key, mcs);
X      if (EOS != mcs[0])
X         m = FALSE;
X      else
X         {
X         sleep (1);
X         ws (umNo);
X         sleep (4);
X         }
X      }
X   }
Xtalking = FALSE;
Xws (umBye);
X(void) alarm (ABORTSEC);
Xst.stCflag = ts.c_cflag;
XSTRCPY (st.stName, (cStrT) argv[2]);
Xsleep (1);
XFPUTS ("Closing 1\n", ls);
XFFLUSH (ls);
Xif (close (1)) malf1 ("Can not close 1");
Xts.c_cflag &= ~(CBAUD | CIBAUD | HUPCL);
XFPUTS ("TCSETS\n", ls);
XFFLUSH (ls);
Xif (-1 == ioctl (0, TCSETS, &ts)) malf1 ("TCSETS failed");
XFPUTS ("TCFLSH\n", ls);
XFFLUSH (ls);
Xif (-1 == ioctl (0, TCFLSH, 2)) malf1 ("TCFLSH failed");
XFPUTS ("Closing 0\n", ls);
XFFLUSH (ls);
Xif (close (0)) malf1 ("Can not close 0");
XFPUTS ("sleep...", ls);
XFFLUSH (ls);
Xsleep (DROPSEC);
XFPUTS ("ok\n", ls);
XFFLUSH (ls);
Xif (0 != open (cun, O_RDWR)) malf1 ("%s: Can not open read/write", cun);
Xif (1 != dup (0)) malf1 ("Can not dup 1");
Xgts (&ts);
Xts.c_iflag |= (IGNPAR | IMAXBEL);
Xts.c_cflag = st.stCflag;
Xts.c_lflag &= ~ECHO;
Xsts (&ts);
Xfor (m = TRUE; m;)
X   {
X   ++tries;
X   ws (mcs);
X   do
X      {
X      rs();
X      rp = mrctab;
X      while ((NULBSTR != rp->mrcStr) && (NULBSTR == prefix (rp->mrcStr, lb)))
X         ++rp;
X      }
X   while (NULBSTR == rp->mrcStr);
X   m = rp->mrcRetry;
X   }
X(void) alarm (0);
Xts.c_cflag = st.stCflag & (~HUPCL);
Xts.c_lflag |= ECHO;
Xsts (&ts);
Xss = mfopen (statefn, "w");
Xif ((1 != fwrite ((cStrT) &st, sizeof (stateT), 1, ss)) || ferror (ss))
X   malf1 ("%s: Write error", statefn);
Xmfclose (ss, statefn);
XSPRINTF (prompt, "%s %u %s\n", sps, tries, sps);
Xws (prompt);
XFPUTS ("Closing stdin\n", ls);
XFFLUSH (ls);
Xif (close (0)) malf1 ("Can not close 0");
XFPUTS ("Closing stdout\n", ls);
XFFLUSH (ls);
Xif (close (1)) malf1 ("Can not close 1");
XFPUTS ("Exit\n", ls);
XFFLUSH (ls);
Xexit (0);
X#ifdef lint
Xreturn (SUCCESS);
X#endif
X}
END_OF_FILE
if test 8757 -ne `wc -c <'callback0.c'`; then
    echo shar: \"'callback0.c'\" unpacked with wrong size!
fi
# end of 'callback0.c'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0