[comp.sources.unix] v14i054: Network News Transfer Protocol, version 1.5, Part08/09

rsalz@bbn.com (Rich Salz) (04/21/88)

Submitted-by: Phil Lapsley <phil@ucbvax.berkeley.edu>
Posting-number: Volume 14, Issue 54
Archive-name: nntp1.5/part08

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 9)."
# Contents:  ./support/nntp_awk.ucbvax ./xmit/nntpxmit.c
# Wrapped by rsalz@fig.bbn.com on Tue Apr 19 18:16:49 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f './support/nntp_awk.ucbvax' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./support/nntp_awk.ucbvax'\"
else
echo shar: Extracting \"'./support/nntp_awk.ucbvax'\" \(12577 characters\)
sed "s/^X//" >'./support/nntp_awk.ucbvax' <<'END_OF_FILE'
X# an awk script 
X# an NNTP log summary report generator
X#
X# NOTE: for systems that are not as yet using the new 4.3 BSD syslog
X# (and therefore have nntp messages lumped with everything else), it
X# would be best to invoke this script thusly:
X#
X#	egrep nntp syslog.old | awk -f nntp_awk > report_of_the_week
X#
X# because this script will include in the report all messages in the log
X# that it does not recognize (on the assumption that they are errors to
X# be dealt with by a human).
X#
X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
X# May 17, 1986 - Norwegian Independence Day
X#
X# Recognize some new things - February 22, 1987
X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
X#
X# fix "xmt is not an array" bug - March 11, 1987
X# Change Elapsed/CPU fields to break out time values, HH:MM:SS
X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
X#
X# Add reporting for newnews commands - August 27, 1987
X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
X#
X# Add nntpxmit connection attempt counting/reporting - December 7, 1987
X# Erik E. Fair <fair@ucbarpa.berkeley.edu>
X#
BEGIN{
X	readers = 0;
X	transmit = 0;
X	receive = 0;
X	polled = 0;
X}
X### Skip stderr reports from rnews
X{
X	n = split($6, path, "/");
X	if (path[n] == "rnews:") next;
X	n = split($7, path, "/");
X	if (path[n] == "rnews") next;
X	host = $6;
X}
X$7 == "group" {
X	readers = 1;
X	ng[$8]++;
X	next;
X}
X$7 == "ihave" {
X	receive = 1;
X	rec[host]++;
X	if ($9 == "accepted") {
X		rec_accept[host]++;
X		if ($10 == "failed") rec_failed[host]++;
X	} else if ($9 == "rejected") rec_refuse[host]++;
X	next;
X}
X# this is from version 1.4 of nntpd
X$7 == "ihave_stats" {
X	receive = 1;
X	rec[host] += $9 + $11 + $13;
X	rec_accept[host] += $9;
X	rec_refuse[host] += $11;
X	rec_failed[host] += $13;
X	next;
X}
X$7 == "connect" {
X	systems[host]++;
X	next;
X}
X# nntpxmit connection errors
X# Ooooh! I *wish* awk had N dimensional arrays,
X# so I wouldn't have to throw away the error message here!
X$7 == "hello:" {
X	conn[host]++;
X	if ($8 == "Connection" && $9 == "refused")
X		rmt_fail[host]++;
X	else
X		open_fail[host]++;
X	next;
X}
X# we'll get stats from this, don't count conn[]
X$7 == "xfer:" {
X	open_fail[host]++;
X# since these are expected to be few in number, we still print
X# the exact error (no "next;" statement here).
X}
X$7 == "greeted" {
X	conn[host]++;
X	rmt_fail[host]++;
X	next;
X}
X$7 == "host" && $8 == "unknown" {
X	conn[host]++;
X	ns_fail[host]++;
X	next;
X}
X# nntpd connection abort - all "broken pipe" right now
X$7 == "disconnect:" { next }
X# syslogd shit
X$7 == "repeated" { next }
X# inews shit
X$11 == "spooled" { next }
X$7 == "exit" {
X	if ($8 > 0) readers = 1;
X	articles[host] += $8;
X	groups[host] += $10;
X	next;
X}
X$7 == "xmit" {
X	xmt_cpu[host] += $9 + $11;
X	xmt_ela[host] += $13;
X	next;
X}
X$7 == "times" {
X	cpu[host] += $9 + $11;
X	ela[host] += $13;
X	next;
X}
X$7 == "stats" {
X	transmit = 1;
X	conn[host]++;
X	xmt[host] += $8;
X	xmt_accept[host] += $10;
X	xmt_refuse[host] += $12;
X	xmt_failed[host] += $14;
X	next;
X}
X#
X#	For the Nth time, I wish awk had two dimensional associative
X#	arrays. I assume that the last request is the same as all the
X#	others in this section of logfile.
X#
X$7 == "newnews" {
X	polled = 1;
X	poll[host] ++;
X	poll_asked[host] = $8;
X	next;
X}
X$7 == "newnews_stats" {
X	poll_offered[host] += $9;
X	poll_took[host] += $11;
X	next;
X}
X$7 == "post" {
X	readers = 1;
X	post[host]++;
X	next;
X}
X$7 == "timeout" {
X	timeout[host]++;
X	timeouts = 1;
X	next;
X}
X$7 == "unrecognized" {
X	unknown[host] = 1;
X	curious = 1;
X}
X### Print anything that we don't recognize in the report
X{
X	print;
X}
END{
X	printf("\n");
X###############################################################################
X### Article Exchange With Peers (other servers) Statistics                  ###
X###############################################################################
X	if (transmit || receive || polled)
X		printf("NNTP peer article transfers\n\n");
X
X	if (polled) for(s in poll) servers[s]++;
X	if (receive) for(s in rec) servers[s]++;
X	if (transmit) for(s in xmt) servers[s]++;
X
X	if (receive) {
X		printf("Article Reception (they contact us)\n");
X		printf("System                  Offered  Took  Toss  Fail Toss   Elapsed       CPU  Pct\n");
X		for(s in rec) {
X
X			nrec += rec[s];
X			nrec_accept += rec_accept[s];
X			nrec_refuse += rec_refuse[s];
X			nrec_failed += rec_failed[s];
X			nrec_cpu += cpu[s];
X			nrec_ela += ela[s];
X
X			they_offered = rec[s];
X			if (they_offered == 0) they_offered = 1;
X			we_toss = (rec_refuse[s] / they_offered) * 100 + 0.5;
X
X			e_hours      = ela[s] / 3600;
X			e_sec        = ela[s] % 3600;
X			e_min        = e_sec / 60;
X			e_sec        %= 60;
X
X			c_hours      = cpu[s] / 3600;
X			c_sec        = cpu[s] % 3600;
X			c_min        = c_sec / 60;
X			c_sec        %= 60;
X
X			tmp = ela[s];
X			if (tmp == 0) tmp = 1;
X			pct = ((cpu[s] / tmp) * 100.0 + 0.5);
X
X			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);
X		}
X
X		e_hours      = nrec_ela / 3600;
X		e_sec        = nrec_ela % 3600;
X		e_min        = e_sec / 60;
X		e_sec        %= 60;
X
X		c_hours      = nrec_cpu / 3600;
X		c_sec        = nrec_cpu % 3600;
X		c_min        = c_sec / 60;
X		c_sec        %= 60;
X
X		they_offered = nrec;
X		if (they_offered == 0) they_offered = 1;
X		we_toss = (nrec_refuse / they_offered) * 100 + 0.5;
X
X		if (nrec_ela == 0) nrec_ela = 1;
X		pct = ((nrec_cpu / nrec_ela) * 100.0 + 0.5);
X
X		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);
X	}
X
X###############################################################################
X	if (polled) {
X		printf("Article Transmission (they poll us)\n");
X		printf("System                     Conn Offrd  Took   Elapsed       CPU  Pct  Groups\n");
X		npoll = 0;
X		npoll_offered = 0;
X		npoll_took = 0;
X		npoll_cpu = 0;
X		npoll_ela = 0;
X
X		for(s in poll) {
X			npoll += poll[s];
X			npoll_offered += poll_offered[s];
X			npoll_took += poll_took[s];
X
X			if (rec[s]) {
X				printf("%-25s %5d %5d %5d  (see Article Reception)  %s\n", s, poll[s], poll_offered[s], poll_took[s], poll_asked[s]);
X			} else {
X				npoll_ela += ela[s];
X				npoll_cpu += cpu[s];
X
X				e_hours = ela[s] / 3600;
X				e_sec   = ela[s] % 3600;
X				e_min   = e_sec / 60;
X				e_sec   %= 60;
X
X				c_hours = cpu[s] / 3600;
X				c_sec   = cpu[s] % 3600;
X				c_min   = c_sec / 60;
X				c_sec   %= 60;
X
X				tmp = ela[s];
X				if (tmp == 0) tmp = 1;
X				pct = ((cpu[s] / tmp) * 100.0 + 0.5);
X
X				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]);
X			}
X		}
X		printf("\n%-25s %5d %5d %5d", "TOTALS", npoll, npoll_offered, npoll_took);
X		if (npoll_ela > 0 && npoll_cpu > 0) {
X
X			e_hours = npoll_ela / 3600;
X			e_sec   = npoll_ela % 3600;
X			e_min   = e_sec / 60;
X			e_sec   %= 60;
X
X			c_hours = npoll_cpu / 3600;
X			c_sec   = npoll_cpu % 3600;
X			c_min   = c_sec / 60;
X			c_sec   %= 60;
X
X			tmp = npoll_ela;
X			if (tmp == 0) tmp = 1;
X			pct = ((npoll_cpu / tmp) * 100.0 + 0.5);
X
X			printf(" %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct);
X		} else
X			printf("\n\n");
X	}
X
X###############################################################################
X	if (transmit) {
X		printf("Article Transmission (we contact them)\n");
X		printf("System                    Offrd  Took  Toss  Fail  Pct   Elapsed       CPU  Pct\n");
X		for(s in xmt) {
X			we_offered = xmt[s];
X			if (we_offered == 0) we_offered = 1;
X			they_toss = (xmt_refuse[s] / we_offered) * 100 + 0.5;
X
X			e_hours = xmt_ela[s] / 3600;
X			e_sec   = xmt_ela[s] % 3600;
X			e_min   = e_sec / 60;
X			e_sec   %= 60;
X
X			c_hours = xmt_cpu[s] / 3600;
X			c_sec   = xmt_cpu[s] % 3600;
X			c_min   = c_sec / 60;
X			c_sec   %= 60;
X
X			elapsed = xmt_ela[s];
X			if (elapsed == 0) elapsed = 1;
X			pct = ((xmt_cpu[s] / elapsed) * 100.0 + 0.5);
X
X			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);
X
X			nxmt        += xmt[s];
X			nxmt_accept += xmt_accept[s];
X			nxmt_refuse += xmt_refuse[s];
X			nxmt_failed += xmt_failed[s];
X			nxmt_ela    += xmt_ela[s];
X			nxmt_cpu    += xmt_cpu[s];
X		}
X
X		we_offered = nxmt;
X		if (we_offered == 0) we_offered = 1;
X		they_toss = (nxmt_refuse / we_offered) * 100 + 0.5;
X
X		e_hours = nxmt_ela / 3600;
X		e_sec   = nxmt_ela % 3600;
X		e_min   = e_sec / 60;
X		e_sec   %= 60;
X
X		c_hours = nxmt_cpu / 3600;
X		c_sec   = nxmt_cpu % 3600;
X		c_min   = c_sec / 60;
X		c_sec   %= 60;
X
X		if (nxmt_ela == 0) nxmt_ela = 1;
X		pct = ((nxmt_cpu / nxmt_ela) * 100.0 + 0.5);
X
X		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);
X
X		printf("Transmission Connection Attempts         ------errors-------\n");
X		printf("System                     Conn    OK    NS   Net   Rmt  Pct\n");
X		for(s in xmt) {
X			tot = conn[s];
X			if (tot == 0) tot = 1;
X			errs = rmt_fail[s] + ns_fail[s] + open_fail[s];
X			ok = (conn[s] - errs);
X			printf("%-25s %5d %5d %5d %5d %5d %3d%%\n", s, conn[s], ok, ns_fail[s], open_fail[s], rmt_fail[s], (100.0 * errs / tot + 0.5));
X			ct_tot += conn[s];
X			ct_ok  += ok;
X			ct_ns  += ns_fail[s];
X			ct_net += open_fail[s];
X			ct_rmt += rmt_fail[s];
X		}
X		tot = ct_tot;
X		if (tot == 0) tot = 1;
X		errs = ct_ns + ct_net + ct_rmt;
X		printf("\n%-25s %5d %5d %5d %5d %5d %3d%%\n\n", "TOTALS", ct_tot, ct_ok, ct_ns, ct_net, ct_rmt, (100.0 * errs / tot + 0.5));
X	}
X
X###############################################################################
X### Article Readership Statistics                                           ###
X###############################################################################
X
X	if (readers) {
X		printf("NNTP readership statistics\n");
X		printf("System                     Conn Articles Groups Post   Elapsed       CPU  Pct\n");
X		for(s in systems) {
X###
X### servers are different animals; they don't belong in this part of the report
X###
X			if (servers[s] > 0 && groups[s] == 0 && articles[s] == 0)
X				continue;
X###
X### report the curious server pokers elsewhere
X###
X			if (groups[s] == 0 && articles[s] == 0 && post[s] == 0) {
X				unknown[s] += systems[s];
X				curious = 1;
X				continue;
X			}
X
X			nconn += systems[s];
X			nart += articles[s];
X			ngrp += groups[s];
X			npost += post[s];
X			ncpu += cpu[s];
X			nela += ela[s];
X
X			e_hours      = ela[s] / 3600;
X			e_sec        = ela[s] % 3600;
X			e_min        = e_sec / 60;
X			e_sec        %= 60;
X
X			c_hours      = cpu[s] / 3600;
X			c_sec        = cpu[s] % 3600;
X			c_min        = c_sec / 60;
X			c_sec        %= 60;
X
X			elapsed = ela[s];
X			if (elapsed == 0) elapsed = 1;
X			pct = ((cpu[s] / elapsed) * 100 + 0.5);
X
X			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);
X		}
X
X		e_hours      = nela / 3600;
X		e_sec        = nela % 3600;
X		e_min        = e_sec / 60;
X		e_sec        %= 60;
X
X		c_hours      = ncpu / 3600;
X		c_sec        = ncpu % 3600;
X		c_min        = c_sec / 60;
X		c_sec        %= 60;
X
X		if (nela == 0) nela = 1;
X		pct = ((ncpu / nela) * 100 + 0.5);
X
X		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);
X	}
X
X###############################################################################
X	if (curious) {
X		printf("Unknown NNTP server explorers\nSystem                     Conn\n");
X		for(s in unknown) {
X			printf("%-25s %5d\n", s, unknown[s]);
X		}
X		printf("\n");
X	}
X###############################################################################
X	if (timeouts) {
X		printf("Server timeouts\n");
X		for(s in timeout) {
X			printf("%-25s %5d\n", s, timeout[s]);
X		}
X		printf("\n");
X	}
X###############################################################################
X	if (readers) {
X		for(g in ng) {
X			x = length(g);
X			if (x > max) max = x;
X
X			i = index(g, ".");
X			if (i > 0) top = substr(g, 1, i - 1);
X			else top = g;
X			category[top] += ng[g];
X		}
X		fmt = sprintf("%%-%ds %%5d\n", max);
X
X		printf("Newsgroup Request Counts (by category)\n");
X		for(g in category) printf(fmt, g, category[g]);
X
X		printf("\nNewsgroup Request Counts (by newsgroup)\n");
X		for(g in ng) printf(fmt, g, ng[g]);
X		printf("\n");
X	}
X}
END_OF_FILE
if test 12577 -ne `wc -c <'./support/nntp_awk.ucbvax'`; then
    echo shar: \"'./support/nntp_awk.ucbvax'\" unpacked with wrong size!
fi
# end of './support/nntp_awk.ucbvax'
fi
if test -f './xmit/nntpxmit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./xmit/nntpxmit.c'\"
else
echo shar: Extracting \"'./xmit/nntpxmit.c'\" \(26902 characters\)
sed "s/^X//" >'./xmit/nntpxmit.c' <<'END_OF_FILE'
X/* nntpxmit - transmit netnews articles across the internet with nntp
X**
X** This program is for transmitting netnews articles between sites
X** that offer the NNTP service, internet style. There are two forms
X** of article transmission that can be used in this environment, since
X** the communication is interactive (and relatively more immediate,
X** when compared to batched file transfer protocols, like UUCP). They
X** are: active send (I have `x', do you want it?) and polling (what
X** have you gotten lately?).
X**
X** 		A C T I V E   S E N D
X**
X** Sites on the UUCP network generally use active send, without asking
X** in advance (that is, unless you got an article from your neighbor,
X** or their site is listed in the Path: header already, you assume
X** they don't have it and send it along). There is an ihave/sendme
X** protocol for doing active send over batched links, but I claim that
X** it won't work well because of the high latency between queueing
X** and actual transfer that UUCP links typically have. That is, you'll
X** still end up with a high rate of duplicate articles being sent over
X** that type of link.
X**
X** With NNTP-based IHAVE, the update window in which another site can
X** give the remote the article you just offered him is the time between
X** the remote telling you it doesn't have the article, and your
X** completed transfer of the article (pretty small). In practice, we
X** still get duplicates, but generally from two problems: synchronized
X** transmission of an article from two different neighbors (this can
X** only be fixed by putting inews(1) into nntpd), and by articles
X** being accepting during an expire(1) run (expire locks out inews
X** processing while it is running, and articles collect until expire
X** is done; since accepted article message-ids aren't added to
X** the history file until expire is done, several clients can offer
X** you the same article, and you'll accept all the copies offered you.
X** When rnews gets run after expire, it will reject the duplicates).
X**
X** 		P O L L I N G
X**
X** Polling presents some article and distribution security problems,
X** because the server has no contol over what a transmission client
X** will ask for, and it must therefore control what it tells a client
X** in response to a query.
X**
X** Articles that appear in local newsgroup hierarchies, or appear in
X** the generally distributed USENET newsgroups with local distributions
X** have to be filtered out from the list of message-IDs that the server
X** gives to a client in response to a NEWNEWS query, or filtered when
X** the server fetches the articles off the disk in response to an
X** ARTICLE command (and therefore has complete access to the required
X** information). Otherwise, distributions will leak.
X**
X** The other problem with polling is that a good client should keep track
X** of when it last successfully polled a server, so that it doesn't force
X** h server to dump its entire history file across the network, and this
X** involves more file locking and manipulations routines.
X**
X** nntpxmit only implements active send, for now.
X**
X** Erik E. Fair <fair@ucbarpa.berkeley.edu>, Dec 4, 1987
X*/
X
X#include "nntpxmit.h"
X#include <stdio.h>
X#include <errno.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#ifdef	BSD4_2
X#include <sys/resource.h>
X#else
X#include <sys/times.h>
extern	time_t	time();
X#endif	BSD4_2
X#include <sys/file.h>
X#include <fcntl.h>
X#include <signal.h>
X#ifdef USG
X#include "sysexits.h"
X#else
X#include <sysexits.h>
X#endif
X#ifdef	SYSLOG
X#include <syslog.h>
X#endif	SYSLOG
X#include "nntp.h"
X#include "llist.h"
X
X#define	MAXFNAME	BUFSIZ	/* maximum filename size - big enough? */
X#define	FCLOSE(fp)	(void) fclose(fp); (fp) = (FILE *)NULL
X
FILE	*getfp();
char	*errmsg();
void	requeue();
void	catchsig();
void	restsig();
void	logstats();
void	log();
int	interrupted();
X
X/*
X** Globals that certain things need.
X**
X** Various subroutines want the program name to report errors.
X** The queue file, queue file pointer and current article name are
X** there to write out the state of the queue file from a signal handler
X** (that is, the list of unsent and (possibly) failed articles) so
X** that when next we try sending to a given remote site, we don't send
X** stuff we've already sent.
X*/
char	*Pname;			/* this program's invocation name */
char	*Host;			/* current remote host */
char	*Qfile;			/* current queue file we're operating on */
FILE	*Qfp;			/* the (FILE *) for above */
char	Article[MAXFNAME];	/* current article filename */
X
X/*
X** Some flags, toggled by arguments
X*/
X#define	TOGGLE(boolean)	(boolean) = !(boolean)
char	Debug = FALSE;
char	Report_Stats = TRUE;
char	ReQueue_Fails = TRUE;
X
char	*USAGE = "USAGE: nntpxmit [-d][-s][-r][-T][-F][-D] hostname|hostname:file [...]";
char	*Fmt = "%s: %s\n";
char	*E_fopen = "fopen(%s, \"%s\"): %s";
char	*E_unlk = "unlink(%s): %s";
X#ifdef	USELOG
char	*NNTPlog = USELOG;	/* yet another external log file */
FILE	*Logfp = (FILE *)NULL;
X#endif	USELOG
X
ll_t	FailedArticles;		/* list of failed articles */
X
struct {
X	u_long	offered;
X	u_long	accepted;
X	u_long	rejected;
X	u_long	failed;
X} Stats = {0L, 0L, 0L, 0L};
X
double Tbegin, Tend;		/* transfer timestamps */
X
extern	int	errno;
extern 	int	strncmp();
extern	char	*rindex();
extern	char	*index();
extern	char	*mktemp();
extern	char	*strcpy();
X
X#ifdef	USG
void
bzero(s, l)
register caddr_t s;
register int	l;
X{
X	while(l-- > 0) *s++ = 0;
X}
X#endif	USG
X
main(ac, av)
int	ac;
char	*av[];
X{
X	register int	i;
X	int	transport = T_IP_TCP;	/* default is IP/TCP */
X	int	isQfile = TRUE;		/* file arg is a Queue file */
X#ifdef	USELOG
X	char	*amode = "a";
X#endif	USELOG
X#ifdef	BSD4_2
X	struct timeval tod;
X	struct timezone tz;
X
X	(void) gettimeofday(&tod, &tz);
X	Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;
X#else
X	Tbegin = (double) time((time_t *)NULL);
X#endif	BSD4_2
X
X	Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]);
X	
X	if (ac < 2) {
X		fprintf(stderr, Fmt, Pname, USAGE);
X		exit(EX_USAGE);
X	}
X
X#ifdef	SYSLOG
X	/* 4.2 BSD openlog has only two args */
X#ifdef	LOG_LOCAL7
X	(void) openlog(Pname, LOG_PID, LOG_LOCAL7);
X#else
X	(void) openlog(Pname, LOG_PID);
X#endif	LOG_LOCAL_7
X#endif	SYSLOG
X#ifdef	USELOG
X	if ((Logfp = fopen(NNTPlog, amode)) == (FILE *)NULL) {
X		char	buf[BUFSIZ];
X
X		sprintf(buf, E_fopen, NNTPlog, amode, errmsg(errno));
X		log(L_NOTICE, buf);
X	}
X#endif	USELOG
X
X	for(i = 1; i < ac; i++) {
X		if (av[i][0] == '-') {
X			switch(av[i][1]) {
X			case 'T':
X				transport = T_IP_TCP;
X				break;
X			case 'D':
X				transport = T_DECNET;
X				break;
X			case 'F':
X				transport = T_FD;
X				break;
X			case 's':
X				TOGGLE(Report_Stats);
X				break;
X			case 'd':
X				TOGGLE(Debug);
X				break;
X			case 'r':
X				TOGGLE(ReQueue_Fails);
X				break;
X			case 'a':
X				isQfile = FALSE;
X				break;
X			default:
X				fprintf(stderr, "%s: no such option: -%c\n",
X					Pname, av[i][1]);
X				fprintf(stderr, Fmt, Pname, USAGE);
X				exit(EX_USAGE);
X			}
X			continue;
X		}
X
X		/*
X		** OK, it wasn't an option, therefore it must be a
X		** hostname, filename pair.
X		**
X		** If the user typed host::file, then it's DECNET,
X		** whether they remembered the "-D" option or not.
X		*/
X		Host = av[i];
X		if ((Qfile = index(Host, ':')) != (char *)NULL) {
X			if (Qfile[1] == ':') {
X				transport = T_DECNET;
X				*Qfile++ = '\0';
X			} else if (transport != T_FD)
X				transport = T_IP_TCP;
X			*Qfile++ = '\0';
X		} else
X			Qfile = Host;
X
X		bzero((caddr_t)&Stats, sizeof(Stats));
X		if (isQfile) {
X			if (sendnews(Host, transport, Qfile, isQfile) && Report_Stats) {
X				logstats();
X			}
X		} else {
X			/* one-shot */
X			(void) strcpy(Article, Qfile);
X			exit(sendnews(Host, transport, Qfile, isQfile) ? EX_OK : EX_TEMPFAIL);
X		}
X	}
X	exit(EX_OK);
X}
X
X/*
X** Calculate how much time we've used,
X** and report that (and the transfer statistics).
X**
X*/
void
logstats()
X{
X	static double ouser = 0.0, osys = 0.0;
X	double user, sys;
X	char buf[BUFSIZ];
X#ifdef	USELOG
X#ifdef	BSD4_2
X	extern	time_t	time();
X#endif	BSD4_2
X	time_t	tstamp;
X	char	*tp;
X	extern	char	*ctime();
X#endif	USELOG
X#ifdef	BSD4_2
X	struct rusage self, kids;
X	struct timeval tod;
X	struct timezone tzdummy;
X
X	(void) getrusage(RUSAGE_SELF, &self);
X	(void) getrusage(RUSAGE_CHILDREN, &kids);
X	(void) gettimeofday(&tod, &tzdummy);
X
X	Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;
X
X	user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
X		(double) self.ru_utime.tv_usec/1000000. +
X		(double) kids.ru_utime.tv_usec/1000000.;
X	
X	sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
X		(double) self.ru_stime.tv_usec/1000000. +
X		(double) kids.ru_stime.tv_usec/1000000.;
X#else
X#define	HZ	60.0	/* typical system clock ticks - param.h */
X	struct tms	cpu;
X
X	(void) times(&cpu);
X
X	Tend = (double) time((time_t *)NULL);
X	user = (double)(cpu.tms_utime + cpu.tms_cutime) / HZ;
X	sys  = (double)(cpu.tms_stime + cpu.tms_cstime) / HZ;
X#endif	BSD4_2
X	sprintf(buf,
X		"%s stats %lu offered %lu accepted %lu rejected %lu failed",
X		Host, Stats.offered, Stats.accepted, Stats.rejected,
X		Stats.failed);
X	log(L_INFO, buf);
X#ifdef 	USELOG
X	if (Logfp != (FILE *)NULL) {
X		(void) time(&tstamp);
X		tp = ctime(&tstamp);
X		tp[19] = '\0';
X		fprintf(Logfp, Fmt, &tp[4], buf);
X	}
X#endif	USELOG
X
X	sprintf(buf, "%s xmit user %.1f system %.1f elapsed %.1f",
X		Host, (user - ouser), (sys - osys), (Tend - Tbegin));
X	log(L_INFO, buf);
X#ifdef 	USELOG
X	if (Logfp != (FILE *)NULL) {
X		fprintf(Logfp, Fmt, &tp[4], buf);
X		(void) fflush(Logfp);
X	}
X#endif	USELOG
X
X	/* reset reference point */
X	Tbegin = Tend;	
X	ouser = user;
X	osys = sys;
X}
X
X/*
X** Given a hostname to connect to, and a file of filenames (which contain
X** netnews articles), send those articles to the named host using NNTP.
X**
X** Return code behavior is different depending upon isQfile.
X**
X**	TRUE	- return TRUE if we contacted the remote and started
X**		  transferring news - this is to decide whether to
X**		  record CPU and transfer statistics.
X**
X**	FALSE	- a one-shot file transfer - return TRUE or FALSE depending
X**		  upon whether we successfully transferred the one article.
X*/
sendnews(host, transport, file, isQfile)
char	*host, *file;
int	transport, isQfile;
X{
X	register FILE	*fp;
X#ifdef	FTRUNCATE
X	char	*mode = "r+";		/* so we can use ftruncate() */
X#else
X	char	*mode = "r";
X#endif	FTRUNCATE
X
X	if ((Qfp = fopen(file, mode)) == (FILE *)NULL) {
X		char	buf[BUFSIZ];
X
X		sprintf(buf, E_fopen, file, mode, errmsg(errno));
X		log(L_WARNING, buf);
X		return(FALSE);
X	}
X
X	/*
X	** interlock with other copies of this process.
X	** non-blocking.
X	*/
X	if (isQfile) {
X		if (!lockfd(fileno(Qfp), file, DONT_BLOCK)) {
X			FCLOSE(Qfp);
X			return(FALSE);
X		}
X	}
X
X	/*
X	** Open a connection to the remote server
X	*/
X	if (hello(host, transport) == FAIL) {
X		FCLOSE(Qfp);
X		return(FALSE);
X	}
X
X	if (isQfile) {
X		/*
X		** We're sending a batch queue:
X		**	open article
X		**	get message-ID
X		**	send "IHAVE <message-ID>" to remote
X		**	read their reply
X		**	send article if appropriate
X		**	iterate to end of queue file
X		*/
X		catchsig(interrupted);
X
X		while((fp = getfp(Qfp, Article, sizeof(Article))) != (FILE *)NULL) {
X			if (!sendarticle(host, fp)) {
X				(void) fclose(fp);
X				requeue(Article);
X				Article[0] = '\0';
X				cleanup();
X				goodbye(DONT_WAIT);
X				restsig();
X				return(TRUE);
X			}
X			(void) fclose(fp);
X		}
X
X		cleanup();
X		goodbye(WAIT);
X		restsig();
X		return(TRUE);
X	} else {
X		/*
X		** Qfp is a netnews article - this is a one-shot
X		** operation, exit code dependent upon remote's
X		** acceptance of the article
X		*/
X		register int	retcode;
X
X		retcode = sendarticle(host, Qfp);
X		FCLOSE(Qfp);
X		goodbye(retcode ? WAIT : DONT_WAIT);
X		return(retcode && Stats.accepted == 1 && Stats.failed == 0);
X	}
X}
X
X/*
X** Perform one transfer operation:
X**	Give IHAVE command
X**	Wait for reply, and send article if they ask for it
X**	Wait for transfer confirmation, and requeue the article
X**		if they drop it.
X**	Watch all network I/O for errors, return FALSE if
X**		the connection fails and we have to cleanup.
X*/
sendarticle(host, fp)
char	*host;
FILE	*fp;
X{
X	register int	code;
X	char	buf[BUFSIZ];
X	char	*e_xfer = "%s xfer: %s";
X
X	switch(code = ihave(fp)) {
X	case CONT_XFER:
X		/*
X		** They want it. Give it to 'em.
X		*/
X		if (!sendfile(fp)) {
X			sprintf(buf, e_xfer, host, errmsg(errno));
X			log(L_NOTICE, buf);
X			Stats.failed++;
X			return(FALSE);
X		}
X		/*
X		** Did the article transfer OK?
X		** Stay tuned to this same socket to find out!
X		*/
X		if ((code = readreply(buf, sizeof(buf))) != OK_XFERED) {
X			Stats.failed++;
X			if (code < 0) {
X				if (errno > 0) {
X					sprintf(buf, e_xfer, host, errmsg(errno));
X					log(L_NOTICE, buf);
X				} else {
X					char errbuf[BUFSIZ];
X
X					sprintf(errbuf, e_xfer, host, buf);
X					log(L_NOTICE, errbuf);
X				}
X				return(FALSE);
X			}
X			if (ReQueue_Fails && code != ERR_XFERRJCT) {
X				requeue(Article);
X				Article[0] = '\0';
X			}
X		}
X		break;
X	case ERR_GOTIT:
X		/* they don't want it */
X		break;
X	default:
X		if (code < 0) {
X			if (errno > 0) {
X				sprintf(buf, e_xfer, host, errmsg(errno));
X				log(L_NOTICE, buf);
X			} else {
X				sprintf(buf, e_xfer, host, "ihave");
X				log(L_NOTICE, buf);
X			}
X		} else {
X			sprintf(buf, "%s improper response to IHAVE: %d while offering %s", host, code, Article);
X			log(L_WARNING, buf);
X		}
X		return(FALSE);
X	}
X	return(TRUE);
X}
X
char *
errmsg(code)
int code;
X{
X	extern int sys_nerr;
X	extern char *sys_errlist[];
X	static char ebuf[6+5+1];
X
X	if (code > sys_nerr || code < 0) {
X		(void) sprintf(ebuf, "Error %d", code);
X		return ebuf;
X	} else
X		return sys_errlist[code];
X}
X
X/*
X** strip leading and trailing spaces
X*/
char *
sp_strip(s)
register char	*s;
X{
X	register char	*cp;
X
X	if (s == NULL)
X		return(NULL);
X
X	if (*s == '\0')
X		return(s);
X	
X	cp = &s[strlen(s) - 1];
X	while(cp > s && isspace(*cp))
X		cp--;
X
X	*++cp = '\0';	/* zap trailing spaces */
X
X	for(cp = s; *cp && isspace(*cp); cp++)
X		continue;
X
X	return(cp);	/* return pointer to first non-space */
X}
X
X/*
X** convert `s' to lower case
X*/
char *
lcase(s)
register char	*s;
X{
X	register char	*cp;
X
X	if (s == (char *)NULL)
X		return(s);
X
X	for(cp = s; *cp != '\0'; cp++)
X		if (isupper(*cp))
X			*cp = tolower(*cp);
X	return(s);
X}
X
X/*
X** Get the message-id header field data with a minimum of fuss.
X*/
char *
getmsgid(fp)
FILE *fp;
X{
X	static	char	buf[BUFSIZ];
X	static	char	*msgid = "message-id";
X	register char	*cp, *cp2;
X
X	while(fgets(buf, sizeof(buf), fp) != (char *)NULL) {
X		switch(buf[0]) {
X		case '\n':
X			return((char *)NULL);	/* EOH, we failed */
X		case 'M':
X		case 'm':
X			if ((cp = index(buf, ':')) == (char *)NULL)
X				continue;
X			*cp++ = '\0';
X			if (strncmp(lcase(buf), msgid, sizeof(*msgid)) == 0) {
X				/* dump extraneous trash - umass.bitnet */
X				/* hope nobody quotes an '>' in a msgid */
X				if ((cp2 = index(cp, '>')) != (char *)NULL)
X					*++cp2 = '\0';
X				return(sp_strip(cp));
X			}
X			break;
X		}
X	}
X	return((char *)NULL);	/* EOF, we failed */
X}
X
X#ifdef	notdef	/* nobody obeys the triply damned protocol anyway! */
X/*
X** Special characters, see RFC822, appendix D.
X*/
isspecial(c)
char	c;
X{
X	char	*specials = "()<>@,;:\\\".[]";
X
X	return(index(specials, c) != (char *)NULL ? TRUE : FALSE);
X}
X
X/*
X** Check on the validity of an RFC822 message-id
X**
X** By The Book, RFC822 Appendix D.
X**	msg-id		= "<" addr-spec ">"
X**	addr-spec	= local-part "@" domain
X**	local-part	= word *("." word)
X**	word		= atom / quoted-string
X**	domain 		= sub-domain *("." sub-domain)
X**	sub-domain	= domain-ref / domain-literal
X**	domain-ref	= atom
X**	domain-literal	= "[" *(dtext / quoted-pair) "]"
X**
X** NOTE: close reading of the RFC822 spec indicates that a fully
X**	qualified domain name (i.e. one with at least one dot) is
X**	NOT required in the domain part of the addr-spec. However,
X**	I've decided to be an asshole and require them, since we'll 
X**	all die a slow death later on if I don't at this juncture.
X**	To disable, if you disagree with me, see the last return
X**	statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu>
X**	May 30, 1986
X*/
msgid_ok(id)
register char	*id;
X{
X	register Langle = FALSE;
X	register Rangle = FALSE;
X	register local_part = FALSE;
X	register at = FALSE;
X	register dot = FALSE;
X
X	/* skip up to the opening angle bracket */
X	if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL)
X		return(FALSE);		/* don't waste my time! */
X
X	for(; *id != '\0'; id++) {
X		switch(*id) {
X		case '<':
X			if (Langle) return(FALSE);
X			Langle = local_part = TRUE;
X			break;
X		case '>':
X			if (Rangle || !Langle || !at) return(FALSE);
X			else Rangle = TRUE;
X			break;
X		case '@':		/* should be a domain spec */
X			at = TRUE;
X			local_part = FALSE;
X			break;
X		case '.':
X			dot = at;
X			break;
X		case '\\':
X			/*
X			** quoted pair; this disallows NULs, but how
X			** many mailers would die if someone used one?
X			*/
X			if (!local_part || (*++id) == '\0') return(FALSE);
X			break;
X		case '"':
X			/*
X			** quoted string
X			*/
X			if (!local_part) return(FALSE);
X			do {
X				switch(*++id) {
X				case '\\':
X					if ((*++id) == '\0') return(FALSE);
X					break;
X				case '\r':
X					return(FALSE);
X				}
X			} while(*id != '\0' && *id != '"');
X			break;
X		case '[':
X			/*
X			** domain literal
X			*/
X			if (local_part) return(FALSE);
X			do {
X				switch(*++id) {
X				case '\\':
X					if ((*++id) == '\0') return(FALSE);
X					break;
X				case '\r':
X					return(FALSE);
X				}
X			} while(*id != '\0' && *id != ']');
X			break;
X		default:
X			if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id))
X				return(FALSE);	/* quit immediately */
X			break;
X		}
X	}
X	return(at && dot && Langle && Rangle);
X}
X#else notdef
X
X/*
X** Simpleton's check for message ID syntax.
X** A concession to the realities of the ARPA Internet.
X*/
msgid_ok(s)
register char *s;
X{
X	register char	c;
X	register in_msgid = FALSE;
X
X	if (s == (char *)NULL)
X		return(FALSE);
X
X	while((c = *s++) != '\0') {
X		if (!isascii(c) || iscntrl(c) || isspace(c))
X			return(FALSE);
X		switch(c) {
X		case '<':
X			in_msgid = TRUE;
X			break;
X		case '>':
X			return(in_msgid);
X		}
X	}
X	return(FALSE);
X}
X#endif	notdef
X
X/*
X** Read the header of a netnews article, snatch the message-id therefrom,
X** and ask the remote if they have that one already.
X*/
ihave(fp)
FILE	*fp;
X{
X	register int	code;
X	register char	*id;
X	char	buf[BUFSIZ];
X
X	if ((id = getmsgid(fp)) == (char *)NULL || *id == '\0') {
X		/*
X		** something botched locally with the article
X		** so we don't send it, but we don't break off
X		** communications with the remote either.
X		*/
X		sprintf(buf, "%s: message-id missing!", Article);
X		log(L_DEBUG, buf);
X		return(ERR_GOTIT);
X	}
X
X	if (!msgid_ok(id)) {
X		sprintf(buf, "%s: message-id syntax error: %s", Article, id);
X		log(L_DEBUG, buf);
X		return(ERR_GOTIT);
X	}
X
X	sprintf(buf, "IHAVE %s", id);
X	Stats.offered++;
X
X	switch(code = converse(buf, sizeof(buf))) {
X	case CONT_XFER:
X		Stats.accepted++;
X		rewind(fp);
X		return(code);
X	case ERR_GOTIT:
X		Stats.rejected++;
X		return(code);
X	default:
X		return(code);
X	}
X}
X
X/*
X** Given that fp points to an open file containing filenames,
X** open and return a file pointer to the next filename in the file.
X** Don't you love indirection?
X**
X** Returns a valid FILE pointer or NULL if end of file.
X*/
FILE *
getfp(fp, filename, fnlen)
register FILE	*fp;
char	*filename;
register int	fnlen;
X{
X	register FILE	*newfp = (FILE *)NULL;
X	register char	*cp;
X	char	*mode = "r";
X
X	while(newfp == (FILE *)NULL) {
X		if (fgets(filename, fnlen, fp) == (char *)NULL)
X			return((FILE *)NULL);		/* EOF, tell caller */
X
X		filename[fnlen - 1] = '\0';	/* make sure */
X
X		/* if fgets() ever forgets the '\n', we're fucked */
X		if (*(cp = &filename[strlen(filename) - 1]) == '\n')
X			*cp = '\0';
X
X		if (filename[0] == '\0')
X			continue;
X
X		if ((newfp = fopen(filename, mode)) == (FILE *)NULL) {
X			/*
X			** The only permissible error is `file non-existant'
X			** anything else indicates something is seriously
X			** wrong, and we should go away to let the shell
X			** script clean up.
X			*/
X			if (errno != ENOENT) {
X				char	buf[BUFSIZ];
X
X				sprintf(buf, E_fopen, filename, mode, errmsg(errno));
X				log(L_WARNING, buf);
X				goodbye(DONT_WAIT);
X				exit(EX_OSERR);
X			}
X		}
X	}
X	return(newfp);
X}
X
X/*
X** OK, clean up any mess and requeue failed articles
X*/
cleanup()
X{
X	dprintf(stderr, "%s: cleanup()\n", Pname);
X	if (Qfp == (FILE *)NULL || Qfile == (char *)NULL)
X		return;
X
X	if ((ReQueue_Fails && Stats.failed > 0) || !feof(Qfp)) {
X		rewrite();
X	} else {
X		/*
X		** Nothing to clean up after, reset stuff and
X		** nuke the queue file.
X		*/
X		requeue((char *)NULL);
X		if (feof(Qfp)) {
X			dprintf(stderr, "%s: unlink(%s)\n", Pname, Qfile);
X			if (unlink(Qfile) < 0) {
X				char	buf[BUFSIZ];
X
X				sprintf(buf, E_unlk, Qfile, errmsg(errno));
X				log(L_WARNING, buf);
X			}
X		}
X		FCLOSE(Qfp);
X	}
X}
X 
X/*
X** Add an article file name to an allocated linked list,
X** so that we can rewrite it back to the queue file later.
X** Calling this with a NULL pointer resets the internal pointer.
X*/
void
requeue(article)
char *article;
X{
X	static ll_t *lp = &FailedArticles;
X
X	if (article == (char *)NULL) {
X		dprintf(stderr, "%s: requeue(): reset\n", Pname);
X		goto reset;		/* this is for our static pointer */
X	}
X
X	if (*article == '\0')
X		return;
X
X	dprintf(stderr, "%s: requeue(%s)\n", Pname, article);
X	if ((lp = l_alloc(lp, article, strlen(article) + 1)) == (ll_t *)NULL) {
X		fprintf(stderr, "%s: requeue(%s) failed, dumping fail list\n",
X			Pname, article);
X		/*
X		** Wow! Did you know that this could blow the stack
X		** if we recurse too deeply? I sure didn't!
X		*/
reset:
X		l_free(&FailedArticles);
X		lp = &FailedArticles;
X	}
X}
X
X/*
X** Note that if I'm not running as "news" or "usenet" (or whatever
X** account is supposed to own netnews), the resultant file will be the
X** wrong ownership, permissions, etc.
X*/
rewrite()
X{
X	register ll_t	*lp;
X	register FILE	*tmpfp;
X	register int	nart = 0;
X	char	*mode = "w+";
X	char	*template = "/tmp/nntpxmitXXXXXX";
X	char	buf[BUFSIZ];
X	static char	*tempfile = (char *)NULL;
X
X	dprintf(stderr, "%s: rewrite(%s)\n", Pname, Qfile);
X
X	if (tempfile == (char *)NULL)		/* should only need this once */
X		tempfile = mktemp(template);
X
X	if ((tmpfp = fopen(tempfile, mode)) == (FILE *)NULL) {
X		sprintf(buf, E_fopen, tempfile, mode, errmsg(errno));
X		log(L_WARNING, buf);
X		FCLOSE(Qfp);
X		return;
X	}
X
X	/*
X	** Requeue the rest of the queue file first,
X	** so that failed articles (if any) go to the end
X	** of the new file.
X	*/
X	if (!feof(Qfp)) {
X		dprintf(stderr, "%s: copying the unused portion of %s to %s\n",
X			Pname, Qfile, tempfile);
X		while(fgets(buf, sizeof(buf), Qfp) != (char *)NULL)
X			(void) fputs(buf, tmpfp);
X	}
X
X	/*
X	** Here we write out the filenames of articles which
X	** failed at the remote end.
X	*/
X	dprintf(stderr, "%s: writing failed article filenames to %s\n",
X		Pname, tempfile);
X	L_LOOP(lp, FailedArticles) {
X		fprintf(tmpfp, "%s\n", lp->l_item);
X		nart++;
X	}
X	dprintf(stderr, "%s: wrote %d article filenames to %s\n",
X		Pname, nart, tempfile);
X
X	(void) fflush(tmpfp);
X	/*
X	** If writing the temp file failed (maybe /tmp is full?)
X	** back out and leave the queue file exactly as it is.
X	*/
X	if (ferror(tmpfp)) {
X		sprintf(buf, "rewrite(): copy to %s failed", tempfile);
X		log(L_WARNING, buf);
X		(void) fclose(tmpfp);
X		FCLOSE(Qfp);
X		if (unlink(tempfile) < 0) {
X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
X			log(L_WARNING, buf);
X		}
X		requeue((char *)NULL);		/* reset */
X		return;
X	}
X
X	rewind(tmpfp);
X#ifdef	FTRUNCATE
X	rewind(Qfp);
X	if (ftruncate(fileno(Qfp), (off_t)0) < 0) {
X		sprintf(buf, "ftruncate(%s, 0): %s", Qfile, errmsg(errno));
X		log(L_WARNING, buf);
X		FCLOSE(Qfp);
X		(void) fclose(tmpfp);
X		if (unlink(tempfile) < 0) {
X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
X			log(L_WARNING, buf);
X		}
X		requeue((char *)NULL);		/* reset */
X		return;
X	}
X#else
X	FCLOSE(Qfp);	/* we just nuked our lock here (lockfd) */
X	if ((Qfp = fopen(Qfile, mode)) == (FILE *)NULL) {
X		sprintf(buf, E_fopen, Qfile, mode, errmsg(errno));
X		log(L_WARNING, buf);
X		(void) fclose(tmpfp);
X		if (unlink(tempfile) < 0) {
X			sprintf(buf, E_unlk, tempfile, errmsg(errno));
X			log(L_WARNING, buf);
X		}
X		requeue((char *)NULL);		/* reset */
X		return;
X	}
X	/* Try to get our lock back (but continue whether we do or not) */
X	(void) lockfd(fileno(Qfp), Qfile, DONT_BLOCK);
X#endif	FTRUNCATE
X
X	dprintf(stderr, "%s: copying %s back to %s\n", Pname, tempfile, Qfile);
X	while(fgets(buf, sizeof(buf), tmpfp) != (char *)NULL)
X		(void) fputs(buf, Qfp);
X
X	(void) fflush(Qfp);
X	if (ferror(Qfp)) {
X		sprintf(buf, "rewrite(): copy to %s failed", Qfile);
X		log(L_WARNING, buf);
X	}
X	(void) fclose(tmpfp);
X	FCLOSE(Qfp);
X	if (unlink(tempfile) < 0) {
X		sprintf(buf, E_unlk, tempfile, errmsg(errno));
X		log(L_WARNING, buf);
X	}
X	requeue((char *)NULL);		/* reset */
X	dprintf(stderr, "%s: rewrite(%s): done\n", Pname, Qfile);
X	return;
X}
X
X/*
X** Signal stuff
X**
X** There's probably too much stuff to do in this signal
X** handler, but we're going to exit anyway...
X*/
interrupted(sig)
int	sig;
X{
X	char buf[BUFSIZ];
X
X#ifndef RELSIG
X	catchsig(SIG_IGN);	/* for System V - hope we're quick enough */
X#endif	RELSIG
X	sprintf(buf, "%s signal %d", Host, sig);
X	log(L_NOTICE, buf);
X	requeue(Article);
X	cleanup();
X	if (Report_Stats)
X		logstats();
X	goodbye(DONT_WAIT);
X	exit(EX_TEMPFAIL);
X}
X
struct {
X	int	signo;
X	ifunp	state;
X} SigList[] = {
X	{SIGHUP},
X	{SIGINT},
X	{SIGQUIT},
X	{SIGTERM},
X	{NULL}
X};
X
void
catchsig(handler)
ifunp	handler;
X{
X	register int	i;
X
X	if (handler != SIG_IGN) {
X		for(i = 0; SigList[i].signo != NULL; i++) {
X			SigList[i].state = signal(SigList[i].signo, handler);
X		}
X	} else {
X		for(i = 0; SigList[i].signo != NULL; i++) {
X			(void) signal(SigList[i].signo, handler);
X		}
X	}
X}
X
void
restsig()
X{
X	register int	i;
X
X	for(i = 0; SigList[i].signo != NULL; i++) {
X		if (SigList[i].state != (ifunp)(-1))
X			(void) signal(SigList[i].signo, SigList[i].state);
X	}
X}
X
X/*
X** log stuff
X*/
void
log(importance, error)
int	importance;
char	*error;
X{
X	FILE	*report = (importance == L_INFO ? stdout : stderr);
X
X	fprintf(report, Fmt, Pname, error);
X#ifdef	SYSLOG
X	switch(importance) {
X	case L_INFO:	importance = LOG_INFO;		break;
X	case L_DEBUG:	importance = LOG_DEBUG;		break;
X	case L_NOTICE:	importance = LOG_NOTICE;	break;
X	case L_WARNING:	importance = LOG_WARNING;	break;
X	default:	importance = LOG_DEBUG;		break;
X	}
X	syslog(importance, error);
X#endif	SYSLOG
X}
X
X/*
X** Lock a file descriptor
X**
X** NOTE: if the appropriate system calls are unavailable,
X** this subroutine is a no-op.
X*/
lockfd(fd, file, non_blocking)
int	fd, non_blocking;
char	*file;			/* just for error reporting */
X{
X	char	buf[BUFSIZ];
X#ifdef	USG
X#ifdef	F_TLOCK
X	if (lockf(fd, (non_blocking ? F_TLOCK : F_LOCK), 0) < 0) {
X		if (errno != EACCES) {
X			sprintf(buf, "lockf(%s): %s\n", file, errmsg(errno));
X			log(L_WARNING, buf);
X		}
X		return(FALSE);
X	}
X#endif	F_TLOCK
X#else
X#ifdef	LOCK_EX
X	if (flock(fd, LOCK_EX|(non_blocking ? LOCK_NB : 0)) < 0) {
X		if (errno != EWOULDBLOCK) {
X			sprintf(buf, "flock(%s): %s\n", file, errmsg(errno));
X			log(L_WARNING, buf);
X		}
X		return(FALSE);
X	}
X#endif	LOCK_EX
X#endif	USG
X	return(TRUE);
X}
END_OF_FILE
if test 26902 -ne `wc -c <'./xmit/nntpxmit.c'`; then
    echo shar: \"'./xmit/nntpxmit.c'\" unpacked with wrong size!
fi
# end of './xmit/nntpxmit.c'
fi
echo shar: End of archive 8 \(of 9\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 9 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.