BSD 4_3_Tahoe development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 22 Oct 1987 07:17:05 +0000 (23:17 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Thu, 22 Oct 1987 07:17:05 +0000 (23:17 -0800)
Work on file usr/src/new/nntp/server/README
Work on file usr/src/new/nntp/server/SYSLOG
Work on file usr/src/new/nntp/server/profile.c
Work on file usr/src/new/nntp/server/time.h
Work on file usr/src/new/nntp/support/Makefile
Work on file usr/src/new/nntp/support/access_file
Work on file usr/src/new/nntp/support/README
Work on file usr/src/new/nntp/rrn/README
Work on file usr/src/new/nntp/support/nntp_awk
Work on file usr/src/new/nntp/rrn/MAKE_RRN_CHANGES
Work on file usr/src/new/nntp/xmit/get_tcp_conn.c
Work on file usr/src/new/nntp/xmit/get_tcp_conn.h
Work on file usr/src/new/nntp/rrn/README_RRN
Work on file usr/src/new/nntp/xmit/nntpxmit.h
Work on file usr/src/new/nntp/xmit/Makefile
Work on file usr/src/new/nntp/xmit/README
Work on file usr/src/new/nntp/xmit/nntpxmit.c
Work on file usr/src/new/nntp/xmit/remote.c
Work on file usr/src/new/nntp/xmit/llist.c
Work on file usr/src/new/nntp/xmit/rfc822.c
Work on file usr/src/new/nntp/xmit/shlock.c
Work on file usr/src/new/nntp/xmit/nntpsend
Work on file usr/src/new/nntp/xmit/llist.h
Work on file usr/src/new/nntp/README
Work on file usr/src/new/nntp/Makefile
Work on file usr/src/new/nntp/CHANGES
Work on file usr/src/new/nntp/xfer/nntpxfer.c
Work on file usr/src/new/nntp/xfer/README

Synthesized-from: CSRG/cd2/4.3tahoe

28 files changed:
usr/src/new/nntp/CHANGES [new file with mode: 0644]
usr/src/new/nntp/Makefile [new file with mode: 0644]
usr/src/new/nntp/README [new file with mode: 0644]
usr/src/new/nntp/rrn/MAKE_RRN_CHANGES [new file with mode: 0755]
usr/src/new/nntp/rrn/README [new file with mode: 0644]
usr/src/new/nntp/rrn/README_RRN [new file with mode: 0644]
usr/src/new/nntp/server/README [new file with mode: 0644]
usr/src/new/nntp/server/SYSLOG [new file with mode: 0644]
usr/src/new/nntp/server/profile.c [new file with mode: 0644]
usr/src/new/nntp/server/time.h [new file with mode: 0644]
usr/src/new/nntp/support/Makefile [new file with mode: 0644]
usr/src/new/nntp/support/README [new file with mode: 0644]
usr/src/new/nntp/support/access_file [new file with mode: 0644]
usr/src/new/nntp/support/nntp_awk [new file with mode: 0644]
usr/src/new/nntp/xfer/README [new file with mode: 0644]
usr/src/new/nntp/xfer/nntpxfer.c [new file with mode: 0644]
usr/src/new/nntp/xmit/Makefile [new file with mode: 0644]
usr/src/new/nntp/xmit/README [new file with mode: 0644]
usr/src/new/nntp/xmit/get_tcp_conn.c [new file with mode: 0644]
usr/src/new/nntp/xmit/get_tcp_conn.h [new file with mode: 0644]
usr/src/new/nntp/xmit/llist.c [new file with mode: 0644]
usr/src/new/nntp/xmit/llist.h [new file with mode: 0644]
usr/src/new/nntp/xmit/nntpsend [new file with mode: 0755]
usr/src/new/nntp/xmit/nntpxmit.c [new file with mode: 0644]
usr/src/new/nntp/xmit/nntpxmit.h [new file with mode: 0644]
usr/src/new/nntp/xmit/remote.c [new file with mode: 0644]
usr/src/new/nntp/xmit/rfc822.c [new file with mode: 0644]
usr/src/new/nntp/xmit/shlock.c [new file with mode: 0644]

diff --git a/usr/src/new/nntp/CHANGES b/usr/src/new/nntp/CHANGES
new file mode 100644 (file)
index 0000000..8bbc809
--- /dev/null
@@ -0,0 +1,237 @@
+
+    This file describes the changes which have been made in NNTP
+since the initial release.  Individuals who either reported or
+inspired the bug/bug fix are in square brackets.
+
+1.4    October 15, 1987
+
+       Reorganized documentation directory.  Thanks for the
+       extraction stuff, Stan.  [Stan Barber, sob%%soma.uucp@rice.edu]
+
+       Added transfer timeouts.  [Steve Schoch, schoch@ames.arpa]
+
+       Fixed a problem with IHAVE which allowed a remote machine to
+       repeatedly feed you articles that you expired (although all
+       you'd do with them is throw them away).
+       [Fred Avolio, avolio@decuac.dec.com]
+
+       DECNet support (see server/dnet_access.c and common/clientlib.c).
+        [Matt Thomas, thomas%syrah.dec@decwrl.dec.com]
+
+       Fixed serious joe code in distribution checks in NEWNEWS.
+
+       NEWNEWS statistics.
+
+       Newsgroup security.
+
+       Performance enhancements (about 2x better for article xfers).
+
+       xhdr command added to improve performance on subject searches.
+
+       Compiled-in server name no longer supported.
+
+       common/clientlib.c/getserverbyfile() now looks at the
+       environment variable NNTPSERVER before checking the file.
+
+       inews/inews.c now limits .signature files to MAX_SIGNATURE lines.
+
+       server/misc.c/spawn() now returns the error output of rnews/inews
+       alone with the posting failed code.  This is in turn printed by
+       inews/inews.c so the user has some idea of why his article wasn't
+       accepted.
+
+       rn patches now for patchlevel #40
+               Bug fix: rrn no longer leaves droppings in /tmp
+               "Skipping unavailable article" problems fixed
+               Support for 4.3 TIOCGWINSZ ioctl [sam@okeeffe.berkeley.edu]
+               Configure asks for domains
+               Pnews/Rnmail understand hostnames with .'s in them.
+               Makefile fixes [harvard!lownlab!kiely]
+
+       PYRAMID #defines removed, as it is all being done by default now.
+
+       inews/inews.c now exits 0, since before it had a random exit
+       status, causing pyramids to choke.  [forys@boulder.colorado.edu]
+
+       server/server.c now logs user/system/elapsed time as floats
+       instead of ints.  [rick@seismo.css.gov]
+
+       server/ihave.c no longer logs every message id transfered but
+       instead keeps statistics which are logged at the end.
+       [rick@seismo.css.gov]
+
+       server/serve.c now times out after TIMEOUT seconds of idleness.
+
+       server/access.c converts remote hostname to lower case
+       when logging, in case you have a nameserver which is helping you.
+
+       server/misc.c/getartbyid now reports message-id's when
+       it encounters a malformed line in the history file.
+       [gds@spam.istc.sri.com]
+
+       inews/inews.c had an uninitialized variable, which
+       could cause trouble.  [jwp%chem@sdcsvax.ucsd.edu]
+
+       common/clientlib.c now understands 4.3 nameserver
+       multiple addresses, and tries them all before
+       giving up.
+
+       common/clientlib.c has has 2 new functions:
+       "getserverbyfile" opens a given file and returns
+       the name of the server given in the file to use
+       for news.  "handle_server_response" prints informative
+       messages based on the initial connection response code.
+
+       server/access.c now is case insensitive when checking
+       for host read/xfer permissions.
+
+       server/misc.c/spawn didn't check for a closed connection
+       while receiving input from client.  As a result, truncated
+       news articles could be received.
+
+       server/newnews.c had a printf which was missing an
+       argument.  [louie@trantor.umd.edu]
+
+       Added fake syslog facility to server.  Code is in
+       server/fakesyslog.c.  [mckenny@sri-unix.arpa]
+
+       Fixed length argument to accept() in server/main.c
+       [mckenny@sri-unix.arpa]
+
+       Now uses pipe to rnews so as to get rnews output for debugging.
+       Also chowns temporary file to POSTER's uid and gid.
+       [mckenny@sri-unix.arpa]
+
+       Fixed bugs in server/netaux.c to close syslog fd.
+       [mckenny@sri-unix.arpa]
+
+       Made bcopy() standard in server/misc.c  [kre@munnari.OZ]
+
+       Documentation changes to make certain things about client
+       installation clearer.  [munnari!charlie.oz!craig]
+
+1.3    30 June 1986
+
+       rrn is no longer included as complete source, but
+       rather as a set of context diffs and a program to
+       apply them to your rn source.  Many thanks go to
+       Gene Spafford for an outstanding job doing this.
+       [spaf@gatech.csnet]
+
+       The dreaded kill/save bug is fixed; rn was passing
+       /bin/sh too many open file descriptors.  Thanks and a tip of the
+       proverbial hat to Chris Maio!  Change to rrn/util.c.
+       [chris@columbia.edu]    
+
+       Fixed a bug in rrn/artio.c which caused an assertion
+       failure on line 114 of artio.c; artopen was returning
+       Nullfp without closing the fp associated with the
+       bogus article.  [genrad!ens@eddie.mit.edu, beorn@ic.berkeley.edu]
+
+       Added #define PYRAMID in common/conf.h, added some
+       #ifdef PYRAMID code in server/misc.c to deal with
+       Pyramids not initializing static data to 0, as well
+       as an fseek problem.  [spaf@gatech.CSNET]
+
+       Another wait bug fixed in spawn() in server/misc.c.
+
+       Added a required \r in post.c.
+
+       Added signal(SIGCHLD, SIG_IGN) to server/serve.c,
+       to fix exit status problem with ALONE defined.
+
+       Statistics logging now returns sum of the nntpd and
+       its children for process time.  server/main.c
+       [fair@ucbarpa.berkeley.edu]
+
+       Subnet support for access file finally added.
+       server/subnet.c added, common/conf.h now has
+       #defines for SUBNET, DAMAGED_NETMASK.
+
+       inews/inews.c now generates a from line with the UUCP
+       name instead of always using gethostname().  common/conf.h
+       changed to add #defines for UUNAME, GHNAME.
+       [jwang@atrp.mit.edu]
+
+       Added LIBS to Makefile. [soma!sob@rice.edu]
+
+1.2c   17 May 1986
+
+       Support for Masscomp added (#define MASSCOMP in ../common/conf.h).
+       [soma!sob@rice.edu]
+
+       Syslog output now requires SYSLOG to be defined in ../common/conf.h.
+       This is handy on systems which, for some reason or another,
+       don't have syslog.  [soma!sob@rice.edu]
+
+       server/post.c had arguments reversed in a printf.  [salex@rice.edu]
+
+       rrn/common.h had PIPESAVER misdefined. [cspencer@bbncc5.arpa]
+
+       server/group.c was missing a \r in a printf.  [lear@rutgers.edu]
+
+       xmit/nntpxmit.c is a new version.  Highlights include
+       improved error reactions and logging info.  [fair@ucbarpa.berkeley.edu]
+
+       xmit/nntpsend is a shell script for sending news via nntp
+       in a sane manner, with locking.  [pleasant@topaz.rutgers.edu,
+       fair@ucbarpa.berkeley.edu]  The locking mechanism is provided
+       courtesy of Mr. Fair's "shlock.c", in xmit/shlock.c.
+
+       support/nntp_awk produces weekly reports from the nntp server
+       logging output.  [fair@ucbarpa.berkeley.edu]
+
+       Makefile (in this directory) would do a "make install" as
+       the default action; it now prints a helpful message.
+       [arnold@cgl.ucsf.edu]
+
+       server/Makefile and support/Makefile had needless dependencies
+       in them; if you didn't do a make depend, you'd have problems
+       on a 4.2 system.  The server and support stuff now depend only
+       on their own .h files.  [arnold@cgl.ucsf.edu]
+
+1.2b   13 April 1986
+
+       common/clientlib.c stupidly had some debugging printfs
+       enabled.
+
+       rrn/{artio.c,head.c} had sprintf("... %d", foo) where "foo"
+       was a long.  %d -> %ld.  [cspencer@bbncc5.arpa]
+
+       server/time.c had an order of evaluation problem in the
+       macro "twodigtoi".  [fletcher@tzec.cs.utexas.edu, among others.]
+
+       server/common.h included <dbm.h> if DBM was defined,
+       caused multiply-defined NULL's.  [cda@opal.berkeley.edu,
+       pleasant@topaz.rutgers.edu, among others.]
+
+       server/active.c would lose because variable "i" would be
+       at the end of the group array if it was called on a timer
+       interrupt.  "i" now set to zero properly.  This only occurs
+       if FASTFORK is defined.  [cda@opal.berkeley.edu]
+
+1.2a   20 March 1986
+
+       common/conf.h defined MAX_GROUPS as 300; this was too low on
+       some machines.  Upped to 450.  [solex@rice.edu, beorn@ic.berkeley.edu]
+
+       rrn/Makefile.sh had .c instead of .o for OBJS and SRCS
+       respectively.  Also had cc -o ../common/clientlib.o (see below).
+
+       inews/inews.c had (char *) 0 for gets(), which made SUN's upset.
+       Changed to simply NULL. [brian@sdcsvax.ucsd.edu]
+
+       inews/Makefile had cc -o ../common/clientlib.o which some
+       machines don't do.  [brian@sdcsvax.ucsd.edu]
+
+       common/clientlib.c has "untp" instead of "nntp".
+
+       server/active.c made more robust about reading active file
+       if active file is longer than MAX_GROUPS.
+
+       server/common.h included common/conf.h after checking for
+       DBM, which caused some problems.  [soma!sob@rice.edu]
+
+1.2    15 March 1986
+
+       Released.
diff --git a/usr/src/new/nntp/Makefile b/usr/src/new/nntp/Makefile
new file mode 100644 (file)
index 0000000..83e676c
--- /dev/null
@@ -0,0 +1,28 @@
+
+#
+# Makefile for NNTP intstallation
+#
+
+install:
+       @ echo "If you want to install a server version or"
+       @ echo "a client version, type \"make install_server\""
+       @ echo "or \"make install_client\" respectively."
+       @ echo "See README for more details."
+
+install_server:
+       cd server; make install
+       cd support; make install
+       cd doc; make install
+
+install_client:
+       cd rrn; make install
+       cd inews; make install
+       cd doc; make install
+
+distrib:
+       rm -rf mods
+       cd server; make distrib
+       cd support; make distrib
+       cd inews; make distrib
+       cd common; make distrib
+       cd doc; make distrib
diff --git a/usr/src/new/nntp/README b/usr/src/new/nntp/README
new file mode 100644 (file)
index 0000000..1f1d1f8
--- /dev/null
@@ -0,0 +1,161 @@
+NNTP README            October 15, 1987        For version 1.4 NNTP package
+
+[See the file CHANGES to see differences between this version and
+older versions.]
+
+     This package contains everything (well, most of it, I hope) that
+you'll need to implement a remote news server running the NNTP protocol.
+
+     A brief tour of the directories and their programs:
+
+       server          Source for the NNTP news server daemon.
+
+       rrn             Patches to "rn" to allow remote news reading.
+                       These patches courtesy of Gene Spafford.
+
+       inews           A "mini-inews" written by Steven Grady
+                       <grady@ingres.berkeley.edu> which allows
+                       remote posting without changing much else.
+
+       xmit            An active transmission client for transferring
+                       news, written by Erik Fair; see note below.
+
+       common          Common stuff (response codes, configuration info,
+                       and some client library routines) for the
+                       the news server and the clients.  The "conf.h"
+                       file here needs to be edited to reflect
+                       the peculiarities of your system.
+
+       support         Some support files that make the nntpd's
+                       life considerably easier.
+
+       doc             Documentation on the server, including manual
+                       pages.  Manual pages for rrn are in rrn/.
+
+       xfer            A passive reception client which uses the
+                       NEWNEWS command to retrieve news from a remote
+                       server.  Written by Brian Kantor, this software
+                       is UNSUPPORTED.
+
+     Each directory has associated with it a README file (except
+for rrn -- rrn has a bunch of 'em.  The one you want is README_RRN).
+As you go through the system customizing things, you should read
+the README for each directory to get an idea of what traps await
+you in that area.  You may also want to print a copy of doc/rfc977,
+which describes the NNTP protocol.
+
+     TWO IMPORTANT NOTES:
+
+       1. The NNTP server assumes that the history file format
+          is 2.11 or 2.10.3; therefore you need 2.11 news.
+
+       2. If you want to transfer news with NNTP, you'll be using
+          the "nntpxmit" program in the "xmit" directory.
+          This requires routines in 2.11 news source, and must
+          be compiled there.  See xmit/README for more info.
+
+     >>>>> Get 2.11 news if you don't have it.
+
+     So, time for a general and cohesive Plan:
+
+     1. Look at common/README.  This will explain the stuff which
+       needs to be tailored for your system in common/conf.h.
+       Make the necessary changes to reflect your system.
+
+     2. Look at server/README; there shouldn't be much to do here,
+       as the configuration stuff should have been taken care of
+       when you edited common/conf.h.
+
+       Do a "make", and see if things work ok -- they should
+       (comforting, right?)
+
+     3. Check out support/README and learn about the support programs.
+       Again, there should be no configuration changes, as that's
+       what common/conf.h is for.  You will need to edit the Makefile
+       here to reflect where you want your binaries to be installed,
+       however.
+
+       Do a "make" here, too.
+
+     4. Look at inews/README.  This is for the pseudo-inews which
+       gets installed on client news machines.  If you don't want
+       your clients to be able to post, don't worry about this.
+       But I suggest you do.  Again, there shouldn't be any
+       configuration futzing to be dealt with.
+
+       Alas, do a make here, too.
+
+     5. The big one: rrn.  Read rrn/README_RRN, which explains
+       Gene's patches and what to do, and do what it suggests.
+
+     6. It would be nice if you could do a "make install" at this point,
+       but you can't: if you compile this on the NNTP server machine,
+       you don't want rrn objects installed.  On the other hand,
+       if you compile this on a client machine, you don't want
+       server objects installed.  So, from this (nntp) directory:
+
+       Server?  Type "make install_server".  This installs "server"
+                and "support".
+
+       Client?  Type "make install_client".  This installs "rrn"
+                and "inews"
+
+       >>> A full client installation of NNTP requires the following
+           files (suitable for rdist, assuming standard directories):
+
+NEWS = ( /usr/local/{Pnews,Rnmail,inews,rn,rrn,newsetup,newsgroups,lib/rn}
+       /usr/man/catl/{Pnews,Rnmail,rn,newsetup,newsgroups}.1 )
+
+           You DO NOT need any of the normal news junk (e.g.,
+           /usr/lib/news, postnews, checknews, readnews) on CLIENT
+           systems.
+
+           You DO need these on SERVER systems.
+
+     Problems?  You can get to me via electronic mail at the
+following addresses:
+
+       Internet:               phil@ucbvax.berkeley.edu
+       UUCP:                   ...!ucbvax!phil
+       Telephone (home):       (415) 848-8409
+       Telephone (work):       (415) 642-6792 or (415) 642-7447
+
+     I'm very interested in learning what hacks need to be made to
+nntpd to get it to work on various systems, and certainly, if there
+are outright bugs, please let me know.
+
+     Also, please send me electronic mail if you decide to use this
+package, as that way I can mail out bug reports and fixes.  Be sure
+to include a reply-able address if your mailer doesn't generate
+one for you.
+
+     One note, though -- as for "rrn", I'll support bugs caused
+by my additions/mods to it (hopefully Larry Wall will be supporting
+this soon...) but please don't send me reports about things which
+were already in rn 4.3.  Thanks.
+
+     [Those who had the pleasure of dealing with the kill/save bug,
+it's gone now, thanks to Chris Maio.]
+
+     Finally, I'd like to thank the various people who both inspired
+and helped to make this project a reality:  Erik Fair, whose criticism
+and suggestions helped mold NNTP (and who wrote the active transmission
+client); Brian Kantor, who really got me motivated enough to go and
+finish the thing, and whose work on the RFC was *tremendous*; Steven
+Grady, who wrote the inews interface (and wasted countless hours only
+to have his work dashed periodically...); Mike Meyer, who beta tested
+the software and pointed out numerous problems; Bob Henry, who let me
+have the resources so that it got done; Peter Yee, who repeated enough
+good ideas to get me to include them; all the folks who had patience
+with me and didn't go off and write this themselves (jsq, you
+listening?  My thanks.); Chuq von Rospach and the members of lan-news;
+Gene Spafford for eliminating having to include 1 MB of source to rn
+by a set of patches; Matt Thomas for adding support for DECNET;
+the kind folks who beta tested version 1.4 and put up with stupid
+bugs and provided helpful feedback, notably Craig Leres, Matt Thomas,
+Wengyik Yeong, and Stan Barber; all the individuals who have reported
+bugs or suggested improvements (see CHANGES for a list); and probably a
+lot of other people I've neglected to mention.  My thanks to all.
+
+                                               Phil Lapsley
+                                               15 October 1987
diff --git a/usr/src/new/nntp/rrn/MAKE_RRN_CHANGES b/usr/src/new/nntp/rrn/MAKE_RRN_CHANGES
new file mode 100755 (executable)
index 0000000..73ee412
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin:/usr/local
+export PATH
+
+for filename in Pnews.header addng.c artio.c common.h final.c head.c \
+       init.c ng.c ngdata.c ngdata.h rcstuff.c respond.c util.c
+do
+patch $filename < ${filename}.rrnpatch
+done
+
+
+for filename in Makefile.SH newsetup.SH newsgroups.SH 
+do
+    patch -o ${filename}.rrn $filename < ${filename}.rrnpatch
+done
+
+if test -r Configure.orig ; then
+echo "Configure.orig exists.  Save the real Configure script and then:"
+echo '"mv Configure.rrn Configure"'
+echo "Then run Configure and answer the questions."
+else
+mv Configure Configure.orig
+mv Configure.rrn Configure
+fi
diff --git a/usr/src/new/nntp/rrn/README b/usr/src/new/nntp/rrn/README
new file mode 100644 (file)
index 0000000..5f97583
--- /dev/null
@@ -0,0 +1,9 @@
+
+Well ...
+
+The patches for rrn aren't here yet.  Go grab the rrn.39.tar.Z in
+"pub" off of ucbvax via anonymous ftp.  That's the complete deal.
+
+The patches should be here in a few days.
+
+                                               Phil
diff --git a/usr/src/new/nntp/rrn/README_RRN b/usr/src/new/nntp/rrn/README_RRN
new file mode 100644 (file)
index 0000000..1a0e236
--- /dev/null
@@ -0,0 +1,52 @@
+Updated hacks to change rn into rrn    28 June 1986
+
+Everything is now automated to install the changes and have
+the source co-resident with the non-network version.  These
+changes also work with rn patches up to #27, and with the
+Georgia Tech patch to make "rn" compatible with 2.10.3 news.
+
+To install, simply run the shell file MAKE_RRN_CHANGES in
+the "rn" source directory  (you may need to alter the file to
+include the location of the "patch" command).  After the file is
+run, "ifdef" changes for the "rrn" version will have been included
+in strategic locations, and a few extra shell files should be
+in place.  Running "Configure" and answering the questions should
+provide you with either the local version (rn) or the network version
+(rrn).
+
+Good luck.
+
+Gene Spafford, (gatech!spaf)
+[Thanks, Gene!  --Phil]
+
+----------
+
+[You should ignore this, Gene's stuff is far better --Phil]
+
+Hacks to change rn into rrn            31 August 1985
+
+     The following steps should be followed to set up "rrn" on
+your system.  The files in this directory are somewhat modified
+versions of those distributed with normal rn 4.3.  However, some
+things were overly-difficult to change, and you will have to
+hack them in by hand later.  Fortunately, that's mostly
+trivial.
+
+     1. Run "Configure", as usual.  However, make one change --
+       make your news spool directory be "/tmp", instead of
+       whatever it would normally be.
+
+     2. Edit the file "config.h".  Append two lines to the end of
+       the file:
+
+       #define SERVER
+       #define SERVER_HOST     "xxx"
+
+       The first line tells the compiler that you want the
+       remote news reading hacks compiled in.  The second
+       tells it that the host to query for news and to send
+       posted articles to is "xxx".  Obviously, "xxx" should be
+       changed to whatever machine name is apropriate.
+       
+     3. Type "make"...
+
diff --git a/usr/src/new/nntp/server/README b/usr/src/new/nntp/server/README
new file mode 100644 (file)
index 0000000..cfefd31
--- /dev/null
@@ -0,0 +1,86 @@
+     Caveat: Before compiling anything here, go look at README and conf.h
+in the "common" directory.  Fix conf.h up, and then come back here.
+
+     Back already?  Ok.  Now following the bouncing numbers:
+
+       1. Create the access file with the proper entries.
+          This file goes wherever you said ACCESS_FILE
+          was supposed to be in common/conf.h.  It's format is
+          explained in the manual entry for nntpd.8c.
+          A sample access file is in ../support/access_file.
+          If you don't care who (ab)uses your news server,
+          you can have the line "default read post" in your access
+          file, which will allow anyone on the network to
+          read and post news via your server.  See the manual
+          page for a better explanation.
+
+Parts two and three are necessary if you're running with TCP:
+
+       2. Make an entry for "nntp" in /etc/services.  Should
+          be port number 119, tcp.  I.e., should look something like:
+
+nntp           119/tcp         readnews        # Network News Transfer Protocol
+
+          Sun users running yp should yppush this file to make sure all
+          the clients get it.
+
+       3. Check ../common/conf.h to make sure you're set to do what
+          you want to do with inetd (i.e., #define ALONE or #undef ALONE).
+          If you are using inetd,
+
+          a. Add a line to /etc/inetd.conf, or whatever your
+             configuration file is, to reflect the presence
+             of the news server.  On 4.3 BSD machines this should
+             look like:
+
+nntp   stream  tcp     nowait  root    /etc/nntpd      nntpd
+
+             while under Ultrix or 4.2 BSD machines:
+
+nntp   stream  tcp     nowait  /etc/nntpd      nntpd
+
+             On a Sun, the file is /etc/servers; the line looks like:
+
+nntp   tcp     /usr/etc/in.nntpd
+
+             Be sure to yppush your /etc/servers file if you run
+             yellow pages.
+
+             Don't forget to kill -HUP your inetd.
+
+          If you're NOT using inetd,
+
+          a. Edit ../common/conf.h to have the line
+
+                       #define ALONE
+
+             to compile in code for the stand alone server.
+
+          b. You may as well also define "FASTFORK" in
+             ../common/conf.h.  This causes the server not to
+             read in the active file every time it forks, but
+             rather to stat it every READINTVL seconds, and if
+             the file has changed since the last read, to
+             read it in again.  This makes the children run
+             faster, since they don't have to read the active
+             file every time the parent forks off a child, but
+             the parent server will eat more cpu, doing
+             stat()s every 10 minutes or so.  If your server machine
+             is heavily loaded, you might leave this out.
+
+          c. Change /etc/rc.local to start the server at
+             boot time.
+
+Else, if you're using decnet:
+
+       2. && 3. Define the NNTP object with ncp:
+
+ncp define object NNTP number 0 file /etc/nntpd
+ncp define object NNTP default user guest type stream
+
+ncp set object NNTP all                        # just once for the running system
+
+       4. Compile the server by doing "make".
+
+       5. Cd .. and continue with the rest of the stuff; you'll
+          wind up doing a make install later.
diff --git a/usr/src/new/nntp/server/SYSLOG b/usr/src/new/nntp/server/SYSLOG
new file mode 100644 (file)
index 0000000..81758fb
--- /dev/null
@@ -0,0 +1,50 @@
+SYSLOG INFO
+
+If LOG is defined, the following informational messages are
+logged at LOG_INFO.  All messages are preceded by
+the host name executing the command.
+
+host connect                   "host" connected to the server.
+host refused connection                "host" tried to connect, but was denied.
+host unrecognized %s           "host" gave an unknown command, %s.
+host group newsgroup           "host" isssued GROUP to "newsgroup".
+host post rejected             "host" tried to POST, but was denied.
+host post succeeded            "host" tried to POST, inews worked.
+host post failed               "host" tried to POST, inews failed.
+host timeout                   "host" didn't issue a command for TIMEOUT
+                               seconds (#defined in common/conf.h), so
+                               nntpd closed the connection.
+host transfer_timeout          "host" hasn't sent any lines of data
+                               during article transmission for XFER_TIMEOUT
+                               seconds, so nntpd closed the connection.
+host ihave_stats accepted %d rejected %d failed %d
+                               "host" quit, having offered news articles
+                               to us for transfer.  We accepted %d,
+                               rejected %d (having already seem them),
+                               and %d failed (inews exited non-zero).
+host newnews %s %s %s GMT|local %s
+                               "host" issued NEWNEWS in group %s,
+                               from date %s time %s.  Timezone was
+                               either GMT or local.  <%s> distributions.
+host newnews_stats told %d took %d
+                               "host" asked for new news (see above).
+                               We told it about %d articles, and it
+                               subsequently issued %d ARTICLE commands,
+                               presumably to retrieve the asked about msgs.
+host exit %d aritcles %d groups        "host" quit, having read a total of %d
+                               articles and %d groups.
+host times user %f system %f elapsed %f
+                               "host" quit, having used %f user seconds,
+                               %f system seconds, and %f real-time elapsed
+                               seconds.
+
+The following messages are logged at priority LOG_DEBUG
+if IHAVE_DEBUG is #defined in common/conf.h:
+
+host ihave artid rejected      "host" offered "artid", we already had it.
+host ihave artid accepted failed       "host" offered "artid", we didn't
+host ihave artid accepted succeeded    have it, and the rnews worked or not.
+
+The following error message is logged at LOG_ERR:
+
+host spawn: EOF before period on a line by itself
diff --git a/usr/src/new/nntp/server/profile.c b/usr/src/new/nntp/server/profile.c
new file mode 100644 (file)
index 0000000..bbe7b44
--- /dev/null
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define        MON     "gmon.out"
+#define        DIR     "/usr/tmp/nntpd.prof"
+
+profile()
+{
+       static char     tmp[] = "gmon.XXXXXX";
+       struct stat     statbuf;
+
+       if (chdir(DIR) < 0)
+               return;
+
+       if (stat(MON, statbuf) < 0)
+               return;
+
+       (void) mktemp(tmp);
+
+       (void) rename(MON, tmp);
+}
diff --git a/usr/src/new/nntp/server/time.h b/usr/src/new/nntp/server/time.h
new file mode 100644 (file)
index 0000000..c3def0b
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * Time manipulation routines.
+ *
+ * %W% (Berkeley) %G%
+ */
+
+extern long    dtol();
+extern char    *ltod();
+extern long    local_to_gmt();
+extern long    gmt_to_local();
diff --git a/usr/src/new/nntp/support/Makefile b/usr/src/new/nntp/support/Makefile
new file mode 100644 (file)
index 0000000..8035868
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# Makefile for NNTP server support programs
+#
+
+OBJS   =       mkgrdates.o
+
+SRCS   =       mkgrdates.c
+
+HFILES =       ../common/conf.h
+
+CFLAGS= -O
+
+# Where these support programs will live
+
+DESTDIR        = /usr/spool/news/lib
+
+mkgrdates: mkgrdates.o
+       cc ${CFLAGS} -o mkgrdates mkgrdates.o
+
+install: mkgrdates
+       cp mkgrdates ${DESTDIR}/mkgrdates
+       chmod 755 ${DESTDIR}/mkgrdates
+
+lint:
+       lint mkgrdates.c
+
+clean:
+       rm -f *.o mkgrdates
+
+distrib: clean
+       rm -rf SCCS old
+
+tags:  ${SRCS} ${HFILES}
+       ctags ${SRCS} ${HFILES}
+
+${OBJS}:       ${HFILES}
diff --git a/usr/src/new/nntp/support/README b/usr/src/new/nntp/support/README
new file mode 100644 (file)
index 0000000..ff37ea1
--- /dev/null
@@ -0,0 +1,42 @@
+>>>>>>>>>>>>>>>>>>>>>>>>> No software in the NNTP 1.4 package
+>> NOTE NOTE NOTE NOTE >> uses the NEWGROUPS command.  As a result,
+>> NOTE NOTE NOTE NOTE >> mkgrdates is no longer supported, but is
+>>>>>>>>>>>>>>>>>>>>>>>>> included for completeness.
+
+     Mkgrdates is run by cron periodically (say, every 6 or 12 hours --
+it's up to you).  All it does is produce a cronologically ordered list
+of newsgroups in the active file, along with their dates of creation.
+It tries to be intelligent, and if the active file hasn't changed since
+it was last run, it simply exits.
+
+     So, you'll need to put "mkgrdates" in /usr/lib/crontab to
+be run periodically.  Every day is probably good enough.
+
+     Some things you might want to be aware of are that it creates
+and updates the files STAT_FILE and NGDATE_FILE, defined in
+../common/conf.h.  It's up to you to define these constants to suit
+your system.
+
+     The file "access_file" is the file which tells the news server
+which hosts can read, which can post, and which can transfer.
+This file wants to be installed wherever ACCESS_FILE in ../common/conf.h
+says it should be (you can configure this to suit you).  Remember
+that this should be readable by whatever uid the news server runs
+as.  Further, remember that the entry "default" must be first in
+the table.
+
+     >>> The access file will support subnets iff you have <<<
+     >>> defined SUBNET when you made the server.         <<<
+
+     Finally, edit Makefile to reflect DESTDIR -- where you want
+the binary to be installed for mkgrdates.
+
+     If you're having the nntp server log copious info, you will
+probably want to run the stat package developed by Erik Fair.
+Once a week you should have crontab do
+
+       awk -f nntp_awk nntplog.old >& nntp_report
+
+where "nntplog.old" is the last week's nntp log file produced
+by syslog.  Any errors which it cannot resolve are placed
+in the front of the report.
diff --git a/usr/src/new/nntp/support/access_file b/usr/src/new/nntp/support/access_file
new file mode 100644 (file)
index 0000000..99844d9
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# Sample NNTP access file.  "read" implies "xfer".
+# Note that "default" must be the first entry in the
+# table.
+#
+# If you defined SUBNET when you compiled the server,
+# this file can have subnets as well as class A, B, C
+# networks and hosts.
+#
+# host/net     read/xfer/no    post/no
+#
+# by default, let anyone transfer news, but not read or post
+default                xfer            no
+# hosts on the Berkeley campus can read and post news
+ucb-ether      read            post
+# bugs, a notorious undergraduate machine, is not allowed
+# to read or post news at all.
+bugs           read            no
+# ic can read and post news, but users on ic cannot read
+# articles in the group ucb.postgres or any of its decendents
+# (e.g., ucb.postgres.core)
+ic             read            post    !ucb.postgres
diff --git a/usr/src/new/nntp/support/nntp_awk b/usr/src/new/nntp/support/nntp_awk
new file mode 100644 (file)
index 0000000..6790941
--- /dev/null
@@ -0,0 +1,403 @@
+# an awk script 
+# an NNTP log summary report generator
+#
+# NOTE: for systems that are not as yet using the new 4.3 BSD syslog
+# (and therefore have nntp messages lumped with everything else), it
+# would be best to invoke this script thusly:
+#
+#      egrep nntp syslog.old | awk -f nntp_awk > report_of_the_week
+#
+# because this script will include in the report all messages in the log
+# that it does not recognize (on the assumption that they are errors to
+# be dealt with by a human).
+#
+# Erik E. Fair <fair@ucbarpa.berkeley.edu>
+# May 17, 1986 - Norwegian Independence Day
+#
+# Recognize some new things - February 22, 1987
+# Erik E. Fair <fair@ucbarpa.berkeley.edu>
+#
+# fix "xmt is not an array" bug - March 11, 1987
+# Change Elapsed/CPU fields to break out time values, HH:MM:SS
+# Erik E. Fair <fair@ucbarpa.berkeley.edu>
+#
+# Add reporting for newnews commands - August 27, 1987
+# Erik E. Fair <fair@ucbarpa.berkeley.edu>
+#
+BEGIN{
+       readers = 0;
+       transmit = 0;
+       receive = 0;
+       polled = 0;
+}
+### Skip stderr reports from rnews
+{
+       n = split($6, path, "/");
+       if (path[n] == "rnews:") next;
+       n = split($7, path, "/");
+       if (path[n] == "rnews") next;
+       host = $6;
+}
+$7 == "group" {
+       readers = 1;
+       ng[$8]++;
+       next;
+}
+$7 == "ihave" {
+       receive = 1;
+       rec[host]++;
+       if ($9 == "accepted") {
+               rec_accept[host]++;
+               if ($10 == "failed") rec_failed[host]++;
+       } else if ($9 == "rejected") rec_refuse[host]++;
+       next;
+}
+# this is from version 1.4 of nntpd
+$7 == "ihave_stats" {
+       receive = 1;
+       rec[host] += $9 + $11 + $13;
+       rec_accept[host] += $9;
+       rec_refuse[host] += $11;
+       rec_failed[host] += $13;
+       next;
+}
+$7 == "connect" {
+       systems[host]++;
+       next;
+}
+$7 == "exit" {
+       if ($8 > 0) readers = 1;
+       articles[host] += $8;
+       groups[host] += $10;
+       next;
+}
+$7 == "xmit" {
+       xmt_cpu[host] += $9 + $11;
+       xmt_ela[host] += $13;
+       next;
+}
+$7 == "times" {
+       cpu[host] += $9 + $11;
+       ela[host] += $13;
+       next;
+}
+$7 == "stats" {
+       transmit = 1;
+       xmt[host] += $8;
+       xmt_accept[host] += $10;
+       xmt_refuse[host] += $12;
+       xmt_failed[host] += $14;
+       next;
+}
+#
+#      For the Nth time, I wish awk had two dimensional associative
+#      arrays. I assume that the last request is the same as all the
+#      others in this section of logfile.
+#
+$7 == "newnews" {
+       polled = 1;
+       poll[host] ++;
+       poll_asked[host] = $8;
+       next;
+}
+$7 == "newnews_stats" {
+       poll_offered[host] += $9;
+       poll_took[host] += $11;
+       next;
+}
+$7 == "post" {
+       readers = 1;
+       post[host]++;
+       next;
+}
+$7 == "timeout" {
+       timeout[host]++;
+       timeouts = 1;
+       next;
+}
+$7 == "unrecognized" {
+       unknown[host] = 1;
+       curious = 1;
+}
+### Print anything that we don't recognize in the report
+{
+       print;
+}
+END{
+       printf("\n");
+###############################################################################
+### Article Exchange With Peers (other servers) Statistics                  ###
+###############################################################################
+       if (transmit || receive || polled)
+               printf("NNTP peer article transfers\n\n");
+
+       if (polled) for(s in poll) servers[s]++;
+       if (receive) for(s in rec) servers[s]++;
+       if (transmit) for(s in xmt) servers[s]++;
+
+       if (receive) {
+               printf("Article Reception (they contact us)\n");
+               printf("System                  Offered  Took  Toss  Fail Toss   Elapsed       CPU  Pct\n");
+               for(s in rec) {
+
+                       nrec += rec[s];
+                       nrec_accept += rec_accept[s];
+                       nrec_refuse += rec_refuse[s];
+                       nrec_failed += rec_failed[s];
+                       nrec_cpu += cpu[s];
+                       nrec_ela += ela[s];
+
+                       they_offered = rec[s];
+                       if (they_offered == 0) they_offered = 1;
+                       we_toss = (rec_refuse[s] / they_offered) * 100 + 0.5;
+
+                       e_hours      = ela[s] / 3600;
+                       e_sec        = ela[s] % 3600;
+                       e_min        = e_sec / 60;
+                       e_sec        %= 60;
+
+                       c_hours      = cpu[s] / 3600;
+                       c_sec        = cpu[s] % 3600;
+                       c_min        = c_sec / 60;
+                       c_sec        %= 60;
+
+                       tmp = ela[s];
+                       if (tmp == 0) tmp = 1;
+                       pct = ((cpu[s] / tmp) * 100.0 + 0.5);
+
+                       printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, rec[s], rec_accept[s], rec_refuse[s], rec_failed[s], we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+               }
+
+               e_hours      = nrec_ela / 3600;
+               e_sec        = nrec_ela % 3600;
+               e_min        = e_sec / 60;
+               e_sec        %= 60;
+
+               c_hours      = nrec_cpu / 3600;
+               c_sec        = nrec_cpu % 3600;
+               c_min        = c_sec / 60;
+               c_sec        %= 60;
+
+               they_offered = nrec;
+               if (they_offered == 0) they_offered = 1;
+               we_toss = (nrec_refuse / they_offered) * 100 + 0.5;
+
+               if (nrec_ela == 0) nrec_ela = 1;
+               pct = ((nrec_cpu / nrec_ela) * 100.0 + 0.5);
+
+               printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nrec, nrec_accept, nrec_refuse, nrec_failed, we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+       }
+
+###############################################################################
+       if (polled) {
+               printf("Article Transmission (they poll us)\n");
+               printf("System                     Conn Offrd  Took   Elapsed       CPU  Pct  Groups\n");
+               npoll = 0;
+               npoll_offered = 0;
+               npoll_took = 0;
+               npoll_cpu = 0;
+               npoll_ela = 0;
+
+               for(s in poll) {
+                       npoll += poll[s];
+                       npoll_offered += poll_offered[s];
+                       npoll_took += poll_took[s];
+
+                       if (rec[s]) {
+                               printf("%-25s %5d %5d %5d  (see Article Reception)  %s\n", s, poll[s], poll_offered[s], poll_took[s], poll_asked[s]);
+                       } else {
+                               npoll_ela += ela[s];
+                               npoll_cpu += cpu[s];
+
+                               e_hours      = ela[s] / 3600;
+                               e_sec        = ela[s] % 3600;
+                               e_min        = e_sec / 60;
+                               e_sec        %= 60;
+
+                               c_hours      = cpu[s] / 3600;
+                               c_sec        = cpu[s] % 3600;
+                               c_min        = c_sec / 60;
+                               c_sec        %= 60;
+
+                               tmp = ela[s];
+                               if (tmp == 0) tmp = 1;
+                               pct = ((cpu[s] / tmp) * 100.0 + 0.5);
+
+                               printf("%-25s %5d %5d %5d %3d:%02d:%02d %3d:%02d:%02d %3d%%  %s\n", s, poll[s], poll_offered[s], poll_took[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct, poll_asked[s]);
+                       }
+               }
+               printf("\n%-25s %5d %5d %5d", "TOTALS", npoll, npoll_offered, npoll_took);
+               if (npoll_ela > 0 && npoll_cpu > 0) {
+
+                       e_hours      = npoll_ela / 3600;
+                       e_sec        = npoll_ela % 3600;
+                       e_min        = e_sec / 60;
+                       e_sec        %= 60;
+
+                       c_hours      = npoll_cpu / 3600;
+                       c_sec        = npoll_cpu % 3600;
+                       c_min        = c_sec / 60;
+                       c_sec        %= 60;
+
+                       tmp = npoll_ela;
+                       if (tmp == 0) tmp = 1;
+                       pct = ((npoll_cpu / tmp) * 100.0 + 0.5);
+
+                       printf(" %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+               } else
+                       printf("\n\n");
+       }
+
+###############################################################################
+       if (transmit) {
+               printf("Article Transmission (we contact them)\n");
+               printf("System                    Offrd  Took  Toss  Fail  Pct   Elapsed       CPU  Pct\n");
+               for(s in xmt) {
+
+                       nxmt += xmt[s];
+                       nxmt_accept += xmt_accept[s];
+                       nxmt_refuse += xmt_refuse[s];
+                       nxmt_failed += xmt_failed[s];
+                       nxmt_ela += xmt_ela[s];
+                       nxmt_cpu += xmt_cpu[s];
+
+                       we_offered = xmt[s];
+                       if (we_offered == 0) we_offered = 1;
+                       they_toss = (xmt_refuse[s] / we_offered) * 100 + 0.5;
+
+                       e_hours      = xmt_ela[s] / 3600;
+                       e_sec        = xmt_ela[s] % 3600;
+                       e_min        = e_sec / 60;
+                       e_sec        %= 60;
+
+                       c_hours      = xmt_cpu[s] / 3600;
+                       c_sec        = xmt_cpu[s] % 3600;
+                       c_min        = c_sec / 60;
+                       c_sec        %= 60;
+
+                       elapsed = xmt_ela[s];
+                       if (elapsed == 0) elapsed = 1;
+                       pct = ((xmt_cpu[s] / elapsed) * 100.0 + 0.5);
+
+                       printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, xmt[s], xmt_accept[s], xmt_refuse[s], xmt_failed[s], they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+               }
+
+               we_offered = nxmt;
+               if (we_offered == 0) we_offered = 1;
+               they_toss = (nxmt_refuse / we_offered) * 100 + 0.5;
+
+               e_hours      = nxmt_ela / 3600;
+               e_sec        = nxmt_ela % 3600;
+               e_min        = e_sec / 60;
+               e_sec        %= 60;
+
+               c_hours      = nxmt_cpu / 3600;
+               c_sec        = nxmt_cpu % 3600;
+               c_min        = c_sec / 60;
+               c_sec        %= 60;
+
+               if (nxmt_ela == 0) nxmt_ela = 1;
+               pct = ((nxmt_cpu / nxmt_ela) * 100.0 + 0.5);
+
+               printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nxmt, nxmt_accept, nxmt_refuse, nxmt_failed, they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+       }
+
+###############################################################################
+### Article Readership Statistics                                           ###
+###############################################################################
+
+       if (readers) {
+               printf("NNTP readership statistics\n");
+               printf("System                     Conn Articles Groups Post   Elapsed       CPU  Pct\n");
+               for(s in systems) {
+###
+### servers are different animals; they don't belong in this part of the report
+###
+                       if (servers[s] > 0 && groups[s] == 0 && articles[s] == 0)
+                               continue;
+###
+### report the curious server pokers elsewhere
+###
+                       if (groups[s] == 0 && articles[s] == 0 && post[s] == 0) {
+                               unknown[s] += systems[s];
+                               curious = 1;
+                               continue;
+                       }
+
+                       nconn += systems[s];
+                       nart += articles[s];
+                       ngrp += groups[s];
+                       npost += post[s];
+                       ncpu += cpu[s];
+                       nela += ela[s];
+
+                       e_hours      = ela[s] / 3600;
+                       e_sec        = ela[s] % 3600;
+                       e_min        = e_sec / 60;
+                       e_sec        %= 60;
+
+                       c_hours      = cpu[s] / 3600;
+                       c_sec        = cpu[s] % 3600;
+                       c_min        = c_sec / 60;
+                       c_sec        %= 60;
+
+                       elapsed = ela[s];
+                       if (elapsed == 0) elapsed = 1;
+                       pct = ((cpu[s] / elapsed) * 100 + 0.5);
+
+                       printf("%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, systems[s], articles[s], groups[s], post[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+               }
+
+               e_hours      = nela / 3600;
+               e_sec        = nela % 3600;
+               e_min        = e_sec / 60;
+               e_sec        %= 60;
+
+               c_hours      = ncpu / 3600;
+               c_sec        = ncpu % 3600;
+               c_min        = c_sec / 60;
+               c_sec        %= 60;
+
+               if (nela == 0) nela = 1;
+               pct = ((ncpu / nela) * 100 + 0.5);
+
+               printf("\n%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nconn, nart, ngrp, npost, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
+       }
+
+###############################################################################
+       if (curious) {
+               printf("Unknown NNTP server explorers\nSystem                     Conn\n");
+               for(s in unknown) {
+                       printf("%-25s %5d\n", s, unknown[s]);
+               }
+               printf("\n");
+       }
+###############################################################################
+       if (timeouts) {
+               printf("Server timeouts\n");
+               for(s in timeout) {
+                       printf("%-25s %5d\n", s, timeout[s]);
+               }
+               printf("\n");
+       }
+###############################################################################
+       if (readers) {
+               for(g in ng) {
+                       x = length(g);
+                       if (x > max) max = x;
+
+                       i = index(g, ".");
+                       if (i > 0) top = substr(g, 1, i - 1);
+                       else top = g;
+                       category[top] += ng[g];
+               }
+               fmt = sprintf("%%-%ds %%5d\n", max);
+
+               printf("Newsgroup Request Counts (by category)\n");
+               for(g in category) printf(fmt, g, category[g]);
+
+               printf("\nNewsgroup Request Counts (by newsgroup)\n");
+               for(g in ng) printf(fmt, g, ng[g]);
+               printf("\n");
+       }
+}
diff --git a/usr/src/new/nntp/xfer/README b/usr/src/new/nntp/xfer/README
new file mode 100644 (file)
index 0000000..da4f074
--- /dev/null
@@ -0,0 +1,9 @@
+This is a "passive" news transmission client.  It queries
+other servers for news with NEWNEWS and gets it via the article
+command.
+
+No docs, sorry.  It's unsupported (like the rest of the stuff is???).
+Read the source code.
+
+Originally written by Brian Kantor <brian@sdcsvax.ucsd.edu>, with some
+bug fixes by Ambar <ambar@athena.mit.edu>.
diff --git a/usr/src/new/nntp/xfer/nntpxfer.c b/usr/src/new/nntp/xfer/nntpxfer.c
new file mode 100644 (file)
index 0000000..e8d28bc
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * nntpxfer
+ *
+ * Connects to the specified nntp server, and transfers all new news
+ * since the last successful invocation.
+ *
+ * last successful invocation date and time are stored in a file at
+ * /usr/spool/news/nntp.<hostname> as 
+ *     groups YYMMDD HHMMSS distributions\n
+ * in case you need to edit it.  You can also override this on 
+ * the command line in the same format, in which case the file won't
+ * be updated.
+ *
+ *     Brian Kantor, UCSD 1986
+ * (some bug fixes by ambar@athena.mit.edu)
+ */
+
+#define DEBUG
+
+/* you'd think that 4096 articles at one go is enough.... */
+#define MAXARTS        4096
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <signal.h>
+#include <dbm.h>
+
+#define INEWS  "/usr/lib/news/inews -p"
+#define HIST   "/usr/lib/news/history"
+
+char   *malloc();
+char   *strcpy();
+char   *strcat();
+long   time();
+u_long inet_addr();
+
+extern int errno;
+char *artlist[MAXARTS];
+int server;                    /* stream socket to the nntp server */
+int newart, dupart, misart;
+
+main(argc, argv)
+int argc;
+char *argv[];
+       {
+       FILE *dtfile;           /* where last xfer date/time stored */
+       char buf[BUFSIZ];
+       char lastdate[16];
+       char distributions[BUFSIZ];
+       char dtname[128];
+       char newsgroups[BUFSIZ];
+       char lasttime[16];
+       int connected = 0;              /* 1 = connected */
+       int i;
+       int omitupdate = 0;             /* 1 = don't update datetime */
+       long clock;
+       long newdate, newtime;
+       struct hostent *hp;
+       struct servent *sp;
+       struct sockaddr_in sin;
+       struct tm *now;
+
+       /* OPTIONS
+               argv[1] MUST be the host name
+               argv[2-4] MAY be "newsgroups YYMMDD HHMMSS"
+                       argv[5] MAY be distributions
+               (otherwise use 2-4/5 from the file
+               "/usr/spool/news/nntp.hostname")
+       */
+
+       if (argc != 2 && argc != 5 && argc != 6)
+               {
+               (void) printf("Usage: %s host [groups YYMMDD HHMMSS [<dist>]]\n",
+                       argv[0]);
+               exit(1);
+               }
+       
+       if (argc > 2)
+               {
+               omitupdate++;
+               (void) strcpy(newsgroups, argv[2]);
+               (void) strcpy(lastdate, argv[3]);
+               (void) strcpy(lasttime, argv[4]);
+               (void) strcpy(distributions, "");
+               if (argc > 5)
+                       (void) strcpy(distributions, argv[5]);
+               }
+       else
+               {
+               (void) strcpy(dtname, "/usr/spool/news/nntp.");
+               (void) strcat(dtname, argv[1]);
+               dtfile = fopen(dtname, "r");
+               if (dtfile == NULL)
+                       {
+                       (void) printf("%s not found; using * 860101 000000 \n", 
+                               dtname);
+                       (void) strcpy(newsgroups, "*");
+                       (void) strcpy(lastdate, "860101");
+                       (void) strcpy(lasttime, "000000");
+                       (void) strcpy(distributions, "");
+                       }
+               else
+                       {
+                       if (fscanf(dtfile, "%s %s %s %s",
+                               newsgroups, lastdate, lasttime, distributions) < 3)
+                               {
+                               (void) printf("%s invalid; using * 860101 000000\n",
+                                       dtname);
+                               (void) strcpy(newsgroups, "*");
+                               (void) strcpy(lastdate, "860101");
+                               (void) strcpy(lasttime, "000000");
+                               (void) strcpy(distributions, "");
+                               }
+                       (void) fclose(dtfile);
+                       }
+               clock = time((long *)0);
+               now = gmtime(&clock);
+               newdate = (now->tm_year * 10000) +
+                       ((now->tm_mon + 1) * 100) + now->tm_mday;
+               newtime = (now->tm_hour * 10000) +
+                       (now->tm_min * 100) + now->tm_sec;
+               }
+
+#ifdef DEBUG
+       (void) printf("newsgroups = '%s'\n", newsgroups);
+       (void) printf("date = '%s'\n", lastdate);
+       (void) printf("time = '%s'\n", lasttime);
+       (void) printf("distributions = '%s'\n", distributions);
+       (void) printf("now is = %06d %06d\n", newdate, newtime);
+#endif
+
+       if (dbminit(HIST) < 0)
+               {
+               perror("couldn't open history file");
+               exit(1);
+               }
+
+       sin.sin_addr.s_addr = inet_addr(argv[1]);
+       if (sin.sin_addr.s_addr != -1) 
+               {
+               sin.sin_family = AF_INET;
+               }
+       else 
+               {
+               hp = gethostbyname(argv[1]);
+               if (hp == NULL) 
+                       {
+                       (void) printf("%s: unknown host\n", argv[1]);
+                       exit(1);
+                       }
+
+               sin.sin_family = hp->h_addrtype;
+#ifdef BSD43
+               bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
+                       hp->h_length);
+#else  BSD43
+               bcopy(hp->h_addr, (caddr_t)&sin.sin_addr,
+                       hp->h_length);
+#endif BSD43
+               }
+       
+       sp = getservbyname("nntp", "tcp");
+       if (sp == NULL)
+               {
+               perror("nntp/tcp");
+               exit(1);
+               }
+
+       sin.sin_port = sp->s_port;
+
+       do      {
+               server = socket(AF_INET, SOCK_STREAM, 0);
+               if (server < 0) 
+                       {
+                       perror("nntpxfer: socket");
+                       exit(1);
+                       }
+
+               if (connect(server, (struct sockaddr *)&sin, sizeof (sin)) < 0) 
+                       {
+#ifdef BSD43
+                       if (hp && hp->h_addr_list[1]) 
+                               {
+                               hp->h_addr_list++;
+                               bcopy(hp->h_addr_list[0],
+                                   (caddr_t)&sin.sin_addr, hp->h_length);
+                               (void) close(server);
+                               continue;
+                               }
+#endif BSD43
+                       perror("nntpxfer: connect");
+                       exit(1);
+                       }
+               connected++;
+               }
+       while (connected == 0);
+
+#ifdef DEBUG
+       (void) printf("connected to nntp server at %s\n", argv[1]);
+#endif
+       /*
+       * ok, at this point we're connected to the nntp daemon 
+       * at the distant host.
+       */
+
+       /* get the greeting herald */
+       (void) sockread(buf);
+#ifdef DEBUG
+       (void) printf("%s\n", buf);
+#endif
+       if (buf[0] != '2')      /* uh-oh, something's wrong! */
+               {
+               (void) printf("protocol error: got '%s'\n", buf);
+               (void) close(server);
+               exit(1);
+               }
+
+
+       /* first, tell them we're a slave process to get priority */
+       sockwrite("SLAVE");
+       (void) sockread(buf);
+#ifdef DEBUG
+       (void) printf("%s\n", buf);
+#endif
+       if (buf[0] != '2')      /* uh-oh, something's wrong! */
+               {
+               (void) printf("protocol error: got '%s'\n", buf);
+               (void) close(server);
+               exit(1);
+               }
+       
+       /* now, ask for a list of new articles */
+       if (strlen(distributions))
+               (void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>", 
+                       newsgroups, lastdate, lasttime, distributions);
+       else
+               (void) sprintf(buf,"NEWNEWS %s %s %s GMT", 
+                       newsgroups, lastdate, lasttime);
+       sockwrite(buf);
+       (void) sockread(buf);
+#ifdef DEBUG
+       (void) printf("%s\n", buf);
+#endif
+       if (buf[0] != '2')      /* uh-oh, something's wrong! */
+               {
+               (void) printf("protocol error: got '%s'\n", buf);
+               (void) close(server);
+               exit(1);
+               }
+       /* and here comes the list, terminated with a "." */
+#ifdef DEBUG
+       (void) printf("data\n");
+#endif
+       while (1)
+               {
+               (void) sockread(buf);
+               if (!strcmp(buf,"."))
+                       break;
+               if (wewant(buf))
+                       {
+                       if (newart > MAXARTS)
+                               {
+                               omitupdate++;
+                               continue;
+                               }
+                       artlist[newart] = malloc((unsigned)(strlen(buf)+1));
+                       (void) strcpy(artlist[newart], buf);
+                       newart++;
+                       }
+               else
+                       dupart++;
+               }
+#ifdef DEBUG
+       (void) printf(".\n%d new, %d dup articles\n", newart, dupart);
+#endif
+
+       /* now that we know which articles we want, retrieve them */
+       for (i=1; i < newart; i++)
+               (void) artfetch(artlist[i]);
+
+#ifdef DEBUG
+       (void) printf("%d missing articles\n", misart);
+#endif
+       /* we're all done, so tell them goodbye */
+       sockwrite("QUIT");
+       (void) sockread(buf);
+#ifdef DEBUG
+       (void) printf("%s\n", buf);
+#endif
+       if (buf[0] != '2')      /* uh-oh, something's wrong! */
+               {
+               (void) printf("error: got '%s'\n", buf);
+               (void) close(server);
+               exit(1);
+               }
+       (void) close(server);
+
+       /* do we want to update the timestamp file? */
+       if (!omitupdate)
+               {
+               (void) sprintf(buf, "%s %06d %06d %s\n",
+                       newsgroups, newdate, newtime, distributions);
+#ifdef DEBUG
+               (void) printf("updating %s:\n\t%s\n", dtname, buf);
+#endif
+               dtfile = fopen(dtname, "w");
+               if (dtfile == NULL)
+                       {
+                       perror(dtname);
+                       exit(1);
+                       }
+               (void) fputs(buf,dtfile);
+               (void) fclose(dtfile);
+               }
+       exit(0);
+}
+
+artfetch(articleid)
+char *articleid;
+       {
+       int lines = 0;
+       char buf[BUFSIZ];
+       FILE *inews;
+
+       /* now, ask for the article */
+       (void) sprintf(buf,"ARTICLE %s", articleid);
+       sockwrite(buf);
+       (void) sockread(buf);
+#ifdef DEBUG
+       (void) printf("%s\n", buf);
+#endif
+       if (buf[0] == '4')      /* missing article, just skipit */
+               {
+               misart++;
+               return(0);
+               }
+
+       if (buf[0] != '2')      /* uh-oh, something's wrong! */
+               {
+               (void) printf("protocol error: got '%s'\n", buf);
+               (void) close(server);
+               exit(1);
+               }
+#ifdef DEBUG
+       (void) printf("command: %s\n", INEWS);
+#endif
+       if ( (inews = popen(INEWS, "w")) == NULL)
+               {
+               perror(INEWS);
+               exit(1);
+               }
+
+       /* and here comes the article, terminated with a "." */
+#ifdef DEBUG
+       (void) printf("data\n");
+#endif
+       while (1)
+               {
+               (void) sockread(buf);
+               if (buf[0] == '.' && buf[1] == '\0')
+                       break;
+               lines++;
+               (void) strcat(buf,"\n");
+               (void) fputs(((buf[0] == '.') ? buf + 1 : buf),
+                          inews);
+               }
+#ifdef DEBUG
+       (void) printf(".\n%d lines\n", lines);
+#endif
+       (void) fflush(inews);
+       (void) pclose(inews);
+       return(0);
+        }
+
+int
+sockread(buf)
+char *buf;
+       {
+       char c;
+       int j = 0;
+#ifdef BSD43
+       fd_set rf;
+#else BSD43
+       int rf;
+#endif BSD43
+       struct timeval tv;
+       int r;
+       char *p = buf;
+
+       while ( 1 )
+               {
+               tv.tv_sec = 1800;       /* 15 minutes */
+               tv.tv_usec = 0L;
+#ifdef BSD43
+               FD_ZERO(&rf);
+               FD_SET(server, &rf);
+#else BSD43
+               rf = 1 << server;
+#endif BSD43
+               r = select(20, (fd_set *)&rf, (fd_set *)0, (fd_set *)&rf, &tv);
+
+               if (r < 0)
+                       {
+                       if (errno == EINTR)
+                               continue;
+                       perror("getsock select");
+                       exit(1);
+                       }
+               if (r == 0)
+                       {
+                       printf("read timed out.\n");
+                       exit(1);
+                       }
+
+               if (read(server, &c, 1) <= 0)
+                       break;
+
+               /* mask off any chance parity bits */
+               *p = c & 0x7f;
+
+               /* look for end of line (== LF) */
+               if (c == 0x0a)
+                       {
+                       if (j > 0 && *(p-1) == 0x0d)
+                               *(p-1) = '\0';
+                       else
+                               *p = '\0';
+                       return(strlen(buf));
+                       }
+               j++; p++;
+               }
+       perror("sockread");
+       (void) close(server);
+       exit(1);
+       /* NOTREACHED */
+       }
+
+sockwrite(buf)
+char *buf;
+       {
+       register int sz;
+       char buf2[BUFSIZ];
+#ifdef DEBUG
+       (void) printf(">>> %s\n", buf);
+#endif
+       (void) strcpy(buf2,buf);
+       (void) strcat(buf2,"\r\n");
+       sz = strlen(buf2);
+       if (write(server,buf2,sz) != sz)
+               {
+               (void) printf("write error on server socket\n");
+               (void) close(server);
+               exit(1);
+               }
+       }
+
+int
+wewant(articleid)
+char *articleid;
+       {
+       datum k, d;
+       char id[BUFSIZ];
+       char *p;
+
+       /* remove any case sensitivity */
+       (void) strcpy(id, articleid);
+       p = id;
+       while (*p)
+               {
+               if (isupper(*p))
+                       *p = tolower(*p);
+               p++;
+               }
+
+       k.dptr = id;
+       k.dsize = strlen(articleid) + 1;
+
+       d = fetch(k);
+
+       if (d.dptr)
+               {
+#ifdef DEBUG
+               (void) printf("dup: '%s'\n", articleid);
+#endif
+               return(0);
+               }
+
+#ifdef DEBUG
+       (void) printf("new: '%s'\n", articleid);
+#endif
+       return(1);
+       }
diff --git a/usr/src/new/nntp/xmit/Makefile b/usr/src/new/nntp/xmit/Makefile
new file mode 100644 (file)
index 0000000..2ba5afd
--- /dev/null
@@ -0,0 +1,12 @@
+
+NNTPSRC = nntpxmit.c rfc822.c remote.c get_tcp_conn.c getdate.y llist.c
+NNTPOBJ = nntpxmit.o rfc822.o remote.o get_tcp_conn.o getdate.o llist.o
+
+get_tcp_conn.o: get_tcp_conn.c get_tcp_conn.h
+remote.o: remote.c response_codes.h nntpxmit.h
+nntpxmit.o: nntpxmit.c nntpxmit.h get_tcp_conn.h header.h
+
+nntpxmit: $(NNTPOBJ)
+       $(CC) $(LDFLAGS) $(NNTPOBJ) -o nntpxmit
diff --git a/usr/src/new/nntp/xmit/README b/usr/src/new/nntp/xmit/README
new file mode 100644 (file)
index 0000000..73e9eb0
--- /dev/null
@@ -0,0 +1,59 @@
+[This is a fairly old (but hardy!) version of nntpxmit.  Erik Fair has
+promised a newer version with more features "soon".  -- Phil, 15 Oct 87]
+
+     The program "nntpxmit" is an active trnamission client
+(see the comment in nntpxmit.c for a description of the
+difference between active and passive clients) written by Erik Fair
+<fair@ucbvax.berkeley.edu, ...!ucbvax!fair>.
+
+     nntpxmit requires support from the 2.11 news source.
+Because of this, it must be compiled in the "src" directory of
+the 2.11 distribution.  You should:
+
+     1. Move the *.c and *.h files to 2.11/src
+
+     2. Move ../common/response_codes.h into 2.11/src
+
+     3. Add the lines in the Makefile in this directory to the
+       2.11/src Makefile.  You can just cat the Makefile
+       here to the Makefile in the 2.11/src directory, if
+       you'd like.
+
+     4. Then do "make nntpxmit".
+
+     Having compiled nntpxmit like that, you need to set your news
+system up to use it.  Nntpxmit takes as arguments an internet host
+to connect to, and a file containing a list of articles to send to
+it.
+
+     So, you might edit your news sys file to have an entry like:
+
+#
+# NASA Ames Research Center
+#
+nike:ucb,uc,mod,to.nike:F:/usr/spool/news/batch/ames-titan.arpa
+
+This will place names of news articles in the ucb, uc, and mod newsgroups
+in the file "/usr/spool/news/batch/ames-titan.arpa".  This is because
+we assume the output batch file will be the machine's internet name.
+
+Periodically, you should have crontab run "nntpsend" to transmit
+the news.  A good choice for "periodically" is every 10 minutes.
+nntpsend depends on the program "shlock", which you'll need to
+compile by hand (horrors):
+
+       cc -O -o shlock shlock.c
+
+Also, nntpsend will need to be customized for your system.
+Fortunately, it's pretty straightforward.
+
+     nntpxmit has an option "-s" which *supresses* statistic
+logging via syslog.  Additionally, the "-d" option is availible
+for debugging.
+
+     Please forward comments/suggestions for improvement/bugs to
+Erik Fair, <fair@ucbvax.berkeley.edu>.
+
+     [My thanks extended to Erik for writing nntpxmit, shlock,
+and the stats scripts.  My thanks also to Mel Pleasant
+<pleasant@topaz.rutgers.edu> for the nntpsend script.  --Phil]
diff --git a/usr/src/new/nntp/xmit/get_tcp_conn.c b/usr/src/new/nntp/xmit/get_tcp_conn.c
new file mode 100644 (file)
index 0000000..8f1b1e3
--- /dev/null
@@ -0,0 +1,115 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <ctype.h>
+#include <netdb.h>
+#include "get_tcp_conn.h"
+
+extern int     errno;
+
+/*
+** Take the name of an internet host in ASCII (this may either be its
+** official host name or internet number (with or without enclosing
+** backets [])), and return the internet address number in 32 bit quantity.
+**
+** returns FAIL for failure to find the host name in the local database,
+** or for a bad internet address spec.
+*/
+u_long
+name_to_address(host)
+register char  *host;
+{
+       if (host == (char *)NULL)
+               return(FAIL);
+
+       /*
+       ** Is this an ASCII internet address? (either of [10.0.0.78] or
+       ** 10.0.0.78).
+       */
+       if (*host == '[' || isdigit(*host)) {
+               u_long  host_address;
+               char    namebuf[128];
+               register char   *cp = namebuf;
+
+               /*
+               ** strip brackets [] or anything else we don't want.
+               */
+               while(*host && cp < &namebuf[sizeof(namebuf)]) {
+                       if (isdigit(*host) || *host == '.')
+                               *cp++ = *host++;
+                       else
+                               host++;
+               }
+
+               if ((host_address = inet_addr(namebuf)) == FAIL)
+                       return(FAIL);   /* malformed internet address spec */
+               return(host_address);
+       } else {
+               struct hostent  *hstp = gethostbyname(host);
+
+               if (hstp == NULL)
+                       return(FAIL);   /* no such host */
+               return(*(u_long *)hstp->h_addr);        /* we assume... */
+       }
+}
+
+/*
+** given a host name (either name or internet address) and service name
+** (or port number) (both in ASCII), give us a TCP connection to the
+** requested service at the requested host (or give us FAIL).
+*/
+get_tcp_conn(host,serv)
+char   *host;
+char   *serv;
+{
+       u_short port;
+       struct in_addr  host_address;
+
+       if ((host_address.s_addr = name_to_address(host)) == FAIL) {
+               return(NOHOST);
+       }
+
+       if (isdigit(*serv)) {
+               port = htons((u_short)(atoi(serv)));
+       } else {
+               struct servent  *srvp = getservbyname(serv, "tcp");
+
+               if (srvp == NULL) {
+                       return(NOSERVICE);
+               }
+               port = (u_short)srvp->s_port;
+       }
+
+       return(mkconn(&host_address, port, IPPROTO_TCP, SOCK_STREAM));
+}
+
+/*
+** create a socket and connect it to a remote host on the specified
+** port by the specified protocol. Return FAIL if something goes
+** wrong somewhere. Since these are exclusively system calls,
+** errno will have the correct error in it.
+*/
+mkconn(host_address, port, protocol, proto_type)
+struct in_addr *host_address;
+u_short        port;
+int    protocol, proto_type;
+{
+       register int    skt;
+       struct sockaddr_in      sadr;
+
+       sadr.sin_family = (u_short)AF_INET;     /* Only internet for now */
+       sadr.sin_addr.s_addr = host_address->s_addr;
+       sadr.sin_port = (u_short)port;
+
+       if ((skt = socket(AF_INET, proto_type, protocol)) < 0)
+               return(FAIL);
+
+       if (connect(skt, &sadr, sizeof(sadr)) < 0) {
+               int     save = errno;
+
+               close(skt);
+               errno = save;
+               return(FAIL);
+       }
+       return(skt);
+}
diff --git a/usr/src/new/nntp/xmit/get_tcp_conn.h b/usr/src/new/nntp/xmit/get_tcp_conn.h
new file mode 100644 (file)
index 0000000..e66bf6b
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+** Return codes from get_tcp_conn().
+*/
+#define FAIL           (-1)            /* routine failed */
+#define        NOHOST          (FAIL-1)        /* no such host */
+#define        NOSERVICE       (FAIL-2)        /* no such service */
+
+#ifndef NULL
+#define        NULL    0
+#endif
diff --git a/usr/src/new/nntp/xmit/llist.c b/usr/src/new/nntp/xmit/llist.c
new file mode 100644 (file)
index 0000000..302c964
--- /dev/null
@@ -0,0 +1,51 @@
+#include "llist.h"
+
+extern void    free();
+extern caddr_t malloc();
+
+#define        align(x)        ((x) + ((x) % sizeof(long)))
+
+/*
+** recursively free a linked list
+*/
+void
+l_free(lp)
+register struct llist *lp;
+{
+       if (lp->l_next == NULL)
+               return;
+       l_free(lp->l_next);
+       free(lp->l_item);
+}
+
+/*
+** allocate a new element in a linked list, along with enough space
+** at the end of the item for the next list element header.
+*/
+struct llist *
+l_alloc(lp, s, len)
+register struct llist  *lp;
+caddr_t        *s;
+register unsigned len;
+{
+       if (s == NULL || lp == NULL)
+               return(NULL);
+
+       lp->l_len = len;
+       len = align(len);
+
+       if ((lp->l_item = malloc(len + sizeof(struct llist))) == NULL)
+               return(NULL);
+
+       bcopy(s, lp->l_item, len);
+       lp->l_next = (struct llist *)(&lp->l_item[len]);
+
+       /*
+       ** set up next list entry
+       */
+       lp = lp->l_next;
+       lp->l_next = NULL;
+       lp->l_item = NULL;
+       lp->l_len = 0;
+       return(lp);
+}
diff --git a/usr/src/new/nntp/xmit/llist.h b/usr/src/new/nntp/xmit/llist.h
new file mode 100644 (file)
index 0000000..32d7678
--- /dev/null
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+
+struct llist {
+       struct llist    *l_next;
+       caddr_t         l_item;
+       unsigned        l_len;
+};
+
+extern void            l_free();
+extern struct llist    *l_alloc();
+
+#ifndef NULL
+#define NULL   0
+#endif
diff --git a/usr/src/new/nntp/xmit/nntpsend b/usr/src/new/nntp/xmit/nntpsend
new file mode 100755 (executable)
index 0000000..aba9cf1
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/csh -f
+#
+# What we have here is a csh script for sending netnews to NNTP sites.
+#
+set batchdir=/usr/spool/news/batch libdir=/usr/spool/news/lib
+set path=( $libdir /usr/ucb /usr/bin /bin $path )
+set pname=$0
+set pname=$pname:t
+echo ${pname}: "[$$]" begin `date`
+#
+# Go to where the action is
+#
+cd $batchdir
+umask 022
+#
+#      For NNTP
+#
+#      Here "foo", "bar", and "zot" are the Internet names of
+#      the machines to which to send.  We make the supposition
+#      that the batch files will be a host's internet name.
+#      So, for example "nike"'s internet name is "ames-titan.arpa".
+#      Because of this, your sys file must have "ames-titan.arpa"
+#      as the batch file output for the machine "nike".
+#
+foreach host ( foo bar zot )
+       set lock=NNTP_LOCK.${host} tmp=${host}.tmp send=${host}.nntp
+       shlock -p $$ -f ${lock}
+       if ($status == 0) then
+               if ( -e ${tmp} ) then
+                       cat ${tmp} >> ${send}
+                       rm ${tmp}
+               endif
+               if ( -e ${host} ) then
+# this depends upon the atomicity of the rename(2) system call used in mv(1)
+                       mv ${host} ${tmp}
+                       cat ${tmp} >> ${send}
+                       rm ${tmp}
+               endif
+               if ( -e ${send} ) then
+                       echo ${pname}: "[$$]" begin ${host}
+                       time nntpxmit ${host}:${send}
+                       echo ${pname}: "[$$]" end ${host}
+               endif
+               rm -f ${lock}
+       else
+               echo ${pname}: "[$$]" ${host} locked by "[`cat ${lock}`]"
+       endif
+end
+echo ${pname}: "[$$]" end `date`
diff --git a/usr/src/new/nntp/xmit/nntpxmit.c b/usr/src/new/nntp/xmit/nntpxmit.c
new file mode 100644 (file)
index 0000000..dfd1163
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+** nntpxmit - transmit netnews articles across the internet with nntp
+**
+** This program is for transmitting netnews between sites that offer the
+** NNTP service, internet style.  Ideally, there are two forms of
+** transmission that can be used in this environment, since the
+** communication is interactive (and relatively immediate, when compared
+** with UUCP).  They are:  passive poll (what have you gotten lately?) and
+** active send (I have `x', do you want it?).  The USENET as a whole
+** uniformly uses active send, and where the communication is batched
+** (e.g. UUCP, or electronic mail) the software sends without even asking,
+** unless it can determine that the article has already been to some site.
+**
+** It turns out that when you implement passive poll, you have to be
+** *very* careful about what you (the server) tell the client, because
+** something that you might wish to restrict distribution of (either
+** internal newsgroups, or material posted in the international groups in
+** local distributions) will otherwise leak out.  It is the case that if
+** the server doesn't tell the client that article `x' is there, the
+** client won't ask for it.  If the server tells about an article which
+** would otherwise stay within some restricted distribution, the onus is
+** then on the client to figure out that the article is not appropriate to
+** post on its local system.  Of course, at that point, we have already
+** wasted the network bandwidth in transferring the article...
+**
+** This is a roundabout way of saying that this program only implements
+** active send.  There will have to be once-over done on the NNTP spec
+** before passive poll goes in, because of the problems that I have cited.
+**
+** Erik E. Fair        <ucbvax!fair>, Oct 14, 1985
+**
+** Changed to exit on unusual errors in opening articles, and now produces
+** CPU usage statistics to syslog.
+**
+** Erik E. Fair <ucbvax!fair>, April 26, 1986
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <sys/file.h>
+#include <syslog.h>
+#include <sysexits.h>
+#include "defs.h"
+#include "header.h"
+#include "response_codes.h"
+#include "nntpxmit.h"
+
+char   *Pname;
+char   Debug = FALSE;
+char   Do_Stats = TRUE;
+char   *USAGE = "USAGE: nntpxmit [-s] hostname:file [hostname:file ...]";
+FILE   *getfp();
+
+struct Stats {
+       u_long  offered;
+       u_long  accepted;
+       u_long  rejected;
+       u_long  failed;
+} Stats = {0L, 0L, 0L, 0L};
+
+struct tms     Prev_times, Cur_times;
+time_t         Tbegin, Tend;
+
+extern int     errno;
+extern char    *rindex();
+extern char    *index();
+extern char    *errmsg();
+extern char    *sp_strip();
+extern int     msgid_ok();
+
+main(ac,av)
+int    ac;
+char   *av[];
+{
+       register int    i;
+       char    *host, *file;
+
+       (void) time(&Tbegin);
+
+       Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
+       
+       if (ac < 2) {
+               fprintf(stderr,"%s: %s\n", Pname, USAGE);
+               exit(EX_USAGE);
+       }
+
+       /* note that 4.2 BSD openlog has only two args */
+#ifdef LOG_LOCAL7
+       (void) openlog(Pname, LOG_PID, LOG_LOCAL7);
+#else
+       (void) openlog(Pname, LOG_PID);
+#endif
+
+       for(i = 1; i < ac; i++) {
+               if (av[i][0] == '-') {
+                       switch(av[i][1]) {
+                       case 's':
+                               Do_Stats = FALSE;
+                               break;
+                       case 'd':
+                               Debug++;
+                               break;
+                       default:
+                               fprintf(stderr,"%s: no such option: -%c\n",
+                                       Pname, av[i][1]);
+                               fprintf(stderr,"%s: %s\n", Pname, USAGE);
+                               exit(EX_USAGE);
+                       }
+                       continue;
+               }
+
+               /*
+               ** OK, it wasn't an option, therefore it must be a
+               ** hostname, filename pair.
+               */
+               host = av[i];
+               if ((file = index(host, ':')) != (char *)NULL) {
+                       *file++ = '\0';
+               } else {
+                       fprintf(stderr,"%s: illegal hostname:file pair: <%s>\n",
+                               Pname, host);
+                       continue;
+               }
+
+               bzero(&Stats, sizeof(Stats));
+               if (sendnews(host, file) && Do_Stats) {
+                       struct tms      delta;
+                       time_t          elapsed;
+                       syslog(LOG_INFO,
+                               "%s stats %lu offered %lu accepted %lu rejected %lu failed\n",
+                               host, Stats.offered, Stats.accepted, Stats.rejected, Stats.failed);
+
+                       click(&delta, &elapsed);
+
+                       syslog(LOG_INFO, "%s xmit user %lu system %lu elapsed %lu\n",
+                               host, delta.tms_utime, delta.tms_stime, elapsed);
+               }
+       }
+       exit(EX_OK);
+}
+
+/*
+** Calculate how much time we've used.
+**
+** The HZ constant is from times(3C) man page, and is probably wrong
+** for anything other than a VAX.
+**
+** Why `click'? Well, imagine that I've got a stopwatch in my hand...
+*/
+#define        HZ      60L
+
+click(cpu, elapsed)
+register struct tms    *cpu;
+time_t *elapsed;
+{
+       (void) times(&Cur_times);
+       (void) time(&Tend);
+
+       /* delta T */
+       *elapsed = Tend - Tbegin;
+       cpu->tms_utime = Cur_times.tms_utime - Prev_times.tms_utime;
+       cpu->tms_stime = Cur_times.tms_stime - Prev_times.tms_stime;
+       cpu->tms_cutime = Cur_times.tms_cutime - Prev_times.tms_cutime;
+       cpu->tms_cstime = Cur_times.tms_cstime - Prev_times.tms_cstime;
+
+       /* reset reference point */
+       Tbegin = Tend;  
+       Prev_times = Cur_times;
+
+       /* aggregate the children with the parent */
+       cpu->tms_utime += cpu->tms_cutime;
+       cpu->tms_stime += cpu->tms_cstime;
+
+       /* adjust these to seconds */
+       cpu->tms_utime /= HZ;
+       cpu->tms_stime /= HZ;
+}
+
+/*
+** Given a hostname to connect to, and a file of filenames (which contain
+** netnews articles), send those articles to the named host using NNTP.
+*/
+sendnews(host, file)
+char   *host, *file;
+{
+       register int    code;
+       register FILE   *filefile = fopen(file, "r");
+       register FILE   *fp;
+       char    buf[BUFSIZ];
+       char    article[BUFSIZ];
+
+       /*
+       ** if no news to send, return
+       */
+       if (filefile == (FILE *)NULL) {
+               dprintf(stderr, "%s: %s: %s\n", Pname, file, errmsg(errno));
+               return(FALSE);
+       }
+
+       if (hello(host) == FAIL) {
+               fclose(filefile);
+               return(FALSE);
+       }
+
+       while((fp = getfp(filefile, article, sizeof(article))) != (FILE *)NULL) {
+               switch(code = ihave(fp, article)) {
+               case CONT_XFER:
+                       if (!sendfile(fp)) {
+                               fprintf(stderr, "%s: %s: article transmission failed while sending %s\n", Pname, host, article);
+                               Stats.failed++;
+                               fclose(filefile);
+                               fclose(fp);
+                               goodbye(DONT_WAIT);
+                               return(TRUE);
+                       }
+                       fclose(fp);
+                       /*
+                       ** Here I read the reply from the remote about the
+                       ** transferred article, and I throw it away. I
+                       ** should probably try and record the article
+                       ** filename and append it back to the batchfile
+                       ** again in the name of reliability, but that's
+                       ** messy, and it's easier to assume that the guy
+                       ** will have redundant feeds.
+                       */
+                       code = readreply(buf, sizeof(buf));
+                       if (code != OK_XFERED) Stats.failed++;
+                       break;
+               case ERR_GOTIT:
+                       fclose(fp);
+                       break;
+               default:
+                       fprintf(stderr,"%s: %s gave an improper response to IHAVE: %d\n", Pname, host, code);
+                       fprintf(stderr,"%s: while sending article %s\n", Pname, article);
+                       fclose(filefile);
+                       fclose(fp);
+                       goodbye(DONT_WAIT);
+                       return(TRUE);
+               }
+       }
+       fclose(filefile);
+       if (unlink(file) < 0) {
+               fprintf(stderr,"%s: unlink(%s): %s\n", Pname, file, errmsg(errno));
+       }
+       goodbye(WAIT);
+       return(TRUE);
+}
+
+/*
+** Read the header of a netnews article, snatch the message-id therefrom,
+** and ask the remote if they have that one already.
+*/
+ihave(fp, article)
+FILE   *fp;
+char   *article;
+{
+       register int    code;
+       struct hbuf     header;
+       char    scr[LBUFLEN];
+       char    buf[BUFSIZ];
+
+       bzero(&header, sizeof(header));
+       if (!rfc822read(&header, fp, scr)) {
+               /*
+               ** something botched locally with the article
+               ** so we don't send it, but we don't break off
+               ** communications with the remote either.
+               */
+               return(ERR_GOTIT);
+       }
+
+       /*
+       ** If an article shows up without a message-id,
+       ** or with a bogus message-id,
+       ** we scream bloody murder. That's one in
+       ** the `can't ever happen' category.
+       */
+       if (header.ident[0] == '\0') {
+               fprintf(stderr, "%s: %s missing message-id!\n", Pname, article);
+               return(ERR_GOTIT);
+       } else {
+               (void) strcpy(scr, sp_strip(header.ident));
+       }
+
+       if (!msgid_ok(scr)) {
+               fprintf(stderr, "%s: %s message-id syntax error!\n", Pname, article);
+               return(ERR_GOTIT);
+       }
+
+       sprintf(buf, "IHAVE %s", scr);
+       Stats.offered++;
+
+       switch(code = converse(buf, sizeof(buf))) {
+       case CONT_XFER:
+               Stats.accepted++;
+               rewind(fp);
+               return(code);
+       default:
+               Stats.rejected++;
+               return(code);
+       }
+}
+
+/*
+** Given that fp points to an open file containing filenames,
+** open and return a file pointer to the next filename in the file.
+** Don't you love indirection?
+**
+** Returns a valid FILE pointer or NULL if end of file.
+*/
+
+FILE *
+getfp(fp, filename, fnlen)
+register FILE  *fp;
+char   *filename;
+register unsigned      fnlen;
+{
+       register FILE   *newfp = (FILE *)NULL;
+       register char   *cp;
+
+       while(newfp == (FILE *)NULL) {
+               if (fgets(filename, fnlen, fp) == (char *)NULL)
+                       return((FILE *)NULL);           /* EOF, tell caller */
+
+               filename[fnlen - 1] = '\0';     /* make sure */
+
+               if (*(cp = &filename[strlen(filename) - 1]) == '\n')
+                       *cp = '\0';
+
+               if ((newfp = fopen(filename, "r")) == (FILE *)NULL) {
+                       register int    save = errno;
+
+                       fprintf(stderr, "%s: fopen(%s, \"r\"): %s\n", Pname, filename, errmsg(errno));
+                       /*
+                       ** The only permissible error is `file non-existant'
+                       ** anything else indicates something is seriously
+                       ** wrong, and we should go away to let the shell
+                       ** script clean up.
+                       */
+                       if (save != ENOENT)
+                               exit(EX_OSERR);
+               }
+       }
+       return(newfp);
+}
diff --git a/usr/src/new/nntp/xmit/nntpxmit.h b/usr/src/new/nntp/xmit/nntpxmit.h
new file mode 100644 (file)
index 0000000..f4b153a
--- /dev/null
@@ -0,0 +1,10 @@
+#define        dprintf if (Debug) fprintf
+
+#define        TIMEOUT 3600    /* seconds to read timeout in sfgets */
+
+/* in goodbye() wait (or not) for QUIT response */
+#define        WAIT            1
+#define        DONT_WAIT       0
+#ifndef FAIL
+#define        FAIL            (-1)
+#endif
diff --git a/usr/src/new/nntp/xmit/remote.c b/usr/src/new/nntp/xmit/remote.c
new file mode 100644 (file)
index 0000000..f2534e5
--- /dev/null
@@ -0,0 +1,300 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <signal.h>
+#include "get_tcp_conn.h"
+#include "response_codes.h"
+#include "nntpxmit.h"
+
+#define        TRUE    1
+#define        FALSE   0
+
+static jmp_buf SFGstack;
+FILE   *rmt_rd;
+FILE   *rmt_wr;
+char   *sfgets();
+char   *rfgets();
+
+extern int     errno;
+extern char    *Pname;
+extern char    Debug;
+extern char    *errmsg();
+
+/*
+** send cmd to remote, terminated with a CRLF.
+*/
+sendcmd(cmd)
+char   *cmd;
+{
+       dprintf(stderr, "<<< %s\n", cmd);       /* DEBUG */
+       (void) fprintf(rmt_wr, "%s\r\n", cmd);
+       (void) fflush(rmt_wr);
+       return(ferror(rmt_wr));
+}
+
+/*
+** read a reply line from the remote server and return the code number
+** as an integer, and the message in a buffer supplied by the caller.
+** Returns FAIL if something went wrong.
+*/
+readreply(buf, size)
+register char  *buf;
+int    size;
+{
+       register char   *cp;
+       register int    len;
+
+       /*
+       ** make sure it's invalid, unless we say otherwise
+       */
+       buf[0] = '\0';
+
+       /*
+       ** read one line from the remote
+       */
+       if (sfgets(buf, size, rmt_rd) == NULL)
+               return(FAIL);   /* error reading from remote */
+
+       /*
+       ** Make sure that what the remote sent us had a CRLF at the end
+       ** of the line, and then null it out.
+       */
+       if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r' &&
+               *(cp + 1) == '\n')
+       {
+               *cp = '\0';
+       } else
+               return(FAIL);   /* error reading from remote */
+
+       dprintf(stderr, ">>> %s\n", buf);       /* DEBUG */
+       /*
+       ** Skip any non-digits leading the response code 
+       ** and then convert the code from ascii to integer for
+       ** return from this routine.
+       */
+       cp = buf;
+       while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
+               cp++;   /* skip anything leading */
+
+       if (*cp == '\0' || !isascii(*cp))
+               return(FAIL);   /* error reading from remote */
+
+       return(atoi(cp));
+}
+
+/*
+** send a command to the remote, and wait for a response
+** returns the response code, and the message in the buffer
+*/
+converse(buf, size)
+char   *buf;
+int    size;
+{
+       register int    resp;
+
+       if (sendcmd(buf))
+               return(FAIL);   /* Ooops! Something went wrong in xmit */
+       /*
+       ** Skip the silly 100 series messages, since they're not the
+       ** final response we can expect
+       */
+       while((resp = readreply(buf, size)) >= 100 && resp < 200)
+               continue;
+       return(resp);
+}
+
+/*
+** Contact the remote server and set up the two global FILE pointers
+** to that socket.
+*/
+hello(host)
+char   *host;
+{
+       int     socket0, socket1;       /* to me (bad pun) */
+       static char     *service = "nntp";
+       char    buf[BUFSIZ];
+
+       switch(socket0 = get_tcp_conn(host, service)) {
+       case NOHOST:
+               fprintf(stderr,"%s: no such host <%s>\n", Pname, host);
+               return(FAIL);
+       case NOSERVICE:
+               fprintf(stderr,"%s: no such service <%s>\n", Pname, service);
+               return(FAIL);
+       case FAIL:
+               fprintf(stderr,"%s: %s: %s\n", Pname, host, errmsg(errno));
+               return(FAIL);
+       }
+
+       if ((socket1 = dup(socket0)) < 0) {
+               close(socket0);
+               fprintf(stderr,"%s: dup(2): %s\n", Pname, errmsg(errno));
+               return(FAIL);
+       }
+
+       if ((rmt_rd = fdopen(socket0, "r")) == (FILE *)NULL) {
+               close(socket0);
+               close(socket1);
+               fprintf(stderr,"%s: fdopen(3): %s\n", Pname, errmsg(errno));
+               return(FAIL);
+       }
+
+       if ((rmt_wr = fdopen(socket1, "w")) == (FILE *)NULL) {
+               fclose(rmt_rd);
+               rmt_rd = (FILE *)NULL;
+               close(socket1);
+               fprintf(stderr,"%s: fdopen(3): %s\n", Pname, errmsg(errno));
+               return(FAIL);
+       }
+
+       switch(readreply(buf, sizeof(buf))) {
+       case OK_CANPOST:
+       case OK_NOPOST:
+               if (ferror(rmt_rd)) {
+                       goodbye(DONT_WAIT);
+                       return(FAIL);
+               }
+               break;
+       default:
+               if (buf[0] != '\0')
+                       fprintf(stderr, "%s: %s\n", Pname, buf);
+               goodbye(DONT_WAIT);
+               return(FAIL);
+       }
+       return(NULL);
+}
+
+/*
+** Say goodbye to the nice remote server.
+*/
+goodbye(wait)
+int    wait;
+{
+       if (sendcmd("QUIT"))
+               wait = FALSE;   /* override, something's wrong. */
+       /*
+       ** I don't care what they say to me; this is just being polite.
+       */
+       if (wait) {
+               char    buf[BUFSIZ];
+
+               (void) readreply(buf, sizeof(buf));
+       }
+       (void) fclose(rmt_rd);
+       rmt_rd = (FILE *)NULL;
+       (void) fclose(rmt_wr);
+       rmt_wr = (FILE *)NULL;
+}
+
+static
+to_sfgets()
+{
+       longjmp(SFGstack, 1);
+}
+
+/*
+** `Safe' fgets, ala sendmail. This fgets will timeout after some
+** period of time, on the assumption that if the remote did not
+** return, they're gone.
+** WARNING: contains a possibly unportable reference to stdio
+** error macros.
+*/
+char *
+sfgets(buf, size, fp)
+char   *buf;
+int    size;
+FILE   *fp;
+{
+       register char   *ret;
+
+       if (setjmp(SFGstack)) {
+               alarm(0);                       /* reset alarm clock */
+               signal(SIGALRM, SIG_DFL);       /* reset SIGALRM */
+               fp->_flag |= _IOERR;            /* set stdio error */
+               return(NULL);                   /* bad read, remote time out */
+       }
+       signal(SIGALRM, to_sfgets);
+       alarm(TIMEOUT);
+       ret = fgets(buf, size, fp);
+       alarm(0);                       /* reset alarm clock */
+       signal(SIGALRM, SIG_DFL);       /* reset SIGALRM */
+       return(ret);
+}
+
+/*
+** Remote fgets - converts CRLF to \n, and returns NULL on `.' EOF from
+** the remote. Otherwise it returns its first argument, like fgets(3).
+*/
+char *
+rfgets(buf, size, fp)
+char   *buf;
+int    size;
+FILE   *fp;
+{
+       register char   *cp = buf;
+       register int    len;
+
+       *cp = '\0';
+       if (sfgets(buf, size, fp) == NULL)
+               return(NULL);
+
+       /* <CRLF> => '\n' */
+       if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') {
+               *cp++ = '\n';
+               *cp = '\0';
+       }
+
+       /* ".\n" => EOF */
+       cp = buf;
+       if (*cp++ == '.' && *cp == '\n') {
+               return(NULL);   /* EOF */
+       }
+
+       /* Dot escaping */
+       if (buf[0] == '.') strcpy(&buf[0], &buf[1]);
+       return(buf);
+}
+
+/*
+** send the contents of an open file descriptor to the remote,
+** with appropriate RFC822 filtering (e.g. CRLF line termination,
+** and dot escaping). Return FALSE if something went wrong.
+*/
+sendfile(fp)
+FILE   *fp;
+{
+       register int    c;
+       register int    nl = TRUE;      /* assume we start on a new line */
+
+       if (fp == (FILE *)NULL)
+               return(FALSE);
+
+       while((c = fgetc(fp)) != EOF) {
+               switch(c) {
+               case '\n':
+                       (void) fputc('\r', rmt_wr);     /* \n -> \r\n */
+                       (void) fputc(c, rmt_wr);
+                       nl = TRUE;              /* for dot escaping */
+                       break;
+               case '.':
+                       if (nl) {
+                               (void) fputc(c, rmt_wr);        /* add a dot */
+                               nl = FALSE;
+                       }
+                       (void) fputc(c, rmt_wr);
+                       break;
+               default:
+                       (void) fputc(c, rmt_wr);
+                       nl = FALSE;
+                       break;
+               }
+       }
+       if (!nl) {
+               (void) fputs("\r\n", rmt_wr);
+       }
+       (void) fputs(".\r\n", rmt_wr);  /* <CRLF>.<CRLF> termination */
+       (void) fflush(rmt_wr);
+       if (ferror(fp) || ferror(rmt_wr))       /* any errors? */
+               return(FALSE);
+       return(TRUE);
+}
diff --git a/usr/src/new/nntp/xmit/rfc822.c b/usr/src/new/nntp/xmit/rfc822.c
new file mode 100644 (file)
index 0000000..f4d8ff5
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+** Get header info from mail-format file.
+** Return non-zero on success.
+*/
+
+#include "defs.h"
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "header.h"
+#include "llist.h"
+
+#ifndef isblank
+#define isblank(c)     ((c) == ' ' || (c) == '\t')
+#endif
+
+char   *hfgets();
+char   *arpadate();
+char   *errmsg();
+char   *strpbrk();
+time_t cgtdate();
+extern char    *index();
+extern unsigned        strlen();
+extern time_t  time();
+
+#define FROM           1
+#define NEWSGROUP      2
+#define TITLE          3
+#define SUBMIT         4
+#define RECEIVE                5
+#define EXPIRE         6
+#define ARTICLEID      7
+#define MESSAGEID      8
+#define REPLYTO                9
+#define FOLLOWID       10
+#define CONTROL                11
+#define SENDER         12
+#define FOLLOWTO       13
+#define PATH           14
+#define POSTVERSION    15
+#define RELAYVERSION   16
+#define DISTRIBUTION   17
+#define ORGANIZATION   18
+#define NUMLINES       19
+#define KEYWORDS       20
+#define APPROVED       21
+#define NFID           22
+#define NFFROM         23
+#define XREF           24
+#define SUMMARY                25
+#define FULLNAME       26
+#define OTHER          99
+
+/*
+** This is the list of headers we recognize.
+** All others get stripped before they get to inews.
+*/
+struct htype   {
+       char    *hname;
+       int     hid;
+} htype[] = {
+       {"Approved:",           APPROVED},
+       {"Article-I.D.:",       ARTICLEID},
+       {"Control:",            CONTROL},
+       {"Date-Received:",      RECEIVE},
+       {"Date:",               SUBMIT},
+       {"Distribution:",       DISTRIBUTION},
+       {"Expires:",            EXPIRE},
+       {"Followup-To:",        FOLLOWTO},
+       {"From:",               FROM},
+/*     {"Full-Name:",          FULLNAME},      */
+       {"In-Reply-To:",        FOLLOWID},
+       {"Keywords:",           KEYWORDS},
+       {"Lines:",              NUMLINES},
+       {"Message-ID:",         MESSAGEID},
+       {"Newsgroups:",         NEWSGROUP},
+       {"Nf-From:",            NFFROM},
+       {"Nf-ID:",              NFID},
+       {"Organization:",       ORGANIZATION},
+       {"Path:",               PATH},
+       {"Posted:",             SUBMIT},
+       {"Posting-Version:",    POSTVERSION},
+/*     {"Received:",           RECEIVE},       a bad name w.r.t. RFC822 */
+       {"References:",         FOLLOWID},
+       {"Relay-Version:",      RELAYVERSION},
+       {"Reply-To:",           REPLYTO},
+       {"Sender:",             SENDER},
+       {"Subject:",            TITLE},
+       {"Summary:",            SUMMARY},
+       {"Title:",              TITLE},
+       {"Xref:",               XREF},
+       {(char *)NULL,          NULL}
+};
+
+char *malloc();
+
+rfc822read(hp, fp, bfr)
+register struct hbuf *hp;
+register FILE *fp;
+char   *bfr;
+{
+       register int    i = type(bfr);
+       long    curpos;
+
+       do {
+               curpos = ftell(fp);
+               switch (i) {
+               case FROM:
+                       getfield(bfr, hp->from, sizeof(hp->from));
+                       break;
+               case NEWSGROUP:
+                       getfield(bfr, hp->nbuf, sizeof(hp->nbuf));
+                       break;
+               case TITLE:
+                       getfield(bfr, hp->title, sizeof(hp->title));
+                       break;
+               case SUBMIT:
+                       getfield(bfr, hp->subdate, sizeof(hp->subdate));
+                       break;
+               case EXPIRE:
+                       getfield(bfr, hp->expdate, sizeof(hp->expdate));
+                       break;
+               case MESSAGEID:
+                       getfield(bfr, hp->ident, sizeof(hp->ident));
+                       break;
+               case REPLYTO:
+                       getfield(bfr, hp->replyto, sizeof(hp->replyto));
+                       break;
+               case FOLLOWID:
+                       getfield(bfr, hp->followid, sizeof(hp->followid));
+                       break;
+               case SENDER:
+                       getfield(bfr, hp->sender, sizeof(hp->sender));
+                       break;
+               case FOLLOWTO:
+                       getfield(bfr, hp->followto, sizeof(hp->followto));
+                       break;
+               case CONTROL:
+                       getfield(bfr, hp->ctlmsg, sizeof(hp->ctlmsg));
+                       break;
+               case DISTRIBUTION:
+                       getfield(bfr, hp->distribution, sizeof(hp->distribution));
+                       break;
+               case ORGANIZATION:
+                       getfield(bfr, hp->organization, sizeof(hp->organization));
+                       break;
+               case KEYWORDS:
+                       getfield(bfr, hp->keywords, sizeof(hp->keywords));
+                       break;
+               case APPROVED:
+                       getfield(bfr, hp->approved, sizeof(hp->approved));
+                       break;
+               case SUMMARY:
+                       getfield(bfr, hp->summary, sizeof(hp->summary));
+                       break;
+               }
+       } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
+
+       if (*bfr != '\n')
+               fseek(fp, curpos, 0);
+       return(TRUE);
+}
+
+#define its(type) (prefix(ptr, type))
+
+type(ptr)
+register char  *ptr;
+{
+       register struct htype   *hp;
+       register char   *colon, *space;
+       static int      lasthdr = FALSE;        /* for continuation headers */
+
+       /*
+       ** some consistency checks (i.e. is this really a header line?)
+       */
+       if ((ptr == NULL) || !isascii(*ptr) || (*ptr == '\n'))
+               return(lasthdr = FALSE);
+
+       if (isblank(*ptr))              /* continuation line? */
+               return(lasthdr);
+
+       colon = index(ptr, ':');
+       space = index(ptr, ' ');
+       if (!colon || space && space < colon)
+               return(lasthdr = FALSE);
+
+       for(hp = htype; hp->hname; hp++) {
+               if (its(hp->hname))
+                       return(lasthdr = hp->hid);
+       }
+       return(lasthdr = OTHER);
+}
+
+/*
+** Get the contents of the field of the header line, appending it,
+** with a space delimeter if it's a continuation line.
+** If there is already something in the header storage, skip this
+** header line and the continuations.
+*/
+getfield(src, dest, size)
+register char  *src, *dest;
+int    size;                   /* of dest (total bytes) */
+{
+       static int      skip = FALSE;   /* skip the continuation lines */
+
+       if (src == (char *)NULL || dest == (char *)NULL)
+               return(FALSE);
+
+       if (isblank(*src)) {                            /* continuation line? */
+               if (skip)
+                       return(TRUE);
+               if ((size -= strlen(dest)) <= 0)        /* any space left? */
+                       return(FALSE);
+               while(*src && isblank(*src))            /* eat whitespace */
+                       src++;
+               *--src = ' ';                           /* backup & add one */
+               (void) strncat(dest, src, size - 1);    /* append to hdr */
+       } else {
+               skip = FALSE;
+               if (*dest)                              /* already got one? */
+                       return(skip = TRUE);            /* skip continuation */
+               if ((src = index(src, ':')) == (char *)NULL)    /* impossible */
+                       return(FALSE);
+               src++;                                  /* skip colon */
+               while(*src && isblank(*src))            /* eat whitespace */
+                       src++;
+               (void) strncpy(dest, src, size - 1);
+       }
+       (void) nstrip(dest);
+       return(TRUE);
+}
+
+/*
+** Write out an RFC822 header, paying no attention to line limits.
+** Ideally, we should do continuations in here...
+*/
+rfc822write(hp, fp)
+register struct hbuf *hp;
+register FILE *fp;
+{
+       time_t t;
+
+       if (hp->path[0])
+               fprintf(fp, "Path: %s\n", hp->path);
+       if (hp->from[0])
+               fprintf(fp, "From: %s\n", hp->from);
+       if (hp->nbuf[0])
+               fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
+       if (hp->title[0])
+               fprintf(fp, "Subject: %s\n", hp->title);
+       if (hp->ident[0])
+               fprintf(fp, "Message-ID: %s\n", hp->ident);
+       if (hp->subdate[0])
+               t = cgtdate(hp->subdate);
+       else
+               time(&t);
+       fprintf(fp, "Date: %s\n", arpadate(&t));
+       if (*hp->expdate)
+               fprintf(fp, "Expires: %s\n", hp->expdate);
+       if (*hp->followid)
+               fprintf(fp, "References: %s\n", hp->followid);
+       if (*hp->ctlmsg)
+               fprintf(fp, "Control: %s\n", hp->ctlmsg);
+       if (*hp->sender)
+               fprintf(fp, "Sender: %s\n", hp->sender);
+       if (*hp->replyto)
+               fprintf(fp, "Reply-To: %s\n", hp->replyto);
+       if (*hp->followto)
+               fprintf(fp, "Followup-To: %s\n", hp->followto);
+       if (*hp->distribution)
+               fprintf(fp, "Distribution: %s\n", hp->distribution);
+       if (*hp->organization)
+               fprintf(fp, "Organization: %s\n", hp->organization);
+       if (*hp->keywords)
+               fprintf(fp, "Keywords: %s\n", hp->keywords);
+       if (*hp->summary)
+               fprintf(fp, "Summary: %s\n", hp->summary);
+       if (*hp->approved)
+               fprintf(fp, "Approved: %s\n", hp->approved);
+       putc('\n', fp);
+       return(!ferror(fp));
+}
+
+/*
+** strip leading and trailing spaces
+*/
+char *
+sp_strip(s)
+register char  *s;
+{
+       register char   *cp;
+
+       if (s == NULL)
+               return(NULL);
+
+       if (*s == '\0')
+               return(s);
+       
+       cp = &s[strlen(s) - 1];
+       while(cp > s && isspace(*cp))
+               cp--;
+
+       *++cp = '\0';   /* zap trailing spaces */
+
+       for(cp = s; *cp && isspace(*cp); cp++)
+               continue;
+
+       return(cp);     /* return pointer to first non-space */
+}
+
+/*
+** crack an RFC822 from header field into address and fullname.
+*/
+crackfrom(addr,name,field)
+char   *addr, *name, *field;
+{
+       char    commbuf[LBUFLEN];
+       char    addrbuf[LBUFLEN];
+       register char   *p;
+       register char   *ap = addrbuf;
+       register char   *np;
+       short   commfound = 0, comment = 0;
+       short   addrfound = 0, address = 0;
+       struct llist    comm, *cp = &comm;
+
+       *name = '\0';   /* just make sure */
+       *addr = '\0';
+
+       if ((p = field) == NULL)
+               return(FALSE);
+
+       while(*p && isspace(*p))        /* eat leading white space */
+               p++;
+
+       while(*p) {
+               switch(*p) {
+               case '(':
+                       if (comment == 0) {
+                               np = commbuf;
+                               *np = '\0';
+                       }
+                       comment++;
+                       break;
+               case ')':
+                       if (comment > 0 && --comment == 0) {
+                               *np++ = *p++;   /* note incr; skip `)' */
+                               *np++ = '\0';
+                               if ((cp = l_alloc(cp, commbuf, strlen(commbuf) + 1)) == NULL) {
+                                       if (commfound)
+                                               l_free(comm);
+                                       return(FALSE);
+                               }
+                               commfound++;
+                               np = NULL;
+                               continue;
+                       }
+                       break;
+               case '<':
+                       if (address) {
+                               if (commfound)
+                                       l_free(comm);
+                               return(FALSE);  /* AWK! Abort! */
+                       }
+                       if (!comment) {
+                               address++;
+                               *ap = '\0';
+                               ap = addr;
+                       }
+                       break;
+               case '>':
+                       if (!comment && address) {
+                               address--;
+                               addrfound++;
+                               *ap = '\0';
+                               ap = &addrbuf[strlen(addrbuf)];
+                               p++;    /* skip the `>' */
+                       }
+                       break;
+               }
+
+               if (comment) {
+                       *np++ = *p;
+               } else if (address) {
+                       if (*p != '<')
+                               *ap++ = *p;
+               } else {
+                       *ap++ = *p;
+               }
+               if (*p)
+                       p++;
+               else
+                       break;
+       }
+
+       *ap++ = '\0';
+
+       if (addrfound) {
+               (void) strcpy(name, sp_strip(addrbuf));
+               (void) strcpy(addr, strcpy(commbuf, sp_strip(addr)));
+       } else {
+               (void) strcpy(addr, sp_strip(addrbuf));
+               *name = '\0';
+       }
+       /*
+       ** Just to be sure that we got the full name,
+       ** we'll take all of the comments!
+       */
+       if (commfound) {
+               register int    len;
+               register int    flag = (*name != '\0' ? TRUE : FALSE);
+
+               for(cp = &comm; cp->l_item; cp = cp->l_next) {
+                       if (flag)
+                               (void)strcat(name, ", ");
+                       flag = TRUE;
+                       if (cp->l_item[cp->l_len - 2] == ')')
+                               cp->l_item[cp->l_len - 2] = '\0';
+                       (void)strcat(name, sp_strip(&cp->l_item[1]));
+               }
+               l_free(comm);
+       }
+       return(TRUE);
+}
+
+/*
+** Check on the validity of an RFC822 message-id.
+** Check for enclosing `<>', an `@', and a `.' after
+** the `@'. Also make sure that everything is ASCII,
+** and non-control characters.
+*/
+msgid_ok(id)
+register char  *id;
+{
+       register atdot = FALSE;
+       register closure = FALSE;
+
+       if (id == NULL)
+               return(FALSE);          /* don't waste my time! */
+
+       if (*id != '<')
+               return(FALSE);
+
+       /* skip the first `<', cause we check for more */
+       for(id++; *id; id++) {
+               switch(*id) {
+               case '<':
+                       return(FALSE);  /* we've already got one */
+               case '>':
+                       closure = TRUE;
+                       break;
+               case '.':
+               case '@':               /* should be a domain spec */
+                       atdot = TRUE;
+                       break;
+               default:
+                       if (!isascii(*id) || iscntrl(*id) || isspace(*id))
+                               return(FALSE);  /* quit immediately */
+                       break;
+               }
+       }
+       return(atdot && closure);
+}
+
+/*
+** hfgets is like fgets, but deals with continuation lines.
+** It also ensures that even if a line that is too long is
+** received, the remainder of the line is thrown away
+** instead of treated like a second line.
+*/
+char *
+hfgets(buf, len, fp)
+char *buf;
+int len;
+FILE *fp;
+{
+       register int c;
+       register int n = 0;
+       register char *cp;
+
+       cp = buf;
+       while (n < len && (c = getc(fp)) != EOF) {
+               if (c == '\n')
+                       break;
+               if (!iscntrl(c) || c == '\b' || c == '\t') {
+                       *cp++ = c;
+                       n++;
+               }
+       }
+       if (c == EOF && cp == buf)
+               return NULL;
+       *cp = '\0';
+
+       if (c != '\n') {
+               /* Line too long - part read didn't fit into a newline */
+               while ((c = getc(fp)) != '\n' && c != EOF)
+                       ;
+       } else if (cp == buf) {
+               /* Don't look for continuation of blank lines */
+               *cp++ = '\n';
+               *cp = '\0';
+               return buf;
+       }
+
+       while ((c = getc(fp)) == ' ' || c == '\t') {    /* for each cont line */
+               /* Continuation line. */
+               if ((n += 2) < len) {
+                       *cp++ = '\n';
+                       *cp++ = c;
+               }
+               while ((c = getc(fp)) != '\n' && c != EOF)
+                       if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
+                               *cp++ = c;
+       }
+       if (n >= len - 1)
+               cp = buf + len - 2;
+       *cp++ = '\n';
+       *cp = '\0';
+       if (c != EOF)
+               (void) ungetc(c, fp); /* push back first char of next header */
+       return buf;
+}
+
+/*
+ * arpadate is like ctime(3) except that the time is returned in
+ * an acceptable ARPANET time format instead of ctime format.
+ */
+char *
+arpadate(longtime)
+time_t *longtime;
+{
+       register char *p, *q, *ud;
+       register int i;
+       static char b[40];
+       extern struct tm *gmtime();
+       extern char *asctime();
+
+       /*  Get current time. This will be used resolve the timezone. */
+       ud = asctime(gmtime(longtime));
+
+       /*  Crack the UNIX date line in a singularly unoriginal way. */
+       q = b;
+
+#ifdef notdef
+/* until every site installs the fix to getdate.y, the day
+   of the week can cause time warps */
+       p = &ud[0];             /* Mon */
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = ','; *q++ = ' ';
+#endif
+
+       p = &ud[8];             /* 16 */
+       if (*p == ' ')
+               p++;
+       else
+               *q++ = *p++;
+       *q++ = *p++; *q++ = ' ';
+
+       p = &ud[4];             /* Sep */
+       *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
+
+       p = &ud[22];            /* 1979 */
+       *q++ = *p++; *q++ = *p++; *q++ = ' ';
+
+       p = &ud[11];            /* 01:03:52 */
+       for (i = 8; i > 0; i--)
+               *q++ = *p++;
+
+       *q++ = ' ';
+       *q++ = 'G';             /* GMT */
+       *q++ = 'M';
+       *q++ = 'T';
+       *q = '\0';
+
+       return b;
+}
+
+time_t
+cgtdate(datestr)
+char *datestr;
+{
+       char    junk[40], month[40], day[30], tod[60], year[50], buf[BUFLEN];
+       static time_t   lasttime;
+       static char     lastdatestr[BUFLEN] = "";
+       extern time_t   getdate();
+
+       if (lastdatestr[0] && strcmp(datestr, lastdatestr) == 0)
+               return(lasttime);
+       lasttime = getdate(datestr, (struct timeb *)NULL);
+       if (lasttime < 0 &&
+         sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
+               (void) sprintf(buf, "%s %s, %s %s", month, day, year, tod);
+               lasttime = getdate(buf, (struct timeb *)NULL);
+       }
+       strncpy(lastdatestr, datestr, BUFLEN);
+       return(lasttime);
+}
+
+char *
+errmsg(code)
+int code;
+{
+       extern int sys_nerr;
+       extern char *sys_errlist[];
+       static char ebuf[6+5+1];
+
+       if (code > sys_nerr) {
+               (void) sprintf(ebuf, "Error %d", code);
+               return ebuf;
+       } else
+               return sys_errlist[code];
+}
+
+/*
+ * Strip trailing newlines, blanks, and tabs from 's'.
+ * Return TRUE if newline was found, else FALSE.
+ */
+nstrip(s)
+register char *s;
+{
+       register char *p;
+       register int rc;
+
+       rc = FALSE;
+       p = s;
+       while (*p)
+               if (*p++ == '\n')
+                       rc = TRUE;
+       while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
+       *++p = '\0';
+       return rc;
+}
+
+prefix(full, pref)
+register char *full, *pref;
+{
+       register char fc, pc;
+
+       while ((pc = *pref++) != '\0') {
+               fc = *full++;
+               if (isupper(fc))
+                       fc = tolower(fc);
+               if (isupper(pc))
+                       pc = tolower(pc);
+               if (fc != pc)
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+#ifndef USG
+char *
+strpbrk(str, chars)
+register char *str, *chars;
+{
+       register char *cp;
+
+       do {
+               cp = chars - 1;
+               while (*++cp) {
+                       if (*str == *cp)
+                               return str;
+               }
+       } while (*str++);
+       return NULL;
+}
+#endif /* !USG */
diff --git a/usr/src/new/nntp/xmit/shlock.c b/usr/src/new/nntp/xmit/shlock.c
new file mode 100644 (file)
index 0000000..e959418
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+** Program to produce reliable locks for shell scripts.
+** Algorithmn suggested by Peter Honeyman, January 1984, in connection
+** with HoneyDanBer UUCP.
+**
+** Erik E. Fair <ucbvax!fair>
+*/
+
+#include <stdio.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#define        LOCK_SET        0
+#define        LOCK_FAIL       1
+
+#define TRUE   1
+#define        FALSE   0
+
+int    Verbose = FALSE;
+char   *Pname;
+char   *USAGE = "%s: USAGE: shlock -f file -p pid [-v]\n";
+char   *errmsg();
+char   *tmpfile();
+
+#define        vprintf if (Verbose) printf
+
+extern int     errno;
+extern char    *rindex();
+extern char    *strcpy();
+extern char    *strcat();
+
+main(ac,av)
+int    ac;
+char   *av[];
+{
+       register int    x;
+       char    *file;
+       int     pid;
+
+       Pname = ((Pname = rindex(av[0], '/')) ? Pname + 1 : av[0]);
+
+       for(x = 1; x < ac; x++) {
+               if (av[x][0] == '-') {
+                       switch(av[x][1]) {
+                       case 'v':
+                               Verbose = TRUE;
+                               break;
+                       case 'p':
+                               if (strlen(av[x]) > 2) {
+                                       pid = atoi(&av[x][2]);
+                               } else {
+                                       pid = atoi(av[x + 1]);
+                                       x++;
+                               }
+                               break;
+                       case 'f':
+                               if (strlen(av[x]) > 2) {
+                                       file = &av[x][2];
+                               } else {
+                                       file = av[x + 1];
+                                       x++;
+                               }
+                               break;
+                       default:
+                               fprintf(stderr, USAGE, Pname);
+                               exit(LOCK_FAIL);
+                       }
+               }
+       }
+       if (pid == 0 || file == (char *)NULL) {
+               fprintf(stderr, USAGE, Pname);
+               exit(LOCK_FAIL);
+       }
+       if (mklock(file, pid))
+               exit(LOCK_SET);
+       exit(LOCK_FAIL);
+}
+
+mklock(file, pid)
+char   *file;
+int    pid;
+{
+       register int    fd;
+       register int    len;
+       register char   *tmp = tmpfile(file);
+       char    *buf[BUFSIZ];
+
+       vprintf("%s: attempting to get lock <%s> for process %d\n", Pname, file, pid);
+       sprintf(buf, "%d\n", pid);
+       len = strlen(buf);
+loop:
+       if ((fd = open(tmp, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) {
+               switch(errno) {
+               case EEXIST:
+                       vprintf("%s: temporary file %s exists already.\n", Pname, tmp);
+                       if (unlink(tmp) < 0) {
+                               fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno));
+                               return(FALSE);
+                       }
+                       /*
+                       ** I hereby profane the god of structured programming
+                       ** Edsgar Djikstra
+                       */
+                       goto loop;
+               default:
+                       fprintf(stderr,"%s: open(%s): %s\n", Pname, tmp, errmsg(errno));
+                       return(FALSE);
+               }
+       }
+
+       /*
+       ** Write the PID into the temporary file before attempting to link
+       ** to the actual lock file. That way we have a valid lock the instant
+       ** the link succeeds.
+       */
+       if (write(fd, buf, len) < 0) {
+               fprintf(stderr, "%s: write(%s,%d): %s\n", Pname, tmp, pid, errmsg(errno));
+               close(fd);
+               return(FALSE);
+       }
+       close(fd);
+
+loop2:
+       if (link(tmp, file) < 0) {
+               switch(errno) {
+               case EEXIST:
+                       vprintf("%s: lock <%s> already exists\n", Pname, file);
+                       if (cklock(file)) {
+                               vprintf("%s: extant lock is valid\n", Pname);
+                               if (unlink(tmp) < 0) {
+                                       fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno));
+                               }
+                               return(FALSE);
+                       } else {
+                               vprintf("%s: extant lock is invalid, removing\n", Pname);
+                               if (unlink(file) < 0) {
+                                       fprintf(stderr,"%s: unlink(%s): %s\n", Pname, file, errmsg(errno));
+                                       return(FALSE);
+                               }
+                       }
+                       goto loop2;
+               default:
+                       fprintf(stderr,"%s: link(%s, %s): %s\n", Pname, tmp, file, errmsg(errno));
+                       return(FALSE);
+               }
+       }
+       if (unlink(tmp) < 0) {
+               fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno));
+       }
+       vprintf("%s: got lock <%s>\n", Pname, file);
+       return(TRUE);
+}
+
+/*
+** Check the validity of an existing lock file.
+**
+**     Read the PID out of the lock
+**     Send a null signal to determine whether that PID still exists
+**     Existence (or not) determines the validity of the lock.
+**
+**     Two bigs wins to this algorithmn:
+**
+**     o       Locks do not survive crashes of either the system or the
+**                     application by any appreciable period of time.
+**
+**     o       No clean up to do if the system or application crashes.
+**
+*/
+
+cklock(file)
+char   *file;
+{
+       register int    fd = open(file, O_RDONLY);
+       register int    len;
+       char    buf[BUFSIZ];
+
+       vprintf("%s: checking extant lock <%s>\n", Pname, file);
+       if (fd < 0) {
+               fprintf(stderr,"%s: open(%s): %s\n", Pname, file, errmsg(errno));
+               return(TRUE);   /* might or might not; conservatism */
+       }
+       
+       if ((len = read(fd, buf, sizeof(buf))) <= 0) {
+               close(fd);
+               vprintf("%s: lock file format error\n", Pname);
+               return(FALSE);
+       }
+       close(fd);
+       buf[len + 1] = '\0';
+       return(p_exists(atoi(buf)));
+}
+
+/*
+** Does the PID exist?
+** Send null signal to find out.
+*/
+
+p_exists(pid)
+int    pid;
+{
+       vprintf("%s: locking process %d is ", Pname, pid);
+       if (kill(pid, 0) < 0) {
+               switch(errno) {
+               case ESRCH:
+                       vprintf("dead\n");
+                       return(FALSE);  /* pid does not exist */
+               case EPERM:
+                       vprintf("alive\n");
+                       return(TRUE);   /* pid exists */
+               default:
+                       vprintf("state unknown: %s\n", errmsg(errno));
+                       return(TRUE);   /* be conservative */
+               }
+       }
+       vprintf("alive\n");
+       return(TRUE);   /* pid exists */
+}
+
+char *
+errmsg(n)
+register int   n;
+{
+       extern  int     sys_nerr;
+       extern  char    *sys_errlist[];
+
+       return((n >= 0 && n < sys_nerr) ? sys_errlist[n] : "unknown error");
+}
+
+char *
+tmpfile(file)
+char *file;
+{
+       register char   *cp;
+       static char     buf[BUFSIZ];
+       char    tempname[BUFSIZ];
+
+       if ((cp = rindex(strcpy(buf, file), '/')) != (char *)NULL) {
+               *(cp + 1) = '\0';
+       } else
+               buf[0] = '\0';
+       sprintf(tempname, "shlock%d", getpid());
+       vprintf("%s: temporary filename: %s\n", Pname, tempname);
+       return(strcat(buf, tempname));
+}