DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T n

⟦294ea026f⟧ TextFile

    Length: 10236 (0x27fc)
    Types: TextFile
    Names: »nntpxmit.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦373604645⟧ »EurOpenD3/news/bnews.2.11/src.tar.Z« 
        └─⟦3beb569ac⟧ 
            └─⟦this⟧ »src/nntpxmit.c« 

TextFile

/*
** 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/time.h>
#include <sys/resource.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};

double 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;
	float	ereal, euser, esys;
	struct timeval tod;
	struct timezone tz;

	(void) gettimeofday(&tod, &tz);
	Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.;

	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
			file = host;

		bzero(&Stats, sizeof(Stats));
		if (sendnews(host, file) && Do_Stats) {
			char sysbuf[BUFSIZ];
			syslog(LOG_INFO,
				"%s stats %lu offered %lu accepted %lu rejected %lu failed\n",
				host, Stats.offered, Stats.accepted, Stats.rejected, Stats.failed);

			click(&euser, &esys, &ereal);

			/* we sprintf into sysbuf because syslog is broken. */
			sprintf(sysbuf, "%s xmit user %.1f system %.1f elapsed %.1f\n",
				host, euser, esys, ereal);
			syslog(LOG_INFO, sysbuf);
		}
	}
	exit(EX_OK);
}

/*
** Calculate how much time we've used.
**
** Why `click'? Well, imagine that I've got a stopwatch in my hand...
*/

click(euser, esys, ereal)
float *euser, *esys , *ereal;
{
	static double ouser = 0., osys = 0.;
	double user, sys;
	struct rusage self, kids;
	struct timeval tod;
	struct timezone tzdummy;

	(void) getrusage(RUSAGE_SELF, &self);
	(void) getrusage(RUSAGE_CHILDREN, &kids);
	(void) gettimeofday(&tod, &tzdummy);
	Tend = tod.tv_sec + (double)tod.tv_usec/1000000.;

	user = self.ru_utime.tv_sec + kids.ru_utime.tv_sec +
		(double) self.ru_utime.tv_usec/1000000. +
		(double) kids.ru_utime.tv_usec/1000000.;
	
	sys = self.ru_stime.tv_sec + kids.ru_stime.tv_sec +
		(double) self.ru_stime.tv_usec/1000000. +
		(double) kids.ru_stime.tv_usec/1000000.;

	/* delta T */
	*ereal = Tend - Tbegin;
	*euser = user - ouser;
	*esys = sys - osys;

	/* reset reference point */
	Tbegin = Tend;	
	ouser = user;
	osys = sys;
}

/*
** 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);
	}

#ifdef BSD4_2
 	if (flock(fileno(filefile), LOCK_EX|LOCK_NB) < 0) {
 		dprintf(stderr,"%s: is already being transferred\n", host);
 		fclose(filefile);
 		return(FALSE);
 	}
#endif BSD4_2

	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)) {
				syslog(LOG_DEBUG, "%s: article transmission failed while sending %s", host, article);
				fprintf(stderr, "%s: %s: article transmission failed while sending %s\n", Pname, host, article);
				Stats.failed++;
				failed(file, filefile, article);
				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:
			syslog(LOG_DEBUG,"%s gave an improper response to IHAVE: %d", host, code);
			syslog(LOG_DEBUG,"while sending article %s", article);
			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);
			failed(file, filefile, article);
			fclose(filefile);
			fclose(fp);
			goodbye(DONT_WAIT);
			return(TRUE);
		}
	}
	fclose(filefile);
	if (unlink(file) < 0) {
		syslog(LOG_WARNING,"unlink(%s): %s", file, errmsg(errno));
		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') {
		syslog(LOG_NOTICE, "%s missing message-id!", article);
		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)) {
		syslog(LOG_NOTICE, "%s message-id syntax error!", article);
		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);
}

failed(file, filefile, article)
char *file;
FILE *filefile;
char *article;
{
	register FILE *fp;
	char fname[BUFSIZ], filename[BUFSIZ];

	sprintf(fname, "%s.tmp", file);
	fp = fopen(fname, "w");
	if (fp == NULL) {
		perror(fname);
		return;
	}
	fprintf(fp, "%s\n", article);
	while (fgets(filename, sizeof filename, filefile) != NULL)
		fputs(filename, fp);
	fclose(fp);
	rename(fname, file);
}