v14i054: Network News Transfer Protocol, version 1.5, Part08/09
Rich Salz
rsalz at bbn.com
Thu Apr 21 08:09:03 AEST 1988
Submitted-by: Phil Lapsley <phil at 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 at 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 at ucbarpa.berkeley.edu>
X# May 17, 1986 - Norwegian Independence Day
X#
X# Recognize some new things - February 22, 1987
X# Erik E. Fair <fair at 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 at ucbarpa.berkeley.edu>
X#
X# Add reporting for newnews commands - August 27, 1987
X# Erik E. Fair <fair at ucbarpa.berkeley.edu>
X#
X# Add nntpxmit connection attempt counting/reporting - December 7, 1987
X# Erik E. Fair <fair at 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 at 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 at 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 at uunet.uu.net.
More information about the Comp.sources.unix
mailing list