|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T n
Length: 10236 (0x27fc) Types: TextFile Names: »nntpxmit.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦373604645⟧ »EurOpenD3/news/bnews.2.11/src.tar.Z« └─⟦3beb569ac⟧ └─⟦this⟧ »src/nntpxmit.c«
/* ** 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); }