innfeed-0.10.1.7.orig/0040755000175100001440000000000007044365142012636 5ustar mdusersinnfeed-0.10.1.7.orig/contrib/0040755000175100001440000000000006400213660014266 5ustar mdusersinnfeed-0.10.1.7.orig/contrib/CVS/0040755000175100001440000000000006400213660014721 5ustar mdusersinnfeed-0.10.1.7.orig/contrib/CVS/Root0100644000175100001440000000002306400213656015564 0ustar mdusers/usr/local/cvsroot innfeed-0.10.1.7.orig/contrib/CVS/Repository0100644000175100001440000000004306400213657017023 0ustar mdusers/usr/local/cvsroot/innfeed/contrib innfeed-0.10.1.7.orig/contrib/CVS/Entries0100644000175100001440000000013506400213660016251 0ustar mdusers/README.newsresp_c/1.1/Thu May 22 02:31:15 1997// /newsresp.c/1.1/Thu May 22 02:31:18 1997// innfeed-0.10.1.7.orig/contrib/README.newsresp_c0100644000175100001440000000707606340727763017345 0ustar mdusersFrom koen@eu.net Tue May 13 16:56:18 1997 Received: from hub.org (localhost [127.0.0.1]) by thelab.hub.org (8.8.5/8.8.2) with ESMTP id QAA03683 for ; Tue, 13 May 1997 16:54:25 -0300 (ADT) Received: from gw.home.vix.com (gw.home.vix.com [192.5.5.1]) by hub.org (8.8.5/8.7.5) with ESMTP id LAA21958 for ; Tue, 13 May 1997 11:44:51 -0400 (EDT) Received: (from daemon@localhost) by gw.home.vix.com (8.8.4/) id HAA00708 for innfeed-users-outgoing; Tue, 13 May 1997 07:31:22 -0700 env-from (owner-innfeed-users) Received: from buri.EU.net (buri.EU.net [193.242.90.18]) by gw.home.vix.com (8.8.4/) via ESMTP id HAA00703 for ; Tue, 13 May 1997 07:31:18 -0700 env-from (koen@buri.EU.net) Received: (from koen@localhost) by buri.EU.net (8.7.4/8.7.3) id QAA16906; Tue, 13 May 1997 16:33:28 +0200 (MET DST) From: Koen De Vleeschauwer Message-Id: <199705131433.QAA16906@buri.EU.net> Subject: Re: innfeed-users: innfeed: measuring server response time To: jeff.garzik@spinne.com (Jeff Garzik) Date: Tue, 13 May 1997 16:33:27 +0200 (MET DST) Cc: innfeed-users@vix.com In-Reply-To: <33780678.148D0186@spinne.com> from "Jeff Garzik" at May 13, 97 02:13:12 am X-Mailer: ELM [version 2.4 PL25] Content-Type: text Sender: owner-innfeed-users@vix.com Precedence: bulk Status: RO X-Status: > > Is there an easy way to measure server response time, and print it out > on the innfeed status page? Cyclone's nntpTime measures login banner > response time and an article add and lookup operation. > > It seems to me that innfeed could do something very similar. It could > very easily sample gettimeofday() or Time.Now to determine a remote > server's average response time for lookups, lookup failures, article > send throughput, whatever. > > These statistics might be invaluable to developers creating advanced > connection and article delivery algorithms. If I knew, for example, > that a site's article send/save throughput was really fast, but history > lookups were really slow, my algorithm could reserve a channel or two > for TAKETHIS-only use. > > Jeff > We use a stand-alone program which opens up an additional nntp channel from time to time and takes a peek at the various response times. It's also interesting to tune one's own box. I've included the source code; please consider this supplied 'as is'; bugs and features alike. SunOS, Solaris and Irix ought to be ok; eg. gcc -traditional -o newsresp ./newsresp.c -lnsl -lsocket on S0laris. If a host has an uncommonly long banner you may have to change a constant somewhere; forget. Please note one has to interpret the output; eg. whether one is measuring rtt or history lookup time. Basic usage is: news 1 % newsresp -n 5 news.eu.net --------------------------------- news.eu.net is 134.222.90.2 port 119 elap diff 0.0 0.0 Connecting ... 0.0 0.0 OK, waiting for prompt 0.0 0.0 <<< 200 EU.net InterNetNews server INN 1.5.1 17-Dec-1996 re [...] 0.0 0.0 >>> ihave <244796399@a> 0.0 0.0 <<< 335 0.0 0.0 >>> . 0.0 0.0 <<< 437 Empty article 0.0 0.0 >>> ihave <244796398@a> 0.0 0.0 <<< 335 0.0 0.0 >>> . 0.0 0.0 <<< 437 Empty article 0.0 0.0 >>> ihave <244796397@a> 0.0 0.0 <<< 335 0.0 0.0 >>> . 0.0 0.0 <<< 437 Empty article 0.0 0.0 >>> ihave <244796396@a> 0.1 0.0 <<< 335 0.1 0.0 >>> . 0.1 0.0 <<< 437 Empty article 0.1 0.0 >>> ihave <244796395@a> 0.1 0.0 <<< 335 0.1 0.0 >>> . 0.1 0.0 <<< 437 Empty article 0.1 0.0 >>> quit 0.1 0.0 <<< 205 . news 2 % innfeed-0.10.1.7.orig/contrib/newsresp.c0100644000175100001440000001164106340727766016324 0ustar mdusers/* newsresp.c - EUnet - bilse */ #define FD_SETSIZE 32 #include #include #include #include #include #include #include #define NNTPPORT 119 struct sockaddr_in sock_in; int sock; char buf[1024]; main(argc,argv) int argc; char *argv[]; { int errflg = 0, c; extern char *optarg; extern int optind; struct hostent *host; unsigned long temp; unsigned numart = 1; struct protoent *tcp_proto; char **whoP; while ( (c = getopt(argc,argv,"n:")) != -1 ) switch ( c ) { case 'n': sscanf(optarg,"%u",&numart); break; default : errflg++; } if ( numart == 0 || optind == argc ) errflg++; if ( errflg ) { fprintf(stderr,"Usage: %s [-n articles] host ...\n",argv[0]); exit(1); } close(fileno(stderr)); fileno(stderr) = dup(fileno(stdout)); if ( (tcp_proto = getprotobyname("tcp")) == 0 ) fatal("getprotobyname"); for ( whoP = argv+optind; *whoP != 0; whoP++ ) { if ( (sock = socket(PF_INET,SOCK_STREAM,tcp_proto->p_proto)) < 0 ) fatal("socket"); temp = inet_addr(*whoP); if ( temp != (unsigned long) -1 ) { sock_in.sin_addr.s_addr = temp; sock_in.sin_family = AF_INET; } else { host = gethostbyname(*whoP); if ( host ) { sock_in.sin_family = host->h_addrtype; #if defined(h_addr) /* In 4.3, this is a #define */ memcpy((caddr_t)&sock_in.sin_addr, host->h_addr_list[0],host->h_length); #else memcpy((caddr_t)&sock_in.sin_addr,host->h_addr,host->h_length); #endif } else { fprintf(stderr,"gethostbyname can't find %s\n",*whoP); exit(1); } } sock_in.sin_port = htons(NNTPPORT); printf("---------------------------------\n%s is %s port %d\n", *whoP,inet_ntoa(sock_in.sin_addr),ntohs(sock_in.sin_port)); punt(numart); close(sock); } } error(what) char *what; { ptime(); fflush(stdout); perror(what); } fatal(what) char *what; { error(what); exit(2); } ierror(how,what) char *how, *what; { printf("Expected %s, bailing out.\n",how); } ifatal(how,what) char *how, *what; { ierror(how,what); exit(1); } unsigned do_time(start) unsigned start; { struct timeval now; gettimeofday(&now,(struct timezone *)0); return ( now.tv_sec*1000 + now.tv_usec/1000 - start ); } unsigned start, elapsed, diff; ptime() { diff = elapsed; elapsed = do_time(start); diff = elapsed - diff; printf("%5.1f %5.1f ",((float)elapsed)/1000.0,((float)diff)/1000.0); } massagebuff(bread,buf) int bread; char *buf; { register char *p; if ( bread > 55 ) strcpy(buf+55," [...]\n"); else buf[bread] = '\0'; for ( p = buf; *p != '\0'; ) if ( *p != '\r' ) /* We like to do it RISC style. */ p++; else { *p = ' '; p++; } } punt(numart) int numart; { static char ihave[32], dot[] = ".\r\n", quit[] = "quit\r\n"; struct timeval start_tv; int bread; printf(" elap diff\n"); diff = elapsed = 0; gettimeofday(&start_tv,(struct timezone *)0); start = start_tv.tv_sec*1000 + start_tv.tv_usec/1000; ptime(); printf("Connecting ...\n"); if ( connect(sock,(struct sockaddr*)&sock_in,sizeof(sock_in)) < 0 ) { error("connect"); return(-1); } ptime(); printf("OK, waiting for prompt\n"); if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return(-1); } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"200",3) != 0 && strncmp(buf,"201",3) != 0 ) { ierror("200 or 201",buf); return(-1); } do { sprintf(ihave,"ihave <%u@a>\r\n",start+numart); ptime(); printf(">>> %s",ihave); if ( write(sock,ihave,strlen(ihave)) != strlen(ihave) ) { error("write socket"); return(-1); } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return(-1); } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"335",3) != 0 && strncmp(buf,"435",3) != 0 ) { ierror("335 or 435 ",buf); return(-1); } if ( strncmp(buf,"335",3) == 0 ) { ptime(); printf(">>> %s",dot); if ( write(sock,dot,sizeof(dot)-1) != sizeof(dot)-1 ) { error("write socket"); return(-1); } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return(-1); } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"437",3) != 0 && strncmp(buf,"235",3) != 0 ) { ierror("437 or 235",buf); return(-1); } } } while ( --numart != 0 ); ptime(); printf(">>> %s",quit); if ( write(sock,quit,sizeof(quit)-1) != sizeof(quit)-1 ) { error("write socket"); return(-1); } if ( (bread=read(sock,buf,sizeof(buf))) < 0 ) { error("read socket"); return(-1); } massagebuff(bread,buf); ptime(); printf("<<< %s",buf); if ( strncmp(buf,"205",3) != 0 ) { ierror("205",buf); return(-1); } return(0); } innfeed-0.10.1.7.orig/ChangeLog0100644000175100001440000005440406331417056014414 0ustar mdusersMon Mar 3 14:13:42 1997 James Brister * configfile.l: Removed unused rule. * README: 0.10.1 notes. * innfeed.conf.5: Details on stdio-fdmax and $INCLUDE syntax. * innfeed.conf: Added stdio-fdmax and $INCLUDE examples. * tape.h: Some cleanup. New prototypes added. * tape.c: Code cleanup. Slightly better handling of logging tape information. * sysconfig.h: Added MAX_STDIO_FD and VOLATILE defines. * startinnfeed.c: Handle getting DMALLOC debugging activiated when started from innd. * msgs.h: Message cleanups. * misc.h: Changed ASSERT macro * main.c: Lower max fd limit to whatever select() can handle. Log some global information to the status file. * innlistener.h: Inlclude listenerLogStatus prototype. * innlistener.c: Include dropped article information in the status file. * host.c: Memory leaks plugged. Code cleanup. * getlim.c: Initial revision * endpoint.h: Added a couple new prototypes. * endpoint.c: Better signal handling (Don Lewis) Support broken stdios that have a low fd limit. Allocate global Endpoint array on demand rather than up front ('cause Solaris claims you can have 2^32 file descriptors all at once.). * convertconfig.pl: Added stdio-fdmax. * connection.c: Handle Endpoint create failure (if fd is too big for select()). * configfile.y: Plugged some memory leaks. Fixed off-by-one bug when re-reading the config file. * configfile.l: Handle $INCLUDE files. * configfile.h: Changed addString signature. Added configCleanup prototype. * config_l.c, config_y.c, config_y.h: Initial revision * config.h: Change a comment and FREE macro. * buffer.c: Handle article that's missing final newline (Mark Mallett) Sat Feb 15 19:41:34 1997 James Brister * configfile.y: Changed FALSE and TRUE names to FALSEBVAL and TRUEBVAL. Expanded coverage of WANT_MAIN. * convertconfig.pl: Bug fix in getting port number from a peer entry. * configfile.l: Changed FALSE and TRUE names to FALSEBVAL and TRUEBVAL. * tape.c: Fixed bug in getting peer values for a dynamically added peer. * sysconfig.h: Added DO_NEED_SYS_SELECT for linux. * innfeed.conf.5: Updated version number for innfeed. * innfeed.1: Updates for new config file and proper version numbers. * host.c: Catch problem with object being accessed after deletion. * endpoint.c: Fixed broken '#if' test. Fri Feb 14 13:15:01 1997 James Brister * README: Updates for 0.10 * innfeed.conf: New style config file. * tape.h: Added gFlushTapes tapeFlush tapeConfigLoadCbk prototypes. * startinnfeed.c: Let USER and INNFEED macros be passed in on the command line. * innfeed.conf.5: Major updates for 0.10. * innfeed.1: Various 0.10 updates. * INSTALL: Minor tweaks. * Makefile: Added install target. * INSTALL: Updated instructions for 0.10. * procbatch.pl: Removed '-w' from interpreter line. Handle innshellvars.pl automagically. Drop usage of 'tape' * startinnfeed.c: Changed path to innfeed. Slight ansi-fication. * misc.h: Added pathMax prototype. * misc.c: Added pathMax and logOrPrint functions. * host.h: Added hostConfigLoadCbk prototype. * host.c: Use new config file interface. * endpoint.h: Added prototypes for cancelWrite and setSigHandler. * endpoint.c: Make signal handling be more consistent. Minor cleanups and bug fixes. * convertconfig.pl: Expanded for new variables. * connection.c: - Use new config file. - Handle remote returning 400 when data still queued for writing. * configfile.y, configfile.l: Various cleanups. * configfile.h: Initial revision Thu Feb 13 23:21:31 1997 James Brister * main.c: New config file usage. Signal handling made asynchronous. * innlistener.c: Bug fixes in remembering bad peers. New fast-exit option via config file * innlistener.h: Include listenerConfigLoadCbk prototype. * tape.c: - Use of new run-time config file structure. - Keep output file open all the time. - Check for hand-prepared files only every X seconds. - Include tape data in status file output. - No more queueing of articles -- they go straight to the file. - The output file must be open for a minimum amount of time before it can be rotated to the input file. * msgs.h: Various new messages. * connection.h: Added cxnConfigLoadCbk prototype. * sysconfig.h: Minor updates. * malloc.c: Help out those systems with sbrk() conflict. * config.h: Grand rearrangment for new config file. * buffer.c: bug fix in memcpy usage (array overrun). * article.c: Check for empty article file. * Makefile: 0.10 update. * testListener.pl: update Tue Jan 28 17:42:55 1997 James Brister * configfile.y, host.c: snapshot Thu Jan 23 23:19:42 1997 James Brister * configfile.y: Initial revision * configfile.l: snapshot Sun Jan 19 22:24:33 1997 James Brister * convertconfig.pl, configfile.l: Initial revision Mon Jan 13 01:02:07 1997 James Brister * README: Heaps o' updates * procbatch.pl: Co-exist with inn 1.5 more easily ('requires' innshellvars.pl). * innfeed.1: Major work for 0.9.3. * tape.h: Added prototype for setOutputSizeLimit. * tape.c: Support for shrinking output backlog files. * startinnfeed.c: Extra include for FreeBSD. * msgs.h: Various new syslog messages added. * misc.h: Code consolidation. Comment additions. * misc.c: Code consolidation of the logging functions. Added the buildFilename function. * main.c: New options. * innlistener.h: Added prototypes for openDroppedArticleFile and closeDroppedArticleFile * innlistener.c: Extra includes. Support the dropped files. * inet_addr.c: Include sys/types.h * host.h: Added hostSetStatusFile prototype. * host.c: Track global values for status file and end-of-process log. Dropped articles go into per-process file. Support relative path for status file. * endpoint.c: Added include of sys/select.h * connection.c: Fixed broken call to syslog. * config.h: Changes in defaults. Added INNFEED_STATUS and LIMIT_FUDGE * article.c: Minor bug fixes. Tue Jan 7 21:12:42 1997 James Brister * innlistener.c: Prototype fixups and a signed vs. unsigned fix. * tape.c: Fixed up signed vs. unsigned. Other minor cleanups. * startinnfeed.c: Include string.h to get strerror() protoype. * msgs.h: Fixed broken conversion character. * misc.h: Added prototype for addPointerFreedOnExit(). Added GNU __attribute__ declarations. * main.c: A 'const' fixup. * innlistener.h: Added prototype for openInputFile(). * host.c: Minor signed vs. unsigned cleanup. * endpoint.h: Added prototype for freeTimeoutQueue(). * endpoint.c, connection.c: A 'const' fixup. * config.h: Added a #define for GNU's __attribute__ keyword. * article.c: dprintf format string mismatch fixed. * testListener.pl: Don't sleep at end if stdout is a file. * main.c: Handle startup with fd 0,1 or 2 being closed (Dave Lawrence) * sysconfig.h: Added appropriate HAVE_MMAP values (Dave Lawrence) * msgs.h: Added CKPT_BNDRY message. * article.c: Do mmap'ing of articles (Dave Lawrence). * endpoint.c: Type cleanup. Thu Jan 2 15:31:19 1997 James Brister * tape.c: Drop using the checkpoint files to just scribble in the input file. Tue Dec 31 16:51:40 1996 James Brister * testListener.pl: Crosspost to local.test * Makefile: Added startinnfeed Fri Dec 13 12:02:31 1996 James Brister * startinnfeed.c: Use strerror() instead of indexing into sys_errlist. * tape.c: Handle case of can't open input file even though it exists. * main.c: Fixed up useage to include a and p flags. * endpoint.c: Cleaned up extra handling of the mainEndPoint. Sat Dec 7 02:28:42 1996 James Brister * endpoint.c: Check if endpoing being deleted is the main endpoint. * README: More updates. * msgs.h: Added MODE_WRITE_PENDING define. * main.c: New -p and -a options. * README: 0.9.2 updates * innfeed.1: Various updates for 0.9.2 * host.c: Call cxnTerminate on flush rather than cxnClose. * endpoint.c: Check mainEndPoint fd more frequently than others. * connection.h: Added cxnTerminate prototype. * connection.c: Check mainEndPoint fd more frequently than others. On flush drop articles not being actually processed. Parameterized pid file. * config.h: Added the SELECT_RATIO define. Thu Nov 28 19:22:59 1996 James Brister * startinnfeed.c: Handle setuid and setgid failures. * config.h: Added GEN_HTML #define. * misc.c: Added time to die() output. * innfeed.1: Document the SIGINT handling. * host.c: Expanded innfeed.status values. * misc.h: Removed extra newline in ASSERT output. * connection.c: Removed extraneous return value. * Makefile: Version number change. Dropped innlog.pl from the distribution. Tue Nov 26 08:56:05 1996 James Brister * sysconfig.h: AIX updates from SeokChan Lee. Fri Nov 22 15:07:08 1996 James Brister * startinnfeed.c: Initial revision Thu Nov 21 13:57:24 1996 James Brister * host.c, main.c, msgs.h: Added syslogging of signal catches. (Michael Hucka) Better innfeed.status output (Michael Hucka) * Makefile, sysconfig.h: Updates for AIX 4.x and 3.x. (dwd@ra.snu.ac.kr) Wed Nov 20 18:02:44 1996 James Brister * TODO.innfeed, config.h, connection.c, connection.h, endpoint.c, host.c, host.h, innlistener.c, innlistener.h, main.c, misc.c, msgs.h, tape.c: with tale's patches Fri Nov 15 21:08:09 1996 James Brister * README.ssh: Initial revision Mon Aug 26 21:37:58 1996 James Brister * TODO.innfeed: snapshot * procbatch.pl: Tidy-ups from tale@uunet.uu.net. Wed Aug 21 07:18:50 1996 James Brister * connection.c: Added forgotten value for a return statement. * Makefile: Update version number only. * innlistener.c: minor bug fix for badPeerName Tue Aug 20 22:55:44 1996 James Brister * README: Added 0.9 updates. * Makefile: Include ChangeLog and makedepend.sh (not makdepend) in distribution. * ChangeLog: Initial revision Tue Aug 20 14:08:58 1996 James Brister * uio_maxiov.c: Changed MAX_IOVEC to MAX_WRITEV_VEC (due to linux). * innlistener.c: Only try to dynamically create a Host the first time an article arrives for it. * tape.c: + Remove empty PEER.output files when closing. + Better handling of garbage in checkpoint files. * sysconfig.h: + Changed MAX_IOVEC to MAX_WRITEV_VEC as linux had a conflicting CPP symbol. + Remove LOG_PID from L_OPENLOG_FLAGS and put it into the actual call (so if using INN's config.h the pid will still get in there). * procbatch.pl: Check hosts names for illegal characters. Better input line format verification. Added a -q option (for quiet). * msgs.h: Various new messages added. * misc.h: Changed getHostDefaults to support per-host streaming. * misc.c: Added a sleep before the abort. Argument fix to syslog call. * malloc.c: Added a sleep before the abort(). * main.c: + Handle per-peer streaming (wolf@pasteur.fr) + Re-install signal handlers inside the handlers for SVR4. + Don't die if a Host defined in the config file is locked (just forget about it). * innlistener.c: + Removed extra call to freeBufferArray that was breaking things. + Support for per-peer streaming. + More robust handling of dynamic peers that can't be created. * innfeed.conf.5: Describe the new fields (streaming, low-pass-low and low-pass-high). * innfeed.conf: Drop the immediate-open field and added the streaming and low-pass-low and low-pass-high. * innfeed.1: + Describe new innfeed.status format. + Misc cleanups. * host.h: Change signature of newHost to allow per-host streaming usage. * host.c: + Let the Host object dictate whether its connections are streaming or not * endpoint.c: + Changed MAX_IOVEC to MAX_WRITEV_VEC due to class with linux cpp symbol. + Added a small sleep before abort() call. + Call signal() inside signal handlers for the sake of SVR4. * connection.c: + Changed a lot of ASSERTs to actually handle the error condition instead of aborting. + Catch a reject banner from nnrpd. + Let the Host object dictate whether streaming should be used or not. * Makefile: Include malloc.c, and innlog.pl. Mon Aug 12 09:08:39 1996 James Brister * buffer.c: Remove buffer byte count assert. * article.c: Log double filenames instead of calling die(). Mon Jun 24 17:41:38 1996 James Brister * msgs.h: Big cleanup by kre. Added REMOTE_STREAMING_OFF. * main.c: Fixed compilation warnings. Forced LOG_PID in openlog. * host.c: Fixed some compilation warnings. Expanded host status logging. * endpoint.c: Fixed some compilation warnings. * connection.c: Randomizing certain timers slightly. Fixed some compilation warnings. Limit number of times a NOCR message is logged. Put a connection to sleep or kill it depending on error and current state. Handling confused peers more robustly. * INSTALL: malloc instructions. Fri Jun 7 09:52:29 1996 James Brister * malloc.c: Initial revision Wed Jun 5 14:14:05 1996 James Brister * INSTALL: Installation updates. * tape.c: No appending of tape files--simple moves instead. * sysconfig.h: Changes for nec_ews * procbatch.pl: Code speed ups. * msgs.h: Added a couple of messages. * main.c: Bug fix with parsing near-empty innfeed.conf file. * innlistener.c: Bug fixed on input lines read from innd. * innfeed.conf: Changed read timeout to 300 seconds. Comment about obsolete immediate-open field. * connection.c: Handle response 480 (probably talking to an nnrpd). Fixed sleepTimeout increases. Changed article queue management slightly. * innfeed.1: BUG section added. Discussion of the tape files. * host.c: Logging and status changes (and bug fix). Connection scheduling changed. * buffer.c: Bug fixed on bufferByteCount total. * config.h: slight cleanup. * Makefile: New version and including procbatch.pl in distribution. * README: Various notes. * article.c: Turned hash table validation into a debugging macro Thu May 23 22:41:48 1996 James Brister * Makefile: Version 0.8.3 * connection.c: Fixed typo. * Makefile: Version 0.8.2 * connection.c: Fix timer clearing after getting mode response. * connection.c: Clean up read timeout setup. * README: Various additions. * Makefile: Preparing for 0.8.1 * sysconfig.h: Minor fixups. * tape.c: Minor bug fix. * msgs.h: Various minor typos fixed. Added global stats logging string. * misc.c: Generate cores in a special directory. Change die() output slightly to include date. * main.c: Minor bug fixes. * innlistener.c: syslog argument format fixes. * host.c: Added status file generation. Added global counters. Minor bug fixes. * endpoint.c: Minor bug fixes. * connection.c: Various state transition fixes. * config.h: Added CORE_DIRECTORY definition. * buffer.c: Minor bug fix. * article.c: Various minor bug fixes. * innfeed.1: Added syslog entries and innfeed.status description. Mon May 20 00:53:01 1996 James Brister * Makefile: Fixed typo. * README: 0.8 final version. * procbatch.pl: Expanded useage string. * connection.c: Fixed clearTimer bug. Sun May 19 22:27:46 1996 James Brister * tape.c: Strip out the old code that had been ifdef'd out. * sysconfig.h: Major re-work. * msgs.h: Use of 'ME' in all appropriate syslog messages. * misc.h: Change in DEFINES. Strip out use of DEADBEEF. * misc.c: Change in use of time(). Bug fixes. * main.c: Change in use of time(). Stripped out the commanders stuff. * innlistener.c: Change in use of time(). * inet_addr.c: Removed inet_addr as it's not needed. * Makefile: Various cleanups. * host.h: Added hostLogNoCheckMode * host.c: Timer fix ups. Logging fixups. * endpoint.c, endpoint.h: Tidied up use of time(). * connection.h, connection.c: Complete state machine change. * config.h: Various cleanups. * commander.c: use DO_NEED_FILE * article.c: Fixed memory leak. * distfile, procbatch.pl: Initial revision Sat Apr 13 16:44:05 1996 James Brister * README: Added notes for version 0.7.1 * Makefile: Added inet_addr.c to build Better cleanup. * Makefile: foo * main.c: Fixing up include of commander.h * README, innfeed.1, makedepend.sh, Makefile, article.c, article.h, buffer.c, buffer.h, commander.c, commander.h, config.h, connection.c, connection.h, ctlinnfeed.c, endpoint.c, endpoint.h, host.c, host.h, innlistener.c, innlistener.h, main.c, misc.c, misc.h, msgs.h, sizeof.c, sysconfig.h, tape.c, tape.h, uio_maxiov.c: 0.7 snapshot Sat Mar 23 10:16:37 1996 James Brister * testListener.pl, tape.c, main.c, innlistener.c, host.c, config.h, connection.c, buffer.c, article.c: snapshot * tape.h, testListener.pl, uio_maxiov.c, main.c, misc.c, misc.h, msgs.h, sysconfig.h, tape.c, host.h, innfeed.1, innlistener.c, innlistener.h, connection.h, endpoint.c, endpoint.h, host.c, connection.c, article.h, buffer.c, buffer.h, commander.c, config.h, INSTALL, Makefile, README, article.c: Snapshot; Tue Mar 12 21:44:53 1996 James Brister * makedepend.sh: Initial revision Mon Feb 5 03:20:36 1996 James Brister * sysconfig.h: Initial revision Thu Feb 1 12:54:36 1996 James Brister * msgs.h: General cleanup. * misc.h: dprintf changes. * misc.c: dprintf changes. maxFd changes. deadBeef addition. * main.c: loggingLevel instead of debuggingOutput Always redirect stdout and stderr somewhere. syslog cleanup. dprintf changes. SIGUSR1 and SIGUSR2 handling. * innlistener.c: dprintf change and handle missing articles. * host.c: Don't get input tape until connected. dprintf change dont't log stats on connections unless compiled in. * endpoint.c: dprintf change and some cleanup. * connection.h: Copyright addition and some cleanup. * connection.c: dprintf change. Fixed response parsing to MODE STREAM command. lockCxn() function * config.h: Pulled platform stuff into sysconfig.h Some cleanup. * commander.c, buffer.c: dprintf change. * article.c: dprintf change and syslog string cleanup. * article.h, buffer.h, commander.h, endpoint.h, host.h, innlistener.h: copyright * innlog.awk: Diffs to handle some of innfeed's entries. * uio_maxiov.c: Initial revision Fri Jan 26 14:05:44 1996 James Brister * innlog.awk: Initial revision Wed Jan 24 01:57:39 1996 James Brister * msgs.h: Message format changes. * main.c: Fixed tests for redirected file and bug on immediate open when using '-x' * innlistener.h, innlistener.c: Added listenerIsDummy function. * host.c: Fixed the null-tape bug and toned down some logging. * connection.c: Fixed TAKETHIS-only mode toggling. * TODO: Various. * README: Thanks. * Makefile: New version and including innlog.awk * INSTALL: Relative path for feed entry. Mon Jan 22 14:59:09 1996 James Brister * TODO: Various changes. * INSTALL: Included innlog.awk info. * README: Slight wording change. * testListener.pl: Change the generated article's format a bit to catch the 'dot' errors. * tape.c: When getting an article off tape we don't stop until we get a an article that actually exists. * msgs.h: Added a message. * main.c: Turn off Commanders by default. Check that stdin is not a redirected file. Open connections immediately if running in batch mode. * innlistener.c: Wasn't stopping when running in batch mode and input was all used up. * innfeed.1: Included info on piping to stdin. * host.c: Redid how the Host gives an article to the Connection when the Connection is idle. * endpoint.c: Do better logging in the endpoint exception handler. Also catch the divide by zero case in hitCompare. * connection.h: Added the cxnQueueArticle function. * connection.c: Fixed the alpha-platform inet_addr bug and changed the article queing mechanism a little bit. * article.c: Fixed up how we determine if the file exists. Also fixed the 'dot' bug in the nntp buffer preparation. Sun Jan 21 11:21:54 1996 James Brister * sizeof.c: Initial revision * endpoint.c: Changed the initial value for timesSelected as linux was not returning -1 on the connect() so prepareWrite wasn't being called. * Makefile, README, connection.c, innfeed.1, innfeed.conf.5, msgs.h: snapshot Sat Jan 20 14:45:12 1996 James Brister * Makefile: beta 1 again * Makefile, testListener.pl, INSTALL, README, TODO, article.c, config.h, connection.c, host.c, host.h, innlistener.c, innlistener.h, main.c, misc.c, misc.h, msgs.h, tape.c, tape.h: beta 1 snapshot * innfeed.conf, innfeed.1, innfeed.conf.5: Initial revision Fri Jan 19 11:37:45 1996 James Brister * Makefile: removed DMALLOC for release. * Makefile, article.c, buffer.c, connection.c, endpoint.c, host.c, innlistener.c, tape.c, testListener.pl: snapshot Thu Jan 18 19:09:03 1996 James Brister * Makefile, TODO, article.c, buffer.c, commander.c, config.h, connection.c, endpoint.c, host.c, innlistener.c, main.c, misc.c, msgs.h, tape.c, tape.h, testListener.pl, article.h, buffer.h, ctlinnfeed.c, host.h, misc.h: snapshot Tue Jan 16 12:15:59 1996 James Brister * TODO: Initial revision * testListener.pl: snapshot * README, INSTALL: Initial revision * article.c, article.h, buffer.c, commander.c, commander.h, connection.c, connection.h, endpoint.c, host.c, host.h, innlistener.c, misc.c, misc.h, msgs.h, tape.h: snapshot * ctlinnfeed.c, main.c: Initial revision Sun Jan 14 07:42:22 1996 James Brister * article.c, article.h, buffer.c, connection.c, connection.h, host.c, host.h, innlistener.c, misc.h, tape.c: snapshot * testListener.pl: Initial revision Sun Jan 7 12:20:54 1996 James Brister * tape.h, tape.c, msgs.h, misc.h, misc.c, innlistener.h, innlistener.c, host.h, host.c, endpoint.h, endpoint.c, connection.h, config.h, commander.h, commander.c, buffer.h, buffer.c, article.h, article.c, Makefile: Initial revision Tue Jan 2 09:24:24 1996 James Brister * connection.c: Initial revision innfeed-0.10.1.7.orig/INSTALL0100644000175100001440000000741506331417054013671 0ustar mdusers1. Edit sysconfig.h as appropriate. This file is where all the platform variations live. Any porting problems should be resolved in here. 2. Edit the first section of the Makefile to set things like the C compiler. 3. Look at config.h. This contains non-platform specific defaults. All the interesting items in here can now be set at run time, so you shouldn't need to change anything in here. 4. Look at startinnfeed.c. This program is setuid root as a front end to innfeed. Its only job is to raise the resource limits on memory and open files before exec'ing innfeed. You'll need this if you configure innfeed to handle many peers and connections. You should look at it to satisfy yourself that it does what I say it does. 5. type 'make' If you get compile errors about UIO_MAXIOV in endpoint.c then: make check-maxiov and then edit the define for MAX_IOVEC in sysconfig.h to use the value displayed. If you later get run time log errors like "failed to write command to remote: invalid argument" then your MAX_IOVEC value is too big. Make and run check-maxiov to verify the number. 6. type 'make install' to copy the new binaries to the pathname you set in the Makefile at step 2. NOTE that the program startinnfeed is installed setuid root. So if you're not running as root when you do the installation, you'll need to fix that up with chown root.news /usr/news/local/startinnfeed chmod u+s,ug=rx,o-rwx /usr/news/local/startinnfeed 7. Create the backlog file directory. This comes from (in order of preference). - the ``-b'' option on the innfeed command line. - the config file value for ``backlog-directory'' - the config.h value for TAPE_DIRECTORY. 8. Copy the sample innfeed.conf to its proper place. This value comes from (in order of preference). - the ``-c'' option on the innfeed command line. - the config.h value for CONFIG_FILE. If you're running innfeed version 0.9.3 or lower, then you can use the supplied perlscript convertconfig.pl to create a new-style config file: convertconfig old-innfeed.conf > new-innfeed.conf 9. Edit the new innfeed.conf as approriate. 10. Validate the new config file with: /usr/news/local/innfeed -C Once you're happy with the new config then... 11. For each peer, add an entry to the INN newsfeeds file that looks something like this: peername:groups+distributions\ :Tm:innfeed! e.g. nic.near.net\ :!junk/!local\ :Tm:innfeed! 12. Add an entry like this too for the above entries to feed into. innfeed!:!*\ :Tc,Wnm*\ :/usr/news/local/startinnfeed As mentioned above, startinnfeed is used here if you have innfeed configured to handle more peers and connections than the system will normally let it. 13. Get innd to start it up (e.g. reload the newsfeeds file). 14. If innd writes a file called innfeed! in the out.going area of your spool, then the program procbatch included here can be used to process it. Edit the definition of '$innshellvars' in procbatch as necessary. Run 'probatch -h' to get instructions. ------------------------------------------------------------------------------ If you want to include Larry Wall's debugging version of malloc included here (for testing purposes only--shouldn't need it otherwise), then in the Makefile uncomment the "MALLOC = malloc.o" line and add the necessary '-I' value to the INCDIRS variable definition (to point at the include subdirectory of the inn source tree). At a pinch you could just rebuild innfeed like this: make MALLOC=malloc.o INCDIRS=-I/usr/local/src/inn/include The Makefile builds malloc.o in debugging mode--meaning it will cause a core dump (into the top of your news spool) on certain malloc failures. Remove the '-Ddebug' from the build line if you prefer to just syslog any failures. innfeed-0.10.1.7.orig/Makefile0100644000175100001440000002074606365762217014315 0ustar mdusers# -*- makefile -*- # # Author: James Brister -- berkeley-unix -- # Start Date: Wed Nov 29 22:01:56 1995 # Project: INN (innfeed) # File: Makefile # RCSId: $Id: Makefile,v 1.5 1997/07/24 23:26:07 scrappy Exp $ # Description: Makefile for innfeed. # # Where the programs get installed. BINDIR = /usr/news/local # Where your man pages live. MANDIR = /usr/local/man # The compiler you want. NOTE... AIX 3.2.5 users do NOT use CC. See the # comment in sysconfig.h CC = gcc # How to run lex and yacc. Innfeed requires Flex (but you shouldn't need it). LEX = lex -d YACC = yacc -t -d # Debug option for compiler -g or -O (or both if using gcc). DBFLAGS = -g # Add any architecture specific defines, flags etc. you need OSFLAGS = # OSFLAGS = -DSOLARIS # uncomment for Solaris # Any extra non-architecture specific defines you may need. # # Add -DUSE_INN_INCLUDES to get INN headers files used (see INCDIRS below) # Add -DNO_SBRK if compilation of malloc.c complains about sbrk() prototype. DEFINES = # Any system specific libraries. If you get undefines at link time for names # matching yy*, then you may need ``-ly -ll''. LIBRARIES = # uncomment for SOLARIS but you may have to leave out the -lresolv # LIBRARIES = -lnsl -lsocket -lresolv ## Add any `-L' flags here LDFLAGS = ## Add any local includes '-I' needed here. (e.g. if using -DUSE_INN_INCLUDES) # INCDIRS = -I/usr/local/src/inn-1.4/include INCDIRS = ## Uncomment the next line to build in larry wall's version of malloc. NOTE: ## You need to build malloc.o with a -I pointing at the include subdirectory ## of your inn source tree. See INCDIRS above. # MALLOC = malloc.o # Uncomment the next line if you don't have a inet_aton() in your # libraries (Solaris is one such OS). # EXTRA_SRC = inet_addr.c # Any extra compiler flags for warning generation. WARNINGS = #WARNINGS = -Wall $(ALL_GCC_WARNINGS) -DCHECK_FORMATS ## Uncomment the next three lines to use the GNU dmalloc library. # DMALLOC = -DUSE_DMALLOC # DMALLOC_LIB = -L/usr/local/gnu/lib -ldmalloc # DMALLOC_INCDIR = -I/usr/local/gnu/include LINTFLAGS = -b -h -z $(DEFINES) ###################################################################### #### NOTHING BELOW HERE IS REALLY CONFIGURABLE #### ###################################################################### .SUFFIXES: .E .sh .pl CPP.c = $(CC) $(CFLAGS) $(CPPFLAGS) -E -I. COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c -I. LINK.o = $(CC) $(LDFLAGS) LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) OUTPUT_OPTION = -o $@ SRC = article.c \ buffer.c \ connection.c \ endpoint.c \ host.c \ innlistener.c \ main.c \ misc.c \ tape.c INCS = article.h \ buffer.h \ config.h \ configfile.h \ connection.h \ endpoint.h \ host.h \ innlistener.h \ misc.h \ msgs.h \ sysconfig.h \ tape.h MANSRC = innfeed.1 innfeed.conf.5 EXTRAS = testListener.pl inet_addr.c uio_maxiov.c configfile.y \ configfile.l config_l.c config_y.c config_y.h \ procbatch.pl convertconfig.pl malloc.c ChangeLog \ startinnfeed.c OBJS = $(SRC:.c=.o) $(EXTRA_SRC:.c=.o) \ config_l.o config_y.o INNFEED_BIN = $(BINDIR)/innfeed STARTINNFEED = $(BINDIR)/startinnfeed ALL_INSTALLED = $(BINDIR)/innfeed $(BINDIR)/startinnfeed \ $(BINDIR)/procbatch $(BINDIR)/convertconfig MAN_INSTALLED = $(MANDIR)/man1/innfeed.1 $(MANDIR)/man5/innfeed.conf.5 ALL_GCC_WARNINGS = -pedantic -W -Waggregate-return -Wcast-align \ -Wcast-qual -Wcomment -Wformat \ -Wimplicit -Winline -Wmissing-prototypes \ -Wmissing-declarations -Wnested-externs \ -Wparentheses -Wpointer-arith \ -Wshadow -Wstrict-prototypes -Wswitch \ -Wtraditional -Wtrigraphs \ -Wunused -Wwrite-strings # -Wreturn-type -Wtemplate-debugging MORE_WARN = -Wconversion CFLAGS = $(OSFLAGS) $(DBFLAGS) $(WARNINGS) CPPFLAGS = $(INCDIRS) $(DMALLOC_INCDIR) $(DMALLOC) $(DEFINES) LOADLIBES = $(DMALLOC_LIB) $(LIBRARIES) VERSION = 0.10.1 COMPRESS = gzip COMPRESS_EXT = gz TARSRC = $(SRC) $(INCS) Makefile README INSTALL innfeed.conf \ $(EXTRAS) $(MANSRC) makedepend.sh TARDIR = innfeed-$(VERSION) TARBALL = $(TARDIR).tar.$(COMPRESS_EXT) TAR = tar MKDIR = mkdir CP = cp -p RM = rm -f MV = mv TAG = etags CHMOD = chmod INSTALL = install -c MAKEDEPEND = ./makedepend DEPENDFLAGS = -s -c '$(CC) -E' .c.o: $(COMPILE.c) $< $(OUTPUT_OPTION) .c.E: $(CPP.c) $< $(OUTPUT_OPTION) .sh: $(RM) $@ ; $(CP) $< $@ ; $(CHMOD) a-w,a+x $@ .pl: $(RM) $@ ; $(CP) $< $@ ; $(CHMOD) a-w,a+x $@ all: innfeed startinnfeed procbatch convertconfig install: $(ALL_INSTALLED) $(MAN_INSTALLED) innfeed: $(OBJS) version.o $(MALLOC) $(LINK.c) -o $@ $(OBJS) version.o $(MALLOC) $(LOADLIBES) SAVEOLD = rm -f $@.orig ; mv $@ $@.orig COPYFILE = $(INSTALL) $? $@ $(INNFEED_BIN): innfeed $(SAVEOLD) $(COPYFILE) $(BINDIR)/procbatch: procbatch $(SAVEOLD) $(COPYFILE) $(BINDIR)/convertconfig: convertconfig $(SAVEOLD) $(COPYFILE) $(BINDIR)/startinnfeed: startinnfeed $(SAVEOLD) $(COPYFILE) chown root.news $@ chmod 04755 $@ objs: $(OBJS) tags: $(SRC) $(INCS) $(TAG) $(SRC) $(INCS) clean:: $(RM) version.c *.E *.o *.a *.core makedepend tags TAGS core \#* *~ $(RM) Makefile.BAK procbatch convertconfig $(RM) innfeed startinnfeed uio_maxiov listing.ps logfile lint.log rcsclean:: rcsclean -u rcscoall:: -co -q -u RCS/*,v realclean:: clean $(RM) y.tab.c y.tab.h lex.yy.c config_y.c config_y.h config_l.c $(RM) -r $(TARDIR) $(RM) $(TARBALL) distclean:: realclean rcsclean .PHONY: tar listing listing:: @$(RM) foo.ps ; \ F=`/bin/ls *.c *.h 2> /dev/null | sed 's/h$$/H/' |\ sort | sed 's/H$$/h/'` ;\ CMD="nenscript -p- $$F | psnup -4 -d1 > listing.ps" ;\ if [ "X$$F" != X ]; then echo $$CMD ; eval $$CMD ; fi startinnfeed.o: startinnfeed.c $(COMPILE.c) -DINNFEED=\"$(INNFEED_BIN)\" -o $@ $? startinnfeed: startinnfeed.o $(LINK.c) -o $@ $? uio_maxiov: uio_maxiov.o $(LINK.c) -o $@ uio_maxiov.o check-maxiov: uio_maxiov @./uio_maxiov makedepend: makedepend.sh version.c: $(OBJS) echo 'const char *versionInfo = "innfeed v. $(VERSION) ('\ `date` ')" ;' |\ sed -e 's/( /(/' -e 's/ )/)/' > version.c tar: $(TARBALL) tardir: $(TARSRC) $(RM) -r $(TARDIR) $(MKDIR) $(TARDIR) $(CP) $(TARSRC) $(TARDIR) $(TARBALL): tardir $(TAR) cvf - $(TARDIR) | $(COMPRESS) > $(TARBALL) depend: makedepend $(SRC) $(INCS) $(MAKEDEPEND) $(DEPENDFLAGS) $(CPPFLAGS) $(SRC) diffdir:: echo -n "Directory? " ; read dir ;\ if [ -d $$dir ]; then\ for i in *.c *.h Makefile;do\ [ ! -f $$dir/$$i ] && continue;\ if ! cmp $$i $$dir/$$i > /dev/null 2>&1; then\ echo patch $$i "<< 'EOF'";\ diff $$dir/$$i $$i;\ echo EOF;\ fi;\ done;\ else\ echo No such directory $$dir;\ fi lint: $(OBJECTS) -lint -u $(LINTFLAGS) $(SRC) > lint.log malloc.o: malloc.c $(COMPILE.c) -Ddebug $< $(OUTPUT_OPTION) # FIXYACC = -e '/^.line [0-9][0-9]* "y.tab.c"[ \t]*$$/d' \ # -e '/^.line [0-9][0-9]* "configfile.y"[ \t]*$$/d' # FIXLEX = -e '/^.line [0-9][0-9]* "lex.yy.c"[ \t]*$$/d' \ # -e '/^.line [0-9][0-9]* "configfile.l"/d' config_y.c config_y.h: configfile.y $(YACC) $? mv y.tab.h config_y.h mv y.tab.c config_y.c # sed $(FIXYACC) < y.tab.c > config_y.c # rm -f y.tab.c config_l.c: configfile.l $(LEX) $? mv lex.yy.c config_l.c # sed $(FIXLEX) < lex.yy.c > config_l.c # rm -f lex.yy.c config_l.o: config_y.o config_l.c tst: config_y.c config_l.c gcc -DWANT_MAIN -o tst -g -Wall config_y.c config_l.c -ly -ll # DO NOT DELETE THIS LINE -- make depend depends on it. article.o: article.c article.h buffer.h config.h endpoint.h misc.h \ msgs.h sysconfig.h buffer.o: buffer.c buffer.h config.h misc.h sysconfig.h connection.o: article.h buffer.h config.h configfile.h connection.c \ connection.h endpoint.h host.h misc.h msgs.h sysconfig.h endpoint.o: buffer.h config.h endpoint.c endpoint.h misc.h msgs.h \ sysconfig.h host.o: article.h buffer.h config.h configfile.h connection.h \ endpoint.h host.c host.h innlistener.h misc.h msgs.h sysconfig.h \ tape.h innlistener.o: article.h buffer.h config.h configfile.h endpoint.h \ host.h innlistener.c innlistener.h misc.h msgs.h sysconfig.h tape.h main.o: article.h buffer.h config.h configfile.h connection.h \ endpoint.h host.h innlistener.h main.c misc.h msgs.h sysconfig.h \ tape.h misc.o: config.h endpoint.h misc.c misc.h msgs.h sysconfig.h tape.h tape.o: article.h config.h configfile.h endpoint.h misc.h msgs.h \ sysconfig.h tape.c tape.h innfeed-0.10.1.7.orig/README0100644000175100001440000004162606375220535013526 0ustar mdusersThis is the beta test version 0.9.3 of the INN feeder program `innfeed.' It is written in ANSI C and tries to be POSIX.1 compliant. The features of it are: 1. Handles the STREAM extenstion to NNTP. 2. Will open multiple connections to the remote host to parallel feed. 3. Will handle multiple remote hosts. 4. Will tear down idle connections. 5. Runs as a channel/funnel feed from INN, or by reading a funnel file. 6. Will stop issuing CHECK commands and go straight to TAKETHIS if the remote responds affermatively to enough CHECKs. 7. It will go back to issuing CHECKs if enough TAKETHIS commands fail. Read the rest of this file and then read and apply the instructions in the the INSTALL file. Changes for 0.10.1 1. Config file inclusion now works via the syntax: $INCLUDE pathname Config files can be included up to a nesting depth of 10. Line numbers and file names are not properly reported yet on errors when includes are used, though. 2. Signal handling is tidied up a bit. If your compiler doesn't support the ``volatile'' keyword, then see the comment in sysconfig.h. 3. If you have a stdio library that hash limit on open files lower then the process limit for open plain files (all flavours of SunOS), then a new config file variable ``stdio-fdmax'' can be used to give that upper bound. When set, all new network connections will be limited to file descriptors over this value, leaving the lower file descriptors free for stdio. See innfeed.conf(5) for more details. Remember that the config file value overrides any compiled in value. Changes for 0.10 1. A major change has been made to the config file. The new format is quite extensible and will let new data items be added in the future without changing the basic format. There's a new option ``-C'' (for ``check'') that will make innfeed read the config file and report on any errors and then exit. This will let you verify things before locking them into a newsfeeds file entry. A program has been added ``convertconfig.pl'' that will read your old config off the command line (or stdin), and will write a new version to stdout. The new config file structure permits non-peer-specific items to be declared (like the location of the status file, or whether to wrap the generated status file in HTML). This is part of the included sample: use-mmap: true news-spool: /var/news/spool/articles backlog-directory: /var/news/spool/innfeed pid-file: innfeed.pid status-file: innfeed.status gen-html: false log-file: innfeed.log backlog-factor: 1.10 connection-stats: false max-reconnect-time: 3600 so only option you'll probably need now is the ``-c'' option to locate the config file, and as this is also compiled in, you may not even need that. See the innfeed.conf(5) man page for more details on config file structure. 2. The backlog file handling is changed slightly: - The .output file is always kept open (until rotation time). - The .output file is allowed to grow for at least 30 seconds (or the value defined by the key backlog-rotate-period in the config file). This prevents thrashing of backlog files. - The hand-prepared file is checked for only every 600 seconds maximum (or the value defined by the key backlog-new), not every time the files are rotated. - The stating of the three backlog files is reduced dramatically. 3. Signal handling is changed so that they are more synchronous with other activity. This should stop the frequent core-dumps that occured when running in funnel file mode and sending SIGTERM or SIGALRM. 4. A bug related to zero-length articles was fixed. They will now be logged. 5. More information is in the innfeed.status file, including the reasons return by the remote when it is throttled. 6. SIGEMT is now a trigger for closing and reopening all the backlog files. If you have scripts that need to fool with the backlogs, then have the scripts move the backlogs out of the way and then send the SIGEMT. Changes for 0.9.3 1. If your system supports mmap() *and* if you have your articles stored on disk in NNTP-ready format (rare), then you can have innfeed mmap article data to save on memory (thanks to Dave Lawrence). There is an important issue with this: if you try to have innfeed handle too many articles (by running many connections and/or high max-check values in innfeed.conf) at once, then your system (not your process) may run out of free vnodes (global file descriptors), as a vnode is used as long as the file is mmaped. So be careful. If your articles are not in NNTP format then this will be noticed and the article will be pulled into memory for fixing up (and then immediately munmap'd). You can disable use of MMAP if you've built it in by using the '-M' flag. I tried mixing mmap'ing and articles not in NNTP format and it was a real performance loss. I'll be trying it differently later. 2. If innfeed is asked to send an article to a host it knows nothing about, or which it cannot acquire the required lock for (which causes the "ME locked cannot setup peer ..." and "ME unconfigured peer" syslog messages), then innfeed will deposit the article information into a file matching the pattern innfeed-dropped.* in the backlog directory (TAPE_DIRECTORY in config.h). This file will not be processed in any manner -- it's up to you to decide what to do with it (wait for innfeed to exit before doing anything with it, or send innfeed a SIGHUP to get it to reread its config file, which will roll this file). 4. The output backlog files will now be kept below a certain byte limit. This happens via the ``-e'' option. If, after writing to an output file, the new length is bigger than the given limit (multiplied by a fudge factor defined in config.h -- default of 1.10) then the file will be shrunk down to this size (or slightly smaller to find the end of line boundary). The front of the file will be removed to do this. This means lost articles for the entries removed. 3. A SIGHUP will make the config be reloaded. 4. The .checkpoint files have been dropped in favour of scribbling the offset into the input file itself. 5. When the process exits normally a final syslog entry covering all of the peers over the life of the process is written. It looks like: Jan 12 15:51:53 data innfeed.tester[24189]: ME global seconds 2472 offered 43820 accepted 10506 refused 31168 rejected 1773 missing 39 6. SIGALARM now rolls the input file, rather than the log file. This is useful in funnel file mode when you move the input file and tell innd to flush it, then send innfeed the signal. 7. The location of the pid file, config file and status file, can now be relative, in which case they're relative to the backlog directory. 8. stdin stdout and stderr are initialized properly when innfeed is started by a process that has closed them. 9. Various values in config.h have changed (paths to look more like values used in inn 1.5 and others to support point #7 above more easily.) 10. procbatch.pl can now 'require' innshellvars.pl that comes with 1.5. The default is not to. You nead to do a one line tweak if you want it to. The defaults in procbatch.pl match the new defaults of innfeed. 11. Core files that get generated on purpose will be done so in CORE_DIRECTORY (as defined in config.h), if that is defined to a pathname. If CORE_DIRECTORY is defined to be NULL (the default now), then the core will be generated in the backlog directory (as possibly modified by the '-b' option). Changes for 0.9.2 1. Now includes David Lawrence's patches to handle funnel files. 2. EAGAIN errors on read and writes are caught and dealt with (of interest to Solaris `victims'). 3. It is now much faster at servicing the file descriptor attached to innd. This means it is faster at recognising it has been flushed and at dropping connections. This means fewer conflicts with new innfeeds starting before the old one has finished up. It is still a good net-citizen and it finishes the commands already started, so the fast response is only as fast as your slowest peer, but it no longer tries to send everything it had queued internally, and locks get released much quicker. 4. Includes Michael Hucka's patch to make the innfeed.status output neater. 5. Includes Andy Vasilyev's HTML-in-innfeed.status patch (but you have to enable it in config.h). 6. Added a '-a' (top of news spool) and a '-p' (pid file path) option. Changes for 0.9 1. Format of innfeed.conf file changed slightly (for per-peer streaming specs). 2. Including Greg Patten's innlog.pl (taken from ftp://loose.apana.org.au/pub/local/innlog/innlog.pl) 3. Added Christophe Wolfhugel's patch to permit a per-peer restriction on using streaming. 4. More robust handling of peers that return bad responses (no long just calling abort). Changes for 0.8.5 1. Massive syslog messages cleanup courtesy of kre. 2. The innlog.awk-patch hash been dropped from the distribution until the new syslog messages are dealt with. 3. State machine more robust in the face of unexpected responses from remote. Connection gets torn down and bad response's logged. 4. The fixed timers (article reception timeout, read timeout, and flush timeout) are all adjusted by up to +/-10% so that things aren't quite so synchronised. 5. The innfeed.status file has been expanded and reformatted to include more information. Changes for 0.8.4 1. A change in the handing off of articles to connections in order to encourage connections that were opened due to activity spikes, to close down sooner. 2. The backlog files are no longer concatenated together at process startup, but the .input is simply used if it exists, and if not then the hand-dropped file is used first and the .output file second. 3. The innfeed.status is no longer updated by a innfeed that is in its death-throws. 4. Specifically catch the 480 response code from NNRPD when we try to give it an IHAVE. 5. The connection reestablishment time gets properly increased when the connection fails to go through (up to and including the reading of the banner message). 6. Bug fix that occasionally had articles sit in a connection and never get processed. 7. Bug fix in the counter of number of sleeping connections. 8. Bug fix in config file parsing. 9. Procbatch.pl included. Changes for version 0.8.1 1. various bug fixes. 2. core files generated by ASSERT are (possibly) put in a seperate directory to ease debugging are Changes for version 0.8 1. The implicit state machine in the Connection objects has been made explicit. 2. Various bug fixes. Changes for version 0.7.1 1. Pulled the source to inet_addr.c from the bind distribution. (Solaris and some others don't have it). Changes for version 0.7 1. The backlog file mechanism has been completely reworked. There are now only two backlog files: one for output and on for input. The output file becomes the input file when the input file is exhausted. 2. Much less strenuous use of writev. Solaris and other sv4r machines have an amazingly low value for the maximum number of iovecs that can be passed into writev. 3. Proper connection cleanup (QUIT issued) at shutdown. 4. A lock is taken out in the backlog directory for each peer. To feed the same peer from two different instances of innfeed (with a batch file for example), then you must use another directory. 5. Creating a file in the backlog directory with the same name as the peer, the that file will be used next time backlog files are processed. Its format must be: pathname msgid where pathname is absolute, or relative to the top of the news spool. 6. More command line options. 7. Dynamic peer creation. If the proper command line option is used (-y) and innfeed is to told to feed a peer that it doesn't have in its config file, then it will create a new binding to the new peer. The ip name must be the same as the peername, i.e. if innd tells innfeed about a peer fooBarBat, then gethostbyname("fooBarBat") better work. 8. Connections will be periodically torn down (1 hour is the default), even if they're active, so that non-innd peers don't have problems with their history files being kept open for too long. 9. The input backlog files are checkpointed every 30 seconds so that a crash while processing a large backlog doesn't require starting over from the beginning. Changes for version 0.6 1. Logging of spooling of backlog only happens once per stats-logging period. Bugs/Problems/Notes etc: 1. There is no graceful handling of file descriptor exhaustion. 2. If the following situation occurs: - articles on disk are NOT in NNTP-ready format. - innfeed was built with HAVE_MMAP defined. - memory usage is higher than expected try running innfeed with the '-M' flag (or recompiling with HAVE_MMAP undefined). Solaris, and possibly other SVR4 machines, waste a lot of swap space. 3. On the stats logging the 'offered' may not equal the sum of the other fields. This is because the stats at that moment were generated while waiting for a response to a command to come back. Innfeed considers an article ``offered'' when it sends the command, not when it gets a response back. Perhaps this should change. 4. If all the Connections for a peer are idle and a new backlog file is dropped in by hand, then it will not be picked up until the next time it gets an article from innd for that peer. This will be fixed in a later version, but for now, if the peer is likely to be idle for a long time, then flush the process. 5. Adding a backlog file by hand does not cause extra Connections to be automatically created, only the existing Connections will use the file. If the extra load requires new Connections to be built when innd delivers new articles for tranmission, then they too will use the file, but this a side effect and not a direct consequence. This means if you want to run in '-x' mode, then make sure your config file entry states the correct number of initial connections, as they're all the Connections that will be created. 6. If '-x' is used and the config file has an entry for a peer that has no batch file to process, then innfeed will not exit after all batch files have been finished--it will just site there idle. 7. If the remote is running inn and only has you in the nnrp.access file, then innfeed will end up talking to nnrpd. Innfeed will try every 30 seconds to reconnect to a server that will accept IHAVE commands. i.e. there is no exponential back of retry attempt. This is because the connection is considered good once the MODE STREAM command has been accepted or rejected (and nnrpd rejects it). Futures: 1. Innfeed will eventually take exploder commands. 2. The config file will be revamped to allow for more global options etc and run-time configuration. Too much is compile-time dependant at the moment. 3. The connection retry time will get more sophisticated to catch problems like the nnrpd issue mentioned above. 4. Include the number of takesthis/check/ihave commands issued in the log entries. 5. Heaps more stuff requested that's buried in my mail folders. Any praise, complaints, requests, porting issues etc. should go to me: brister@vix.com. Many thanks to the following people for extra help (above and beyond the call of duty) with pateches, beta testing and/or suggestions: Christophe Wolfhugel Robert Elz Russell Vincent Paul Vixie Stephen Stuart John T. Stapleton Alan Barrett Lee McLoughlin Dan Ellis Katsuhiro Kondou Marc G. Fournier Steven Bauer Richard Perini Per Hedeland Clayton O'Neill Dave Pascoe Michael Handler Petr Lampa David Lawrence Don Lewis Landon Curt Noll If I've forgotten anybody, please let me know. Thanks also to the ISC for sponsoring this work. innfeed-0.10.1.7.orig/article.h0100644000175100001440000001005206331417053014422 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 10:09:28 1995 * Project: INN (innfeed) * File: article.h * RCSId: $Id: article.h,v 1.1.1.1 1997/04/29 16:13:31 scrappy Exp $ * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * Description: The public interface to articles. The articles are * implemented via reference counting. This interface * provides the methods for getting the contents of the article. * * When an Article is created there's a chance that another * copy of it already exists. For example if the Article is * pulled out of a Tape for a particular host it may already * be in existance in some other host. This class will manage * this situation to prevent multiple copies of the article * being in core. */ #if ! defined ( article_h__ ) #define article_h__ #include #include "misc.h" /* Create a new Article object. FILENAME is the path of the file the */ /* article is in. MSGID is the news message id of the article */ Article newArticle (const char *filename, const char *msgid) ; /* delete the given article. Just decrements refcount and then FREEs if the refcount is 0. */ void delArticle (Article article) ; void gPrintArticleInfo (FILE *fp, u_int indentAmt) ; /* print some debugging info about the article. */ void printArticleInfo (Article art, FILE *fp, u_int indentAmt) ; /* return true if this article's file still exists. */ bool artFileIsValid (Article article) ; /* return true if we have the article's contents (calling this may trigger the reading off the disk). */ bool artContentsOk (Article article) ; /* increments reference count and returns a copy of article that can be kept (or passed off to someone else) */ Article artTakeRef (Article article) ; /* return the pathname of the file the article is in. */ const char *artFileName (Article article) ; /* return a list of buffers suitable for giving to an endpoint. The return value can (must) be given to freeBufferArray */ Buffer *artGetNntpBuffers (Article article) ; /* return the message id stoed in the article object */ const char *artMsgId (Article article) ; /* return the number of buffers that artGetNntpBuffers() would return. */ u_int artNntpBufferCount (Article article) ; /* tell the Article class to log (or not) missing articles as they occur. */ void artLogMissingArticles (bool val) ; /* if VAL is true, then when an article is read off disk the necesary carriage returns are inserted instead of setting up iovec-style buffers for writev. Useful for systems like solaris that have very small max number of iovecs that writev can take. Must be called only once before the first article is created. */ void artBitFiddleContents (bool val) ; /* set the limit on the number of bytes in all articles (this is not a hard limit). Can only be called one time before any articles are created. */ void artSetMaxBytesInUse (u_int val) ; #endif /* article_h__ */ innfeed-0.10.1.7.orig/buffer.c0100644000175100001440000002767706340727312014272 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 14:25:35 1995 * Project: INN (innfeed) * File: buffer.c * RCSId: $Id: buffer.c,v 1.3 1997/05/22 02:26:18 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The implementation of the Buffer class. Buffers are * reference counted objects that abstract memory regions in a * way similar to 'struct iovec'. * */ #if ! defined (lint) static const char *rcsid = "$Id: buffer.c,v 1.3 1997/05/22 02:26:18 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #include #include #include #include #include "buffer.h" static Buffer gBufferList = NULL ; static Buffer bufferPool = NULL ; static u_int bufferCount = 0 ; static u_int bufferByteCount = 0 ; struct buffer_s { int refCount ; char *mem ; size_t memSize ; /* the length of mem */ size_t dataSize ; /* amount that has actual data in it. */ bool deletable ; struct buffer_s *next ; struct buffer_s *prev ; }; #define BUFFER_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (struct buffer_s))) static void fillBufferPool (void) { int i ; bufferPool = ALLOC (struct buffer_s, BUFFER_POOL_SIZE) ; ASSERT (bufferPool != NULL) ; for (i = 0; i < BUFFER_POOL_SIZE - 1; i++) bufferPool[i] . next = &(bufferPool [i + 1]) ; bufferPool [BUFFER_POOL_SIZE-1] . next = NULL ; } Buffer newBuffer (size_t size) { Buffer nb ; if (bufferPool == NULL) fillBufferPool() ; nb = bufferPool; ASSERT (nb != NULL) ; bufferPool = bufferPool->next ; nb->refCount = 1 ; nb->mem = MALLOC (size + 1) ; ASSERT (nb->mem != NULL) ; nb->mem [size] = '\0' ; nb->memSize = size ; nb->dataSize = 0 ; nb->deletable = true ; bufferByteCount += size + 1 ; bufferCount++ ; nb->next = gBufferList ; nb->prev = NULL; if (gBufferList != NULL) gBufferList->prev = nb ; gBufferList = nb ; #if 0 dprintf (1,"Creating a DELETABLE buffer %p\n",nb) ; #endif return nb ; } Buffer newBufferByCharP (const char *ptr, size_t size, size_t dataSize) { Buffer nb ; if (bufferPool == NULL) fillBufferPool() ; nb = bufferPool; ASSERT (nb != NULL) ; bufferPool = bufferPool->next ; nb->refCount = 1 ; nb->mem = (char *) ptr ; /* cast away const */ nb->memSize = size ; nb->dataSize = dataSize ; nb->deletable = false ; nb->next = gBufferList ; nb->prev = NULL; if (gBufferList != NULL) gBufferList->prev = nb ; gBufferList = nb ; bufferCount++ ; #if 0 dprintf (1,"Creating a NON-DELETABLE buffer %p\n",nb) ; #endif return nb ; } void delBuffer (Buffer buff) { if (buff != NULL && --(buff->refCount) == 0) { #if 0 dprintf (1,"Freeing a %s buffer (%p)\n", (buff->deletable ? "DELETABLE" : "NON-DELETABLE"), buff) ; #endif bufferCount-- ; if (buff->deletable) { bufferByteCount -= (buff->memSize + 1) ; FREE (buff->mem) ; buff->mem = NULL ; } if (buff->next != NULL) buff->next->prev = buff->prev ; if (buff->prev != NULL) buff->prev->next = buff->next ; else { ASSERT(gBufferList == buff) ; gBufferList = buff->next ; } buff->next = bufferPool ; bufferPool = buff ; } } Buffer bufferTakeRef (Buffer buff) { ASSERT (buff != NULL) ; if (buff != NULL) buff->refCount++ ; return buff ; } void gPrintBufferInfo (FILE *fp, u_int indentAmt) { Buffer b ; char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Buffer List : (count %d) {\n",indent,bufferCount) ; for (b = gBufferList ; b != NULL ; b = b->next) printBufferInfo (b,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printBufferInfo (Buffer buffer, FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; char bufferStart [256] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sBuffer : %p {\n",indent,buffer) ; if (buffer == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } i = MIN(sizeof (bufferStart) - 1,buffer->dataSize) ; strncpy (bufferStart,buffer->mem,i) ; bufferStart [i] = '\0' ; fprintf (fp,"%s refcount : %d\n",indent,buffer->refCount) ; fprintf (fp,"%s data-size : %ld\n",indent,(long) buffer->dataSize) ; fprintf (fp,"%s mem-size : %ld\n",indent,(long) buffer->memSize) ; fprintf (fp,"%s base : %p\n", indent,(void *) buffer->mem) ; fprintf (fp,"%s deletable : %s\n",indent,boolToString(buffer->deletable)); fprintf (fp,"%s buffer [0:%ld] : \"%s\"\n", indent, (long) i, bufferStart) ; fprintf (fp,"%s}\n",indent) ; } void *bufferBase (Buffer buff) { return buff->mem ; } size_t bufferSize (Buffer buff) { return buff->memSize ; } size_t bufferDataSize (Buffer buff) { return buff->dataSize ; } void bufferIncrDataSize (Buffer buff, size_t size) { if (buff->dataSize + size > buff->memSize) die ("Trying to make a buffer data size bigger than its memory alloc"); else buff->dataSize += size ; } void bufferDecrDataSize (Buffer buff, size_t size) { ASSERT (size > buff->dataSize) ; buff->dataSize -= size ; } void bufferSetDataSize (Buffer buff, size_t size) { buff->dataSize = size ; ASSERT (buff->dataSize <= buff->memSize) ; } void freeBufferArray (Buffer *buffs) { Buffer *b = buffs ; while (b && *b) { delBuffer (*b) ; b++ ; } if (buffs) FREE (buffs) ; } /* Allocate an array and put all the arguments (the last of which must be NULL) into it. The terminating NULL is put in the returned array. */ Buffer *makeBufferArray (Buffer buff, ...) { va_list ap ; size_t cLen = 10, idx = 0 ; Buffer *ptr, p ; ptr = CALLOC (Buffer, cLen) ; ASSERT (ptr != NULL) ; ptr [idx++] = buff ; va_start (ap, buff) ; do { p = va_arg (ap, Buffer) ; if (idx == cLen) { cLen += 10 ; ptr = REALLOC (ptr,Buffer,cLen) ; ASSERT (ptr != NULL) ; } ptr [idx++] = p ; } while (p != NULL) ; va_end (ap) ; return ptr ; } bool isDeletable (Buffer buff) { return buff->deletable ; } /* Dups the array including taking out references on the Buffers inside */ Buffer *dupBufferArray (Buffer *array) { Buffer *newArr ; int count = 0 ; while (array && array [count] != NULL) count++ ; newArr = ALLOC (Buffer, count + 1) ; ASSERT (newArr != NULL) ; for (count = 0 ; array [count] != NULL ; count++) newArr [count] = bufferTakeRef (array [count]) ; newArr [count] = NULL ; return newArr ; } u_int bufferArrayLen (Buffer *array) { u_int count = 0 ; if (array != NULL) while (*array != NULL) { count++ ; array++ ; } return count ; } bool copyBuffer (Buffer dest, Buffer src) { char *baseDest = bufferBase (dest) ; char *baseSrc = bufferBase (src) ; u_int amt = bufferDataSize (src) ; if (amt > bufferSize (dest)) return false ; memcpy (baseDest, baseSrc, amt) ; bufferSetDataSize (dest,amt) ; return true ; } u_int bufferRefCount (Buffer buf) { return buf->refCount ; } void bufferAddNullByte (Buffer buff) { char *p = bufferBase (buff) ; p [buff->dataSize] = '\0' ; } /* append the src buffer to the dest buffer growing the dest as needed. Can only be done to deletable buffers. */ bool concatBuffer (Buffer dest, Buffer src) { ASSERT (dest->deletable) ; if ( !dest->deletable ) return false ; /* yeah, i know this is taken care of above */ if ((dest->dataSize + src->dataSize) > dest->memSize) { char *newMem = MALLOC (dest->dataSize + src->dataSize + 1) ; ASSERT (newMem != NULL) ; bufferByteCount += ((dest->dataSize + src->dataSize) - dest->memSize) ; memset (newMem, 0, dest->dataSize + src->dataSize + 1) ; memcpy (newMem, dest->mem, dest->dataSize) ; ASSERT (dest->mem != NULL) ; FREE (dest->mem) ; dest->mem = newMem ; dest->memSize = dest->dataSize + src->dataSize ; /* yep. 1 less */ } memcpy (&dest->mem[dest->dataSize], src->mem, dest->dataSize) ; dest->dataSize += src->dataSize ; return true ; } /* realloc the buffer's memory to increase the size by AMT */ bool expandBuffer (Buffer buff, size_t amt) { dprintf (2,"Expanding buffer....\n") ; if (!buff->deletable) return false ; bufferByteCount += amt ; buff->memSize += amt ; buff->mem = REMALLOC (buff->mem, buff->memSize + 1) ; ASSERT (buff->mem != NULL) ; return true ; } /* Take a buffer and shift the contents around to add the necessary CR before every line feed and a '.' before every '.' at the start of a line. */ bool nntpPrepareBuffer (Buffer buffer) { int msize, newDsize, dsize, extra ; char *base, p, *src, *dst ; bool needfinal = false ; ASSERT (buffer != NULL) ; dsize = buffer->dataSize ; msize = buffer->memSize - 1 ; base = buffer->mem ; extra = 0 ; p = '\0' ; for (src = base + dsize - 1 ; src > base ; ) { if (*src == '\n') { extra++ ; if (p == '.') extra++ ; } p = *src-- ; } if (*src == '\n') { extra++ ; if (p == '.') extra++ ; } if (dsize > 0 && base [dsize - 1] != '\n') { needfinal = true ; extra += 2 ; } newDsize = dsize + extra ; if (msize - dsize < extra) { dprintf (2,"Expanding buffer in nntpPrepareBuffer (from %d to %d)\n", msize, msize + (extra - (msize - dsize))) ; if ( !expandBuffer (buffer, extra - (msize - dsize)) ) { dprintf (1,"Expand failed...\n") ; return false ; } ASSERT (dsize == (int) buffer->dataSize) ; base = buffer->mem ; } base [newDsize] = '\0' ; if (needfinal) { base [newDsize - 1] = '\n' ; base [newDsize - 2] = '\r' ; newDsize -= 2 ; extra -= 2 ; } if (extra) { p = '\0'; src = base + dsize - 1 ; dst = base + newDsize - 1 ; while (1) { if (*src == '\n') { if (p == '.') { *dst-- = '.' ; extra-- ; } *dst-- = '\n' ; *dst = '\r' ; if (--extra <= 0) break ; p = '\0' ; dst-- ; src-- ; } else p = *dst-- = *src-- ; } ASSERT(dst >= base && src >= base) ; } if (needfinal) newDsize += 2 ; bufferSetDataSize (buffer,newDsize) ; return true ; } innfeed-0.10.1.7.orig/buffer.h0100644000175100001440000001266706331417053014266 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 11:02:04 1995 * Project: INN (innfeed) * File: buffer.h * RCSId: $Id: buffer.h,v 1.1.1.1 1997/04/29 16:13:31 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The public interface to the Buffer class. * * The Buffer class encapsulates a region of memory. It * provides reference counting so that redundant * memory allocation and copying is minimized. A good * example of this is the need to write the same data * (e.g. an article body) out more than one socket. The * data is stored in a Buffer and the Buffer is given * to each EndPoint that is to write it. With the * Refcount appropriately set the EndPoints can release * their references at will and the Buffer will be * cleaned up when necessary. */ #if ! defined ( buffer_h__ ) #define buffer_h__ #include #include #include "misc.h" /* * Create a new Buffer object and initialize it. */ Buffer newBuffer (size_t size) ; /* Create a new Buffer object around the preallocted PTR, which is SIZE bytes long. The data size of the Buffer is set to DATASIZE. When then buffer is released it will not delete the ptr (this is useful to have Buffers around contant strings, or Buffers around other Buffers) */ Buffer newBufferByCharP (const char *ptr, size_t size, size_t dataSize) ; /* * give up interest in the Buffer. Decrement refcount and delete if no * more referants */ void delBuffer (Buffer buff) ; /* print some debugging information about the buffer. */ void printBufferInfo (Buffer buffer, FILE *fp, u_int indentAmt) ; /* print debugging information about all outstanding buffers. */ void gPrintBufferInfo (FILE *fp, u_int indentAmt) ; /* increment reference counts so that the buffer object can be */ /* handed off to another function without it being deleted when that */ /* function calls bufferDelete(). Returns buff, so the call can be */ /* used in function arg list. */ Buffer bufferTakeRef (Buffer buff) ; /* returns the address of the base of the memory owned by the Buffer */ void *bufferBase (Buffer buff) ; /* returns the size of the memory buffer has available. This always returns 1 less than the real size so that there's space left for a null byte when needed. The extra byte is accounted for when the newBuffer function is called. */ size_t bufferSize (Buffer) ; /* return the amount of data actually in the buffer */ size_t bufferDataSize (Buffer buff) ; /* increment the size of the buffer's data region */ void bufferIncrDataSize (Buffer buff, size_t size) ; /* decrement the size of the buffer's data region */ void bufferDecrDataSize (Buffer buff, size_t size) ; /* set the size of the data actually in the buffer */ void bufferSetDataSize (Buffer buff, size_t size) ; /* walk down the BUFFS releasing each buffer and then freeing BUFFS itself */ void freeBufferArray (Buffer *buffs) ; /* All arguments are non-NULL Buffers, except for the last which must be NULL. Creates a free'able array and puts all the buffers into it (does not call bufferTakeRef on them). */ Buffer *makeBufferArray (Buffer buff, ...) ; /* returns true if the buffer was created via newBuffer (vs. newBufferByCharP) */ bool isDeletable (Buffer buff) ; /* Dups the array including taking out references on the Buffers inside. Return value must be freed (or given to freeBufferArray) */ Buffer *dupBufferArray (Buffer *array) ; /* return the number of non-NULL elements in the array. */ u_int bufferArrayLen (Buffer *array) ; /* copies the contents of the SRC buffer into the DEST buffer and sets the data size appropriately. Returns false if src is bigger than dest. */ bool copyBuffer (Buffer dest, Buffer src) ; /* return the ref count on the buffer */ u_int bufferRefCount (Buffer buff) ; /* insert a null byte at the end of the data region */ void bufferAddNullByte (Buffer buff) ; /* append the data of src to the data of dest, if dest is big enough */ bool concatBuffer (Buffer dest, Buffer src) ; /* expand the buffer's memory by the given AMT */ bool expandBuffer (Buffer buff, size_t amt) ; /* Expand the buffer (if necessary) and insert carriage returns before very line feed. Adjusts the data size. The base address may change afterwards. */ bool nntpPrepareBuffer (Buffer buffer) ; #endif /* buffer_h__ */ innfeed-0.10.1.7.orig/config.h0100644000175100001440000002064706364205155014263 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 08:34:23 1995 * Project: INN (innfeed) * File: config.h * RCSId: $Id: config.h,v 1.3 1997/07/19 18:41:49 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The application configuration values. This file is #include'd * before any system header files, so it can't rely on any CPP * symbols other that what the compiler defines. * */ #if ! defined ( config_h__ ) #define config_h__ /* Go edit sysconfig.h for platform differences */ #include "sysconfig.h" /**********************************************************************/ /* Application specific defines */ /**********************************************************************/ /* the path to the run-time config file. If relative, then relative to TAPE_DIRECTORY (or the value of the ``-b'' option). Overridden by ``-c'' option. */ #define CONFIG_FILE "/var/news/etc/innfeed.conf" /* * This next section contains things than can be overriden in the config * file. The strings inside each comment is the key used in the * innfeed.conf file to override the value here. See innfeed.conf for a * description of each./ */ /* in tape.c */ #define TAPE_DIRECTORY "/var/news/spool/innfeed" /* backlog-directory */ #define TAPE_HIGHWATER 5 /* backlog-highwater */ #define TAPE_ROTATE_PERIOD 60 /* backlog-rotate-period */ #define TAPE_CHECKPOINT_PERIOD 30 /* backlog-ckpt-period */ #define TAPE_NEWFILE_PERIOD 600 /* backlog-newfile-period */ #define TAPE_DISABLE 0 /* no-backlog */ /* in main.c */ #define NEWSSPOOL "/var/news/spool/articles" /* news-spool */ #define PID_FILE "innfeed.pid" /* pid-file */ #define LOG_FILE "/var/log/news/innfeed.log" /* log-file */ /* in host.c */ #define DNS_RETRY_PERIOD 900 /* dns-retry */ #define DNS_EXPIRE_PERIOD 86400 /* dns-expire */ #define CLOSE_PERIOD (60 * 60) /* close-period */ #define GEN_HTML 0 /* gen-html */ #define INNFEED_STATUS "innfeed.status" /* status-file */ #define LOG_CONNECTION_STATS 0 /* connection-stats */ #define HOST_HIGHWATER 10 /* host-highwater */ #define STATS_PERIOD (60 * 10) /* stats-period */ #define STATS_RESET_PERIOD (60 * 60 * 12) /* stats-reset-period */ #define ARTTOUT 600 /* article-timeout */ #define RESPTOUT 300 /* response-timeout */ #define INIT_CXNS 1 /* initial-connections */ #define MAX_CXNS 2 /* max-connections */ #define MAX_Q_SIZE 5 /* max-queue-size */ #define STREAM 1 /* streaming */ #define NOCHECKHIGH 95.0 /* no-check-high */ #define NOCHECKLOW 90.0 /* no-check-low */ #define PORTNUM 119 /* port-number */ #define BLOGLIMIT 0 /* backlog-limit */ #define LIMIT_FUDGE 1.10 /* backlog-factor */ #define BLOGLIMIT_HIGH 0 /* backlog-limit-high */ #define INIT_RECON_PER 30 /* initial-reconnect-time */ #define MAX_RECON_PER (60 * 60 * 1)/* max-reconnect-time */ /****************************************************************************/ /* * The rest below are not run-time configurable. */ /* If this file exists at startup then it's the same as having done '-d 1' on the command line. This is a cheap way of avoiding continual reloading of the newsfeeds file when debugging. */ #define DEBUG_FILE "/var/log/news/innfeed.debug" /* if defined to a non-zero number, then a snapshot will be printed whenever die() is called (e.g. on assert failure). This can use up a lot of disk space. */ #define SNAPSHOT_ON_DIE 0 /* the full pathname of the file to get a printed dump of the system when a SIGINT is delivered (or SNAPSHOT_ON_DIE is non-zero--see below). */ #define SNAPSHOT_FILE "/var/tmp/innfeed.snapshot" /* Define this be an existing directory (or NULL). If innfeed deliberatly dumps core it will chdir() to this directory first (if non-NULL). If NULL then it will chdir to TAPE_DIRECTORY (as possibly modified by the '-b' option). */ #define CORE_DIRECTORY NULL /* strings that get added to the end of a peer name for generating backlog file names. A peername cannot end in any of these string (e.g. having a peer called 'mypeer.input' will not work) */ #define OUTPUT_TAIL ".output" #define INPUT_TAIL ".input" #define LOCK_TAIL ".lock" /* rough estimate of average article line length (including headers). Smaller number means more efficient article preparation (for transfer), but, if much smaller than reality, then more memory wastage. */ #define CHARS_PER_LINE 60 /* How many seconds between logging statistics on article allocation. For no logging set to 0 */ #define ARTICLE_STATS_PERIOD (10 * 60) /* 10 minutes */ /* max number of parallel connections to a single remote. This is just a sanity check for the runtime config file. */ #define MAX_CONNECTION_COUNT 50 /* default size in bytes for buffers */ #define BUFFER_SIZE 256 /* amount we expand buffers on partial reads */ #define BUFFER_EXPAND_AMOUNT 128 /* minimum number of seconds between log messages for starting spooling. i.e. if the connection bounces up and down this will prevent frequent logging of the spooling message. 0 turns off this logging. */ #define SPOOL_LOG_PERIOD 600 /* some big numbers just for sanity checking */ #define MAX_MAXCHECKS 10000 /* no more than 10000 articles at a time */ #define MAX_MAXART_TOUT 86400 /* one day max between articles from inn */ #define MAX_RESP_TOUT 3600 /* one hour max to wait for response */ /* the check / no-check filter value, i.e. roughly how many past articles we take into account whilst doing the average for check / no-check mode. Ensure it's a float. */ #define FILTERVALUE 50.0 /* the maximum number of peers we'll handle (not connections) */ #define MAX_HOSTS 100 /* We try to keep article memory allocation below this limit. Doesn't work very well, though. */ #define SOFT_ARTICLE_BYTE_LIMIT (1024 * 1024 * 10) /* 10MB */ /* define SELECT_RATIO to the number of times through the main loop before checking on the fd from inn again.... */ #define SELECT_RATIO 3 #if defined (DO_BIND_USE_SIZEOF) #define AF_UNIX_SOCKSIZE(S) (sizeof S) #else #define AF_UNIX_SOCKSIZE(S) (sizeof S.sun_family + strlen(S.sun_path) + 1) #endif /* defined(DO_BIND_USE_SIZEOF) */ #if ! defined (USE_DMALLOC) #undef ALLOC #define ALLOC(TYPE, COUNT) (TYPE *) malloc(sizeof(TYPE) * (COUNT)) #undef MALLOC #define MALLOC(SIZE) (char *) malloc(SIZE) #undef CALLOC #define CALLOC(TYPE, COUNT) (TYPE *) calloc((COUNT), sizeof(TYPE)) #undef REALLOC #define REALLOC(ptr, TYPE, COUNT) \ (TYPE *) realloc((char *)(ptr), (sizeof(TYPE) * (COUNT))) #undef REMALLOC #define REMALLOC(ptr, SIZE) (char *) realloc((char *)(ptr),(SIZE)) #undef FREE #define FREE(ptr) ((void) (ptr != NULL && (free((char *)(ptr)), ptr = NULL))) #else #include #endif /* !defined (USE_DMALLOC) */ #if defined (DBTIMES) /* some small values for testing things. */ #undef STATS_PERIOD #define STATS_PERIOD 30 /* 30 seconds */ #undef STATS_RESET_PERIOD #define STATS_RESET_PERIOD (6 * 60) /* 6 minutes */ #undef ARTICLE_STATS_PERIOD #define ARTICLE_STATS_PERIOD (6 * 60) /* 7 minutes */ #undef CLOSE_PERIOD #define CLOSE_PERIOD (3 * 60) /* 5 minutes */ #endif /* DBTIMES */ #if ! defined (__GNUC__) #define __attribute__(x) #endif #if defined (CHECK_FORMATS) extern void syslog (int, const char *,...) __attribute__ ((__format__ (printf, 2, 3))); #endif #endif /* config_h__ */ innfeed-0.10.1.7.orig/config_l.c0100644000175100001440000013144506331417055014566 0ustar mdusers/* A lexical scanner generated by flex */ /* Scanner skeleton version: * $Header: /usr/local/cvsroot/innfeed/config_l.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #include /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ #ifdef c_plusplus #ifndef __cplusplus #define __cplusplus #endif #endif #ifdef __cplusplus #include #include /* Use prototypes in function declarations. */ #define YY_USE_PROTOS /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_PROTOS #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef __TURBOC__ #pragma warn -rch #pragma warn -use #include #include #define YY_USE_CONST #define YY_USE_PROTOS #endif #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif #ifdef YY_USE_PROTOS #define YY_PROTO(proto) proto #else #define YY_PROTO(proto) () #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #define YY_BUF_SIZE 16384 typedef struct yy_buffer_state *YY_BUFFER_STATE; extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* The funky do-while in the following #define is used to turn the definition * int a single C statement (which needs a semi-colon terminator). This * avoids problems with code like: * * if ( condition_holds ) * yyless( 5 ); * else * do_something_else(); * * Prior to using the do-while the compiler would get upset at the * "else" because it interpreted the "if" statement as being all * done when it reached the ';' after the yyless() call. */ /* Return all but the first 'n' matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ *yy_cp = yy_hold_char; \ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yytext_ptr ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ typedef unsigned int yy_size_t; struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; static YY_BUFFER_STATE yy_current_buffer = 0; /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". */ #define YY_CURRENT_BUFFER yy_current_buffer /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart YY_PROTO(( FILE *input_file )); void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); void yy_load_buffer_state YY_PROTO(( void )); YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); #define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *str )); YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); static void *yy_flex_alloc YY_PROTO(( yy_size_t )); static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); static void yy_flex_free YY_PROTO(( void * )); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! yy_current_buffer ) \ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ yy_current_buffer->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! yy_current_buffer ) \ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ yy_current_buffer->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (yy_current_buffer->yy_at_bol) #define FLEX_DEBUG typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; #define FLEX_DEBUG extern char *yytext; #define yytext_ptr yytext static yy_state_type yy_get_previous_state YY_PROTO(( void )); static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); static int yy_get_next_buffer YY_PROTO(( void )); static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yy_c_buf_p = yy_cp; #define YY_NUM_RULES 19 #define YY_END_OF_BUFFER 20 static yyconst short int yy_accept[55] = { 0, 0, 0, 7, 7, 20, 18, 11, 1, 15, 10, 19, 16, 2, 18, 18, 3, 4, 18, 8, 7, 19, 18, 11, 15, 10, 0, 0, 17, 16, 18, 18, 18, 8, 7, 13, 0, 0, 17, 18, 18, 18, 0, 12, 18, 5, 18, 0, 9, 18, 14, 18, 18, 6, 0 } ; static yyconst int yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 5, 6, 1, 1, 7, 1, 1, 1, 1, 1, 8, 9, 1, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 1, 1, 1, 1, 1, 1, 1, 1, 12, 13, 14, 1, 15, 1, 16, 1, 1, 17, 1, 18, 19, 20, 1, 21, 1, 1, 22, 1, 1, 1, 1, 1, 1, 23, 1, 1, 1, 1, 24, 24, 1, 1, 25, 24, 15, 1, 1, 1, 1, 1, 1, 24, 19, 20, 1, 26, 1, 24, 27, 24, 1, 1, 1, 1, 28, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst int yy_meta[30] = { 0, 1, 2, 3, 4, 5, 1, 6, 1, 1, 7, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 1, 7, 1, 1, 1 } ; static yyconst short int yy_base[62] = { 0, 0, 100, 28, 30, 105, 0, 102, 107, 0, 0, 80, 25, 107, 15, 23, 0, 0, 86, 0, 99, 107, 0, 98, 0, 0, 92, 32, 88, 34, 78, 24, 78, 0, 87, 107, 78, 75, 65, 18, 25, 57, 54, 107, 43, 0, 45, 54, 0, 38, 107, 37, 33, 0, 107, 51, 58, 65, 72, 79, 86, 88 } ; static yyconst short int yy_def[62] = { 0, 54, 1, 55, 55, 54, 56, 54, 54, 57, 58, 59, 56, 54, 56, 56, 56, 56, 56, 60, 54, 54, 56, 54, 57, 58, 54, 61, 56, 56, 56, 56, 56, 60, 54, 54, 54, 54, 56, 56, 56, 56, 54, 54, 56, 56, 56, 54, 56, 56, 54, 56, 56, 56, 0, 54, 54, 54, 54, 54, 54, 54 } ; static yyconst short int yy_nxt[137] = { 0, 6, 7, 8, 9, 10, 6, 11, 12, 6, 12, 13, 6, 6, 6, 14, 6, 6, 6, 6, 15, 6, 6, 6, 6, 6, 6, 6, 16, 17, 20, 21, 20, 21, 28, 29, 30, 31, 40, 35, 44, 30, 36, 28, 29, 44, 45, 53, 31, 40, 52, 45, 19, 19, 19, 19, 19, 19, 19, 22, 51, 50, 49, 48, 47, 22, 24, 24, 24, 46, 24, 24, 24, 25, 25, 38, 25, 25, 25, 25, 26, 26, 43, 26, 26, 26, 26, 33, 42, 34, 33, 33, 33, 33, 37, 37, 41, 39, 38, 35, 23, 34, 32, 27, 23, 54, 18, 5, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 } ; static yyconst short int yy_chk[137] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 12, 12, 14, 15, 31, 27, 39, 14, 27, 29, 29, 39, 40, 52, 15, 31, 51, 40, 55, 55, 55, 55, 55, 55, 55, 56, 49, 47, 46, 44, 42, 56, 57, 57, 57, 41, 57, 57, 57, 58, 58, 38, 58, 58, 58, 58, 59, 59, 37, 59, 59, 59, 59, 60, 36, 34, 60, 60, 60, 60, 61, 61, 32, 30, 28, 26, 23, 20, 18, 11, 7, 5, 2, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int yy_flex_debug; int yy_flex_debug = 1; static yyconst short int yy_rule_linenum[19] = { 0, 65, 67, 69, 71, 73, 75, 77, 79, 124, 126, 128, 130, 143, 145, 148, 233, 235, 237 } ; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 char *yytext; #line 1 "configfile.l" #define INITIAL 0 #line 2 "configfile.l" /* -*- text -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Fri, 17 Jan 1997 17:08:37 +0100 * Project: INN (innfeed) * File: configfile.l * RCSId: $Id: config_l.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ * Description: A flex input file for the innfeed config file. * */ #include #include #include #include #include "configfile.h" #include "config_y.h" #include "config.h" #if ! defined (FLEX_SCANNER) #error "You must use FLEX to process the lex input file." #endif #if defined (FLEX_DEBUG) #define YY_USER_INIT yy_flex_debug = (getenv ("YYDEBUG") == NULL ? 0 : 1) #endif char *strPtr = 0 ; int strPtrLen = 0 ; int strIdx = 0 ; int sawBsl ; int lineCount = 0 ; int c ; static void strAppend (int ch); static void strAppend (int ch) { if (strIdx == strPtrLen) { if (strPtr == 0) strPtr = malloc (strPtrLen = 50) ; else strPtr = realloc (strPtr,strPtrLen += 10) ; } strPtr [strIdx++] = ch ; } #define MAX_INCLUDE_DEPTH 11 struct includeFile { YY_BUFFER_STATE state; char *name ; } include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; #define incl 1 #line 479 "lex.yy.c" /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap YY_PROTO(( void )); #else extern int yywrap YY_PROTO(( void )); #endif #endif #ifndef YY_NO_UNPUT static void yyunput YY_PROTO(( int c, char *buf_ptr )); #endif #ifndef yytext_ptr static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput YY_PROTO(( void )); #else static int input YY_PROTO(( void )); #endif #endif #if YY_STACK_USED static int yy_start_stack_ptr = 0; static int yy_start_stack_depth = 0; static int *yy_start_stack = 0; #ifndef YY_NO_PUSH_STATE static void yy_push_state YY_PROTO(( int new_state )); #endif #ifndef YY_NO_POP_STATE static void yy_pop_state YY_PROTO(( void )); #endif #ifndef YY_NO_TOP_STATE static int yy_top_state YY_PROTO(( void )); #endif #else #define YY_NO_PUSH_STATE 1 #define YY_NO_POP_STATE 1 #define YY_NO_TOP_STATE 1 #endif #ifdef YY_MALLOC_DECL YY_MALLOC_DECL #else #if __STDC__ #ifndef __cplusplus #include #endif #else /* Just try to get by without declaring the routines. This will fail * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) * or sizeof(void*) != sizeof(int). */ #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( yy_current_buffer->yy_is_interactive ) \ { \ int c = '*', n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL int yylex YY_PROTO(( void )) #endif /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ if ( yyleng > 0 ) \ yy_current_buffer->yy_at_bol = \ (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 63 "configfile.l" #line 632 "lex.yy.c" if ( yy_init ) { yy_init = 0; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yy_start ) yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! yy_current_buffer ) yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_load_buffer_state(); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yy_c_buf_p; /* Support of yytext. */ *yy_cp = yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yy_start; yy_current_state += YY_AT_BOL(); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 107 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yy_last_accepting_cpos; yy_current_state = yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ if ( yy_flex_debug ) { if ( yy_act == 0 ) fprintf( stderr, "--scanner backing up\n" ); else if ( yy_act < 19 ) fprintf( stderr, "--accepting rule at line %d (\"%s\")\n", yy_rule_linenum[yy_act], yytext ); else if ( yy_act == 19 ) fprintf( stderr, "--accepting default rule (\"%s\")\n", yytext ); else if ( yy_act == 20 ) fprintf( stderr, "--(end of buffer or a NUL)\n" ); else fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); } switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yy_hold_char; yy_cp = yy_last_accepting_cpos; yy_current_state = yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 65 "configfile.l" lineCount++ ; YY_BREAK case 2: YY_RULE_SETUP #line 67 "configfile.l" { return (COLON) ; } YY_BREAK case 3: YY_RULE_SETUP #line 69 "configfile.l" { return (LBRACE) ; } YY_BREAK case 4: YY_RULE_SETUP #line 71 "configfile.l" { return (RBRACE) ; } YY_BREAK case 5: YY_RULE_SETUP #line 73 "configfile.l" { return (PEER) ; } YY_BREAK case 6: YY_RULE_SETUP #line 75 "configfile.l" BEGIN(incl); YY_BREAK case 7: YY_RULE_SETUP #line 77 "configfile.l" /* eat the whitespace before include filename */ YY_BREAK case 8: YY_RULE_SETUP #line 79 "configfile.l" { if (include_stack_ptr == MAX_INCLUDE_DEPTH - 1) { int i ; fprintf( stderr, "Includes nested too deeply:\n" ); for (i = 1 ; i <= include_stack_ptr ; i++) fprintf (stderr,"\t%s\n",include_stack[i].name) ; syslog (LOG_ERR, "includes nested to deeply") ; exit( 1 ); } if ((yyin = fopen(yytext,"r")) == NULL) { syslog (LOG_CRIT,"include file fopen failed: %s %s", yytext,strerror(errno)); fprintf (stderr,"include file fopen failed: %s %s\n", yytext,strerror(errno)); exit (1) ; } else { fprintf (stderr,"Including (%d) from %s\n", include_stack_ptr + 1,yytext) ; syslog (LOG_NOTICE,"Including (%d) from %s\n", include_stack_ptr + 1, yytext) ; include_stack[include_stack_ptr].state = YY_CURRENT_BUFFER; include_stack[++include_stack_ptr].name = strdup (yytext) ; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); } BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(incl): #line 113 "configfile.l" { if ( include_stack_ptr <= 0 ) yyterminate(); else { free (include_stack[include_stack_ptr].name) ; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[--include_stack_ptr].state); } } YY_BREAK case 9: YY_RULE_SETUP #line 124 "configfile.l" { return (GROUP) ; } YY_BREAK case 10: YY_RULE_SETUP #line 126 "configfile.l" { (void) 0 ; } YY_BREAK case 11: YY_RULE_SETUP #line 128 "configfile.l" { (void) 1 ; } YY_BREAK case 12: YY_RULE_SETUP #line 130 "configfile.l" { switch (yytext[2]) { case '\\': yylval.chr = '\\' ; break ; case 'a': yylval.chr = 007 ; break ; case 'b': yylval.chr = 010 ; break ; case 'f': yylval.chr = 014 ; break ; case 'n': yylval.chr = 012 ; break ; case 'r': yylval.chr = 015 ; break ; case 't': yylval.chr = 011 ; break ; case 'v': yylval.chr = 013 ; break ; } return (CHAR) ; } YY_BREAK case 13: YY_RULE_SETUP #line 143 "configfile.l" { yylval.chr = yytext[1] ; return (CHAR) ; } YY_BREAK case 14: YY_RULE_SETUP #line 145 "configfile.l" { yylval.chr = (char)strtol(&yytext[2], (char **)NULL, 8); return (CHAR) ;} YY_BREAK case 15: YY_RULE_SETUP #line 148 "configfile.l" {{ int i ; for (i = 1, strIdx = 0, sawBsl = 0 ; ; i++) { if (i < yyleng) c = yytext [i] ; else c = input() ; if (c != EOF) { switch (c) { case '\\': if (sawBsl) { strAppend (c) ; sawBsl = 0 ; } else sawBsl = 1 ; break ; case '\n': if (!sawBsl) strAppend(c) ; sawBsl = 0 ; lineCount++ ; break ; case '\"': if (sawBsl) { strAppend (c) ; sawBsl = 0 ; } else { strAppend ('\0') ; yylval.string = strPtr ; strPtr = 0 ; strPtrLen = strIdx = 0 ; return (STRING) ; } break ; case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': if (sawBsl) { switch (c) { case 'a': strAppend (007) ; break ; case 'b': strAppend (010) ; break ; case 'f': strAppend (014) ; break ; case 'n': strAppend (012) ; break ; case 'r': strAppend (015) ; break ; case 't': strAppend (011) ; break ; case 'v': strAppend (013) ; break ; } sawBsl = 0 ; } else strAppend (c) ; break ; default: strAppend (c) ; sawBsl = 0 ; break ; } } else { return (STRING) ; } } }} YY_BREAK case 16: YY_RULE_SETUP #line 233 "configfile.l" { yylval.integer = atoi (yytext) ; return (IVAL) ; } YY_BREAK case 17: YY_RULE_SETUP #line 235 "configfile.l" { yylval.real = atof (yytext) ; return (RVAL) ; } YY_BREAK case 18: YY_RULE_SETUP #line 237 "configfile.l" { yylval.name = strdup (yytext) ; if (strcasecmp (yylval.name,"false") == 0) return (FALSEBVAL) ; else if (strcasecmp (yylval.name,"true") == 0) return (TRUEBVAL) ; else return (WORD) ; } YY_BREAK case 19: YY_RULE_SETUP #line 247 "configfile.l" ECHO; YY_BREAK #line 975 "lex.yy.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yy_hold_char; if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between yy_current_buffer and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yy_n_chars = yy_current_buffer->yy_n_chars; yy_current_buffer->yy_input_file = yyin; yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { yy_did_buffer_switch_on_eof = 0; if ( yywrap() ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yy_c_buf_p = &yy_current_buffer->yy_ch_buf[yy_n_chars]; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer() { register char *dest = yy_current_buffer->yy_ch_buf; register char *source = yytext_ptr; register int number_to_move, i; int ret_val; if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( yy_current_buffer->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a singled characater, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ yy_n_chars = 0; else { int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ #ifdef YY_USES_REJECT YY_FATAL_ERROR( "input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); #else /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = yy_current_buffer; int yy_c_buf_p_offset = (int) (yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yy_flex_realloc( (void *) b->yy_ch_buf, b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; #endif } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read ); } if ( yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; yy_current_buffer->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yy_n_chars += number_to_move; yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state() { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = yy_start; yy_current_state += YY_AT_BOL(); for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ #ifdef YY_USE_PROTOS static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) #else static yy_state_type yy_try_NUL_trans( yy_current_state ) yy_state_type yy_current_state; #endif { register int yy_is_jam; register char *yy_cp = yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 55 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 54); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #ifdef YY_USE_PROTOS static void yyunput( int c, register char *yy_bp ) #else static void yyunput( c, yy_bp ) int c; register char *yy_bp; #endif { register char *yy_cp = yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yy_hold_char; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = yy_n_chars + 2; register char *dest = &yy_current_buffer->yy_ch_buf[ yy_current_buffer->yy_buf_size + 2]; register char *source = &yy_current_buffer->yy_ch_buf[number_to_move]; while ( source > yy_current_buffer->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); yy_n_chars = yy_current_buffer->yy_buf_size; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; yytext_ptr = yy_bp; yy_hold_char = *yy_cp; yy_c_buf_p = yy_cp; } #endif /* ifndef YY_NO_UNPUT */ #ifdef __cplusplus static int yyinput() #else static int input() #endif { int c; *yy_c_buf_p = yy_hold_char; if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* This was really a NUL. */ *yy_c_buf_p = '\0'; else { /* need more input */ yytext_ptr = yy_c_buf_p; ++yy_c_buf_p; switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { if ( yywrap() ) { yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; return EOF; } if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; break; case EOB_ACT_LAST_MATCH: #ifdef __cplusplus YY_FATAL_ERROR( "unexpected last match in yyinput()" ); #else YY_FATAL_ERROR( "unexpected last match in input()" ); #endif } } } c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ *yy_c_buf_p = '\0'; /* preserve yytext */ yy_hold_char = *++yy_c_buf_p; yy_current_buffer->yy_at_bol = (c == '\n'); return c; } #ifdef YY_USE_PROTOS void yyrestart( FILE *input_file ) #else void yyrestart( input_file ) FILE *input_file; #endif { if ( ! yy_current_buffer ) yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_init_buffer( yy_current_buffer, input_file ); yy_load_buffer_state(); } #ifdef YY_USE_PROTOS void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) #else void yy_switch_to_buffer( new_buffer ) YY_BUFFER_STATE new_buffer; #endif { if ( yy_current_buffer == new_buffer ) return; if ( yy_current_buffer ) { /* Flush out information for old buffer. */ *yy_c_buf_p = yy_hold_char; yy_current_buffer->yy_buf_pos = yy_c_buf_p; yy_current_buffer->yy_n_chars = yy_n_chars; } yy_current_buffer = new_buffer; yy_load_buffer_state(); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yy_did_buffer_switch_on_eof = 1; } #ifdef YY_USE_PROTOS void yy_load_buffer_state( void ) #else void yy_load_buffer_state() #endif { yy_n_chars = yy_current_buffer->yy_n_chars; yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; yyin = yy_current_buffer->yy_input_file; yy_hold_char = *yy_c_buf_p; } #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) #else YY_BUFFER_STATE yy_create_buffer( file, size ) FILE *file; int size; #endif { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file ); return b; } #ifdef YY_USE_PROTOS void yy_delete_buffer( YY_BUFFER_STATE b ) #else void yy_delete_buffer( b ) YY_BUFFER_STATE b; #endif { if ( ! b ) return; if ( b == yy_current_buffer ) yy_current_buffer = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yy_flex_free( (void *) b->yy_ch_buf ); yy_flex_free( (void *) b ); } #ifndef YY_ALWAYS_INTERACTIVE #ifndef YY_NEVER_INTERACTIVE extern int isatty YY_PROTO(( int )); #endif #endif #ifdef YY_USE_PROTOS void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) #else void yy_init_buffer( b, file ) YY_BUFFER_STATE b; FILE *file; #endif { yy_flush_buffer( b ); b->yy_input_file = file; b->yy_fill_buffer = 1; #if YY_ALWAYS_INTERACTIVE b->yy_is_interactive = 1; #else #if YY_NEVER_INTERACTIVE b->yy_is_interactive = 0; #else b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; #endif #endif } #ifdef YY_USE_PROTOS void yy_flush_buffer( YY_BUFFER_STATE b ) #else void yy_flush_buffer( b ) YY_BUFFER_STATE b; #endif { b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == yy_current_buffer ) yy_load_buffer_state(); } #ifndef YY_NO_SCAN_BUFFER #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) #else YY_BUFFER_STATE yy_scan_buffer( base, size ) char *base; yy_size_t size; #endif { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b ); return b; } #endif #ifndef YY_NO_SCAN_STRING #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_string( yyconst char *str ) #else YY_BUFFER_STATE yy_scan_string( str ) yyconst char *str; #endif { int len; for ( len = 0; str[len]; ++len ) ; return yy_scan_bytes( str, len ); } #endif #ifndef YY_NO_SCAN_BYTES #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) #else YY_BUFFER_STATE yy_scan_bytes( bytes, len ) yyconst char *bytes; int len; #endif { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = len + 2; buf = (char *) yy_flex_alloc( n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < len; ++i ) buf[i] = bytes[i]; buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #endif #ifndef YY_NO_PUSH_STATE #ifdef YY_USE_PROTOS static void yy_push_state( int new_state ) #else static void yy_push_state( new_state ) int new_state; #endif { if ( yy_start_stack_ptr >= yy_start_stack_depth ) { yy_size_t new_size; yy_start_stack_depth += YY_START_STACK_INCR; new_size = yy_start_stack_depth * sizeof( int ); if ( ! yy_start_stack ) yy_start_stack = (int *) yy_flex_alloc( new_size ); else yy_start_stack = (int *) yy_flex_realloc( (void *) yy_start_stack, new_size ); if ( ! yy_start_stack ) YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); } yy_start_stack[yy_start_stack_ptr++] = YY_START; BEGIN(new_state); } #endif #ifndef YY_NO_POP_STATE static void yy_pop_state() { if ( --yy_start_stack_ptr < 0 ) YY_FATAL_ERROR( "start-condition stack underflow" ); BEGIN(yy_start_stack[yy_start_stack_ptr]); } #endif #ifndef YY_NO_TOP_STATE static int yy_top_state() { return yy_start_stack[yy_start_stack_ptr - 1]; } #endif #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif #ifdef YY_USE_PROTOS static void yy_fatal_error( yyconst char msg[] ) #else static void yy_fatal_error( msg ) char msg[]; #endif { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ yytext[yyleng] = yy_hold_char; \ yy_c_buf_p = yytext + n - YY_MORE_ADJ; \ yy_hold_char = *yy_c_buf_p; \ *yy_c_buf_p = '\0'; \ yyleng = n; \ } \ while ( 0 ) /* Internal utility routines. */ #ifndef yytext_ptr #ifdef YY_USE_PROTOS static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) #else static void yy_flex_strncpy( s1, s2, n ) char *s1; yyconst char *s2; int n; #endif { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_USE_PROTOS static void *yy_flex_alloc( yy_size_t size ) #else static void *yy_flex_alloc( size ) yy_size_t size; #endif { return (void *) malloc( size ); } #ifdef YY_USE_PROTOS static void *yy_flex_realloc( void *ptr, yy_size_t size ) #else static void *yy_flex_realloc( ptr, size ) void *ptr; yy_size_t size; #endif { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } #ifdef YY_USE_PROTOS static void yy_flex_free( void *ptr ) #else static void yy_flex_free( ptr ) void *ptr; #endif { free( ptr ); } #if YY_MAIN int main() { yylex(); return 0; } #endif #line 247 "configfile.l" innfeed-0.10.1.7.orig/config_y.c0100644000175100001440000010157206331417055014601 0ustar mdusers#ifndef lint static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93 (BSDI)"; #endif #include #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYEMPTY (-1) #define YYLEX yylex() #define yyclearin (yychar=YYEMPTY) #define yyerrok (yyerrflag=0) #define YYRECOVERING (yyerrflag!=0) #define YYPREFIX "yy" #line 2 "configfile.y" /* -*- text -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Fri, 17 Jan 1997 16:09:10 +0100 * Project: INN (innfeed) * File: config.y * RCSId: $Id: config_y.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ * Description: * */ #include #include #include #include #include #include "config.h" #include "configfile.h" #include "msgs.h" #include "misc.h" #define UNKNOWN_SCOPE_TYPE "line %d: unknown scope type: %s" #define SYNTAX_ERROR "line %d: syntax error" extern int lineCount ; scope *topScope = NULL ; static scope *currScope = NULL ; char *errbuff = NULL ; static void appendName (scope *s, char *p) ; static char *valueScopedName (value *v) ; static void freeValue (value *v) ; static char *checkName (scope *s, const char *name) ; static void addValue (scope *s, value *v) ; static char *addScope (scope *s, const char *name, scope *val) ; static void printScope (FILE *fp, scope *s, int indent) ; static void printValue (FILE *fp, value *v, int indent) ; static scope *newScope (const char *type) ; #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) ; #endif #if 0 int isString (scope *s, const char *name, int inherit) { value *v = findValue (s,name,inherit) ; return (v != NULL && v->type == stringval) ; } #endif int getBool (scope *s, const char *name, int *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != boolval) return 0 ; *rval = v->v.bool_val ; return 1 ; } int getString (scope *s, const char *name, char **rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != stringval) return 0 ; *rval = strdup (v->v.charp_val) ; return 1 ; } int getReal (scope *s, const char *name, double *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != realval) return 0 ; *rval = v->v.real_val ; return 1 ; } int getInteger (scope *s, const char *name, long *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != intval) return 0 ; *rval = v->v.int_val ; return 1 ; } void freeScopeTree (scope *s) { int i ; if (s == NULL) return ; if (s->parent == NULL && s->me != NULL) { /* top level scope */ free (s->me->name) ; free (s->me) ; } for (i = 0 ; i < s->value_idx ; i++) if (s->values[i] != NULL) freeValue (s->values [i]) ; free (s->values) ; free (s->scope_type) ; s->parent = NULL ; s->values = NULL ; free (s) ; } char *addInteger (scope *s, const char *name, long val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = intval ; v->v.int_val = val ; addValue (s,v) ; return NULL ; } char *addChar (scope *s, const char *name, char val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = charval ; v->v.char_val = val ; addValue (s,v) ; return NULL ; } char *addBoolean (scope *s, const char *name, int val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = boolval ; v->v.bool_val = val ; addValue (s,v) ; return NULL ; } char *addReal (scope *s, const char *name, double val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = realval ; v->v.real_val = val ; addValue (s,v) ; return NULL ; } char *addString (scope *s, const char *name, const char *val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = stringval ; v->v.charp_val = strdup (val) ; addValue (s,v) ; return NULL ; } value *findValue (scope *s, const char *name, int inherit) { const char *p ; if (name == NULL || *name == '\0') return NULL ; if (*name == ':') return findValue (topScope,name + 1,0) ; else if (s == NULL) return findValue (topScope,name,0) ; else { int i ; if ((p = strchr (name,':')) == NULL) p = name + strlen (name) ; for (i = 0 ; i < s->value_idx ; i++) { if (strncmp (s->values[i]->name,name,p - name) == 0) { if (*p == '\0') /* last segment of name */ return s->values[i] ; else if (s->values[i]->type != scopeval) errbuff = strdup ("Component not a scope") ; else return findValue (s->values[i]->v.scope_val,p + 1,0) ; } } /* not in this scope. Go up if inheriting values and only if no ':' in name */ if (inherit && *p == '\0') return findValue (s->parent,name,inherit) ; } return NULL ; } /* find the scope that name belongs to. If mustExist is true then the name must be a fully scoped name of a value. relative scopes start at s. */ scope *findScope (scope *s, const char *name, int mustExist) { scope *p = NULL ; char *q ; int i ; if ((q = strchr (name,':')) == NULL) { if (!mustExist) p = s ; else for (i = 0 ; p == NULL && i < s->value_idx ; i++) if (strcmp (s->values[i]->name,name) == 0) p = s ; return p ; } else if (*name == ':') { while (s->parent != NULL) s = s->parent ; return findScope (s,name + 1,mustExist) ; } else { for (i = 0 ; i < s->value_idx ; i++) if (strncmp (s->values[i]->name,name,q - name) == 0) if (s->values[i]->type == scopeval) return findScope (s->values[i]->v.scope_val,q + 1,mustExist) ; } return NULL ; } /****************************************************************************/ /* */ /****************************************************************************/ static void appendName (scope *s, char *p) { if (s == NULL) return ; else { appendName (s->parent,p) ; strcat (p,s->me->name) ; strcat (p,":") ; } } static char *valueScopedName (value *v) { scope *p = v->myscope ; int len = strlen (v->name) ; char *q ; while (p != NULL) { len += strlen (p->me->name) + 1 ; p = p->parent ; } q = malloc (len + 1) ; q [0] = '\0' ; appendName (v->myscope,q) ; strcat (q,v->name) ; return q ; } static void freeValue (value *v) { free (v->name) ; switch (v->type) { case scopeval: freeScopeTree (v->v.scope_val) ; break ; case stringval: free (v->v.charp_val) ; break ; default: break ; } free (v) ; } static char *checkName (scope *s, const char *name) { int i ; char *error = NULL ; if (s == NULL) return NULL ; for (i = 0 ; i < s->value_idx ; i++) { char *n = NULL ; if (strcmp (name,s->values [i]->name) == 0) { #define FMT "Two definitions of %s" n = valueScopedName (s->values[i]) ; error = malloc (strlen (FMT) + strlen (n) + 2) ; sprintf (error,FMT,n) ; free (n) ; return error ; } } return error ; } static void addValue (scope *s, value *v) { v->myscope = s ; if (s == NULL) return ; if (s->value_count == s->value_idx) { if (s->values == 0) { s->values = (value **) calloc (10,sizeof (value *)) ; s->value_count = 10 ; } else { s->value_count += 10 ; s->values = (value **) realloc (s->values, sizeof (value *) * s->value_count); } } s->values [s->value_idx++] = v ; } static char *addScope (scope *s, const char *name, scope *val) { value *v ; char *error ; if ((error = checkName (s,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = scopeval ; v->v.scope_val = val ; val->me = v ; val->parent = s ; addValue (s,v) ; currScope = val ; return NULL ; } static void printScope (FILE *fp, scope *s, int indent) { int i ; for (i = 0 ; i < s->value_idx ; i++) printValue (fp,s->values [i],indent + 5) ; } static void printValue (FILE *fp, value *v, int indent) { int i ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; switch (v->type) { case intval: fprintf (fp,"%s : %ld # INTEGER\n",v->name,v->v.int_val) ; break ; case stringval: fprintf (fp,"%s : \"",v->name) ; { char *p = v->v.charp_val ; while (*p) { if (*p == '"' || *p == '\\') fputc ('\\',fp) ; fputc (*p,fp) ; p++ ; } } fprintf (fp,"\" # STRING\n") ; break ; case charval: fprintf (fp,"%s : %c",v->name,047) ; switch (v->v.char_val) { case '\\': fprintf (fp,"\\\\") ; break ; default: if (isprint (v->v.char_val)) fprintf (fp,"%c",v->v.char_val) ; else fprintf (fp,"\\%03o",v->v.char_val) ; } fprintf (fp,"%c # CHARACTER\n",047) ; break ; case realval: fprintf (fp,"%s : %f # REAL\n",v->name,v->v.real_val) ; break ; case boolval: fprintf (fp,"%s : %s # BOOLEAN\n", v->name,(v->v.bool_val ? "true" : "false")) ; break ; case scopeval: fprintf (fp,"%s %s { # SCOPE\n",v->v.scope_val->scope_type,v->name) ; printScope (fp,v->v.scope_val,indent + 5) ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; fprintf (fp,"}\n") ; break ; default: fprintf (fp,"UNKNOWN value type: %d\n",v->type) ; exit (1) ; } } static scope *newScope (const char *type) { scope *t ; int i ; t = (scope *) calloc (1,sizeof (scope)) ; t->parent = NULL ; t->scope_type = strdup (type) ; for (i = 0 ; t->scope_type[i] != '\0' ; i++) t->scope_type[i] = tolower (t->scope_type[i]) ; return t ; } #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) { while (a && b && *a && *b && (tolower (*a) == tolower (*b)) && len > 0) a++, b++, len-- ; if (a == NULL && b == NULL) return 0 ; else if (a == NULL) return 1 ; else if (b == NULL) return -1 ; else if (*a == '\0' && *b == '\0') return 0 ; else if (*a == '\0') return 1 ; else if (*b == '\0') return -1 ; else if (*a < *b) return 1 ; else if (*a > *b) return -1 ; else return 0 ; abort () ; } #endif #define BAD_KEY "line %d: illegal key name: %s" #define NON_ALPHA "line %d: keys must start with a letter: %s" static char *keyOk (const char *key) { const char *p = key ; char *rval ; if (key == NULL) return strdup ("NULL key") ; else if (*key == '\0') return strdup ("EMPTY KEY") ; if (!isalpha(*p)) { rval = malloc (strlen (NON_ALPHA) + strlen (key) + 15) ; sprintf (rval,NON_ALPHA,lineCount, key) ; return rval ; } p++ ; while (*p) { if (!(isalnum (*p) || *p == '_' || *p == '-')) { rval = malloc (strlen (BAD_KEY) + strlen (key) + 15) ; sprintf (rval,BAD_KEY,lineCount,key) ; return rval ; } p++ ; } return NULL ; } static PFIVP *funcs = NULL ; static void **args = NULL ; static int funcCount ; static int funcIdx ; void configAddLoadCallback (PFIVP func,void *arg) { if (func == NULL) return ; if (funcIdx == funcCount) { funcCount += 10 ; if (funcs == NULL) { funcs = (PFIVP *) malloc (sizeof (PFIVP) * funcCount); args = (void **) malloc (sizeof (void *) * funcCount) ; } else { funcs = (PFIVP *) realloc (funcs,sizeof (PFIVP) * funcCount); args = (void **) realloc (args,sizeof (void *) * funcCount) ; } } args [funcIdx] = arg ; funcs [funcIdx++] = func ; } void configRemoveLoadCallback (PFIVP func) { int i, j ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] == func) break ; for (j = i ; j < funcIdx - 1 ; j++) { funcs [j] = funcs [j + 1] ; args [j] = args [j + 1] ; } if (funcIdx > 1 && i < funcIdx) { funcs [i - 2] = funcs [i - 1] ; args [i - 2] = args [i - 1] ; } if (funcIdx > 0 && i < funcIdx) funcIdx-- ; } static int doCallbacks (void) { int i ; int rval = 1 ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] != NULL) rval = (funcs[i](args [i]) && rval) ; return rval ; } static char *key ; #line 673 "configfile.y" typedef union{ scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; } YYSTYPE; #line 695 "y.tab.c" #define PEER 257 #define GROUP 258 #define IVAL 259 #define RVAL 260 #define NAME 261 #define STRING 262 #define SCOPE 263 #define COLON 264 #define LBRACE 265 #define RBRACE 266 #define TRUEBVAL 267 #define FALSEBVAL 268 #define CHAR 269 #define WORD 270 #define IP_ADDRESS 271 #define YYERRCODE 256 short yylhs[] = { -1, 2, 0, 3, 1, 1, 1, 5, 4, 6, 4, 4, 7, 4, 8, 8, 8, 8, 8, 8, 8, }; short yylen[] = { 2, 0, 2, 1, 0, 2, 2, 0, 6, 0, 6, 3, 0, 4, 1, 1, 1, 1, 1, 1, 1, }; short yydefred[] = { 1, 0, 4, 0, 6, 0, 0, 0, 5, 0, 0, 0, 0, 7, 9, 11, 0, 4, 4, 15, 18, 19, 16, 17, 20, 14, 13, 0, 0, 0, 8, 10, }; short yydgoto[] = { 1, 27, 2, 28, 8, 17, 18, 12, 26, }; short yysindex[] = { 0, 0, 0, -256, 0, -267, -266, -265, 0, -259, -254, -253, -255, 0, 0, 0, -252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -256, -247, -246, 0, 0, }; short yyrindex[] = { 0, 0, 0, 13, 0, 0, 0, -243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -244, 0, 0, 0, 0, }; short yygindex[] = { 0, 21, 0, 6, 0, 0, 0, 0, 0, }; #define YYTABLESIZE 24 short yytable[] = { 4, 5, 6, 9, 10, 11, 13, 19, 20, 16, 21, 14, 15, 2, 7, 22, 23, 24, 25, 30, 31, 12, 3, 3, 29, }; short yycheck[] = { 256, 257, 258, 270, 270, 270, 265, 259, 260, 264, 262, 265, 265, 0, 270, 267, 268, 269, 270, 266, 266, 264, 266, 2, 18, }; #define YYFINAL 1 #ifndef YYDEBUG #define YYDEBUG 1 #endif #define YYMAXTOKEN 271 #if YYDEBUG char *yyname[] = { "end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"PEER","GROUP","IVAL","RVAL", "NAME","STRING","SCOPE","COLON","LBRACE","RBRACE","TRUEBVAL","FALSEBVAL","CHAR", "WORD","IP_ADDRESS", }; char *yyrule[] = { "$accept : input", "$$1 :", "input : $$1 entries", "scope : entries", "entries :", "entries : entries entry", "entries : entries error", "$$2 :", "entry : PEER WORD LBRACE $$2 scope RBRACE", "$$3 :", "entry : GROUP WORD LBRACE $$3 scope RBRACE", "entry : WORD WORD LBRACE", "$$4 :", "entry : WORD $$4 COLON value", "value : WORD", "value : IVAL", "value : TRUEBVAL", "value : FALSEBVAL", "value : RVAL", "value : STRING", "value : CHAR", }; #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 int yydebug; int yynerrs; struct yystack { short *ssp; YYSTYPE *vsp; short *ss; YYSTYPE *vs; int stacksize; short *sslim; }; int yychar; /* some people use this, so we copy it in & out */ int yyerrflag; /* must be global for yyerrok & YYRECOVERING */ YYSTYPE yylval; #line 787 "configfile.y" int yyerror (const char *s) { #undef FMT #define FMT "line %d: %s" errbuff = malloc (strlen (s) + strlen (FMT) + 20) ; sprintf (errbuff,FMT,lineCount,s) ; return 0 ; } int yywrap (void) { return 1 ; } extern FILE *yyin ; extern int yydebug ; #define NO_INHERIT 0 #if ! defined (WANT_MAIN) struct peer_table_s { char *peerName ; value *peerValue ; } ; static struct peer_table_s *peerTable ; static int peerTableCount ; static int peerTableIdx ; void configCleanup (void) { int i ; for (i = 0 ; i < peerTableIdx ; i++) free (peerTable[i].peerName) ; free (peerTable) ; freeScopeTree (topScope); free (funcs) ; free (args) ; } int buildPeerTable (FILE *fp, scope *s) { int rval = 1 ; int i, j ; for (i = 0 ; i < s->value_idx ; i++) { if (ISSCOPE (s->values[i]) && ISPEER (s->values[i])) { for (j = 0 ; j < peerTableIdx ; j++) { if (strcmp (peerTable[j].peerName,s->values[i]->name) == 0) { logOrPrint (LOG_ERR,fp,DUP_PEER_NAME,peerTable[j].peerName) ; rval = 0 ; break ; } } if (j == peerTableIdx) { if (peerTableCount == peerTableIdx) { peerTableCount += 10 ; if (peerTable == NULL) peerTable = ALLOC(struct peer_table_s,peerTableCount) ; else peerTable = REALLOC (peerTable,struct peer_table_s, peerTableCount) ; } peerTable[peerTableIdx].peerName = strdup (s->values[i]->name); peerTable[peerTableIdx].peerValue = s->values[i] ; peerTableIdx++ ; } } else if (ISSCOPE (s->values[i])) rval = (buildPeerTable (fp,s->values[i]->v.scope_val) && rval) ; } return rval ; } /* read the config file. Any errors go to errorDest if it is non-NULL, otherwise they are syslogged. If justCheck is true then return after parsing */ static int inited = 0 ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) { scope *oldTop = topScope ; FILE *fp ; int rval ; if (!inited) { inited = 1 ; yydebug = (getenv ("YYDEBUG") == NULL ? 0 : 1) ; if (yydebug) atexit (configCleanup) ; } if (file == NULL || strlen (file) == NULL || !fileExistsP (file)) { logOrPrint (LOG_ERR,errorDest,NOSUCH_CONFIG, file ? file : "(null)") ; dprintf (1,"No such config file: %s\n", file ? file : "(null)") ; exit (1) ; } if ((fp = fopen (file,"r")) == NULL) { logOrPrint (LOG_ERR,errorDest,CFG_FOPEN_FAILURE, file) ; exit (1) ; } logOrPrint (LOG_NOTICE,errorDest,"loading %s", file) ; yyin = fp ; topScope = NULL ; rval = yyparse () ; fclose (fp) ; if (rval != 0) /* failure */ { freeScopeTree (topScope) ; if (justCheck) freeScopeTree (oldTop) ; else topScope = oldTop ; topScope = NULL ; if (errbuff != NULL) { if (errorDest != NULL) fprintf (errorDest,CONFIG_PARSE_FAILED,"",errbuff) ; else syslog (LOG_ERR,CONFIG_PARSE_FAILED,"ME ",errbuff) ; free (errbuff) ; } return 0 ; } if (dump) { fprintf (errorDest ? errorDest : stderr,"Parsed config file:\n") ; printScope (errorDest ? errorDest : stderr,topScope,-5) ; fprintf (errorDest ? errorDest : stderr,"\n") ; } if (justCheck) { freeScopeTree (topScope) ; freeScopeTree (oldTop) ; topScope = NULL ; } else { for (peerTableIdx-- ; peerTableIdx >= 0 ; peerTableIdx--) { free (peerTable [peerTableIdx].peerName) ; peerTable [peerTableIdx].peerName = NULL ; peerTable [peerTableIdx].peerValue = NULL ; } peerTableIdx = 0 ; if (!buildPeerTable (errorDest,topScope)) logAndExit (1,"Failed to build list of peers") ; } return 1 ; } value *getNextPeer (int *cookie) { value *rval ; if (*cookie < 0 || *cookie >= peerTableIdx) return NULL ; rval = peerTable[*cookie].peerValue ; (*cookie)++ ; return rval ; } value *findPeer (const char *name) { value *v = NULL ; int i ; for (i = 0 ; i < peerTableIdx ; i++) if (strcmp (peerTable[i].peerName,name) == 0) { v = peerTable[i].peerValue ; break ; } return v ; } #endif #if defined (WANT_MAIN) int main (int argc, char **argv) { if ( yyparse() ) printf ("parsing failed: %s\n",errbuff ? errbuff : "NONE") ; else { printScope (stdout,topScope,-5) ; if (argc == 3) { #if 0 printf ("Looking for %s of type %s: ",argv[2],argv[1]) ; if (strncmp (argv[1],"int",3) == 0) { int i = 0 ; if (!getInteger (topScope,argv[2],&i)) printf ("wasn't found.\n") ; else printf (" %d\n",i) ; } else if (strncmp (argv[1],"real",4) == 0) { double d = 0.0 ; if (!getReal (topScope,argv[2],&d)) printf ("wasn't found.\n") ; else printf (" %0.5f\n",d) ; } #else value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { long ival = 987654 ; if (getInteger (v->v.scope_val,argv[2],&ival,1)) printf ("Getting %s : %ld",argv[2],ival) ; else printf ("Name is not legal: %s\n",argv[2]) ; } #endif } else if (argc == 2) { #if 1 value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { printf ("Getting %s : ",argv[1]) ; printValue (stdout,v,0) ; } #else if (findScope (topScope,argv[1],1) == NULL) printf ("Can't find the scope of %s\n",argv[1]) ; #endif } } freeScopeTree (topScope) ; return 0 ; } #endif /* defined (WANT_MAIN) */ #line 1114 "y.tab.c" /* allocate initial stack */ #if defined(__STDC__) || defined(__cplusplus) static int yyinitstack(struct yystack *sp) #else static int yyinitstack(sp) struct yystack *sp; #endif { int newsize; short *newss; YYSTYPE *newvs; newsize = YYINITSTACKSIZE; newss = (short *)malloc(newsize * sizeof *newss); newvs = (YYSTYPE *)malloc(newsize * sizeof *newvs); sp->ss = sp->ssp = newss; sp->vs = sp->vsp = newvs; if (newss == NULL || newvs == NULL) return -1; sp->stacksize = newsize; sp->sslim = newss + newsize - 1; return 0; } /* double stack size, up to YYMAXDEPTH */ #if defined(__STDC__) || defined(__cplusplus) static int yygrowstack(struct yystack *sp) #else static int yygrowstack(sp) struct yystack *sp; #endif { int newsize, i; short *newss; YYSTYPE *newvs; if ((newsize = sp->stacksize) >= YYMAXDEPTH) return -1; if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; i = sp->ssp - sp->ss; if ((newss = (short *)realloc(sp->ss, newsize * sizeof *newss)) == NULL) return -1; sp->ss = newss; sp->ssp = newss + i; if ((newvs = (YYSTYPE *)realloc(sp->vs, newsize * sizeof *newvs)) == NULL) return -1; sp->vs = newvs; sp->vsp = newvs + i; sp->stacksize = newsize; sp->sslim = newss + newsize - 1; return 0; } #define YYFREESTACK(sp) { free((sp)->ss); free((sp)->vs); } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab int yyparse() { register int yym, yyn, yystate, yych; register YYSTYPE *yyvsp; YYSTYPE yyval; struct yystack yystk; #if YYDEBUG register char *yys; extern char *getenv(); if (yys = getenv("YYDEBUG")) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif yynerrs = 0; yyerrflag = 0; yychar = yych = YYEMPTY; if (yyinitstack(&yystk)) goto yyoverflow; *yystk.ssp = yystate = 0; yyloop: if (yyn = yydefred[yystate]) goto yyreduce; if (yych < 0) { if ((yych = YYLEX) < 0) yych = 0; yychar = yych; #if YYDEBUG if (yydebug) { yys = 0; if (yych <= YYMAXTOKEN) yys = yyname[yych]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yych, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yych) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yych) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) goto yyoverflow; *++yystk.ssp = yystate = yytable[yyn]; *++yystk.vsp = yylval; yychar = yych = YYEMPTY; if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yych) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yych) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #ifdef lint goto yynewerror; #endif yynewerror: yyerror("syntax error"); #ifdef lint goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yystk.ssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yystk.ssp, yytable[yyn]); #endif if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) goto yyoverflow; *++yystk.ssp = yystate = yytable[yyn]; *++yystk.vsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yystk.ssp); #endif if (yystk.ssp <= yystk.ss) goto yyabort; --yystk.ssp; --yystk.vsp; } } } else { if (yych == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yych <= YYMAXTOKEN) yys = yyname[yych]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yych, yys); } #endif yychar = yych = YYEMPTY; goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; yyvsp = yystk.vsp; /* for speed in code under switch() */ yyval = yyvsp[1-yym]; switch (yyn) { case 1: #line 700 "configfile.y" { lineCount = 1 ; addScope (NULL,"",newScope ("")) ; topScope = currScope ; } break; case 2: #line 704 "configfile.y" { if (!doCallbacks()) YYABORT ; } break; case 6: #line 710 "configfile.y" { errbuff = malloc (strlen(SYNTAX_ERROR) + 12) ; sprintf (errbuff,SYNTAX_ERROR,lineCount) ; YYABORT ; } break; case 7: #line 717 "configfile.y" { errbuff = addScope (currScope,yyvsp[-1].name,newScope ("peer")) ; free (yyvsp[-1].name) ; if (errbuff != NULL) YYABORT ; } break; case 8: #line 721 "configfile.y" { currScope = currScope->parent ; } break; case 9: #line 724 "configfile.y" { errbuff = addScope (currScope,yyvsp[-1].name,newScope ("group")) ; free (yyvsp[-1].name) ; if (errbuff != NULL) YYABORT ; } break; case 10: #line 728 "configfile.y" { currScope = currScope->parent ; } break; case 11: #line 731 "configfile.y" { errbuff = malloc (strlen(UNKNOWN_SCOPE_TYPE) + 15 + strlen (yyvsp[-2].name)) ; sprintf (errbuff,UNKNOWN_SCOPE_TYPE,lineCount,yyvsp[-2].name) ; free (yyvsp[-2].name) ; free (yyvsp[-1].name) ; YYABORT ; } break; case 12: #line 739 "configfile.y" { if ((errbuff = keyOk(yyvsp[0].name)) != NULL) YYABORT ; else key = yyvsp[0].name ; } break; case 14: #line 746 "configfile.y" { if ((errbuff = addString (currScope, key, yyvsp[0].name)) != NULL) YYABORT ; free (key) ; free (yyvsp[0].name) ; } break; case 15: #line 752 "configfile.y" { if ((errbuff = addInteger(currScope, key, yyvsp[0].integer)) != NULL) YYABORT; free (key) ; } break; case 16: #line 757 "configfile.y" { if ((errbuff = addBoolean (currScope, key, 1)) != NULL) YYABORT ; free (key) ; free (yyvsp[0].name) ; } break; case 17: #line 763 "configfile.y" { if ((errbuff = addBoolean (currScope, key, 0)) != NULL) YYABORT ; free (key) ; free (yyvsp[0].name) ; } break; case 18: #line 769 "configfile.y" { if ((errbuff = addReal (currScope, key, yyvsp[0].real)) != NULL) YYABORT ; free (key) ; } break; case 19: #line 774 "configfile.y" { if ((errbuff = addString (currScope, key, yyvsp[0].string)) != NULL) YYABORT; free (key) ; } break; case 20: #line 779 "configfile.y" { if ((errbuff = addChar (currScope, key, yyvsp[0].chr)) != NULL) YYABORT ; free (key) ; } break; #line 1436 "y.tab.c" } yystk.ssp -= yym; yystate = *yystk.ssp; yystk.vsp -= yym; yym = yylhs[yyn]; yych = yychar; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yystk.ssp = YYFINAL; *++yystk.vsp = yyval; if (yych < 0) { if ((yych = YYLEX) < 0) yych = 0; yychar = yych; #if YYDEBUG if (yydebug) { yys = 0; if (yych <= YYMAXTOKEN) yys = yyname[yych]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yych, yys); } #endif } if (yych == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yystk.ssp, yystate); #endif if (yystk.ssp >= yystk.sslim && yygrowstack(&yystk)) goto yyoverflow; *++yystk.ssp = yystate; *++yystk.vsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: YYFREESTACK(&yystk); return (1); yyaccept: YYFREESTACK(&yystk); return (0); } innfeed-0.10.1.7.orig/config_y.h0100644000175100001440000000072606331417055014605 0ustar mdusers#define YYEMPTY (-1) #define PEER 257 #define GROUP 258 #define IVAL 259 #define RVAL 260 #define NAME 261 #define STRING 262 #define SCOPE 263 #define COLON 264 #define LBRACE 265 #define RBRACE 266 #define TRUEBVAL 267 #define FALSEBVAL 268 #define CHAR 269 #define WORD 270 #define IP_ADDRESS 271 typedef union{ scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; } YYSTYPE; extern YYSTYPE yylval; innfeed-0.10.1.7.orig/configfile.h0100644000175100001440000000566206331417053015117 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Fri, 17 Jan 1997 17:35:59 +0100 * Project: INN (innfeed) * File: configfile.h * RCSId: $Id: configfile.h,v 1.1.1.1 1997/04/29 16:13:31 scrappy Exp $ * Description: * */ #if ! defined ( configfile_h__ ) #define configfile_h__ /* pointer to function taking void-star param and returning int. */ typedef int (*PFIVP)(void *) ; typedef enum { intval, charval, boolval, realval, stringval, scopeval } tag ; typedef struct _scope { struct _value *me ; char *scope_type ; int value_count ; int value_idx ; struct _value **values ; struct _scope *parent ; } scope ; typedef struct _value { char *name ; struct _scope *myscope ; tag type ; union { char *charp_val ; char char_val ; double real_val ; int bool_val ; long int_val ; struct _scope *scope_val ; } v ; } value ; extern scope *topScope ; extern char *errbuff ; int isWord (scope *s, const char *name, int inherit) ; int isName (scope *s, const char *name, int inherit) ; int getReal (scope *s, const char *name, double *rval, int inherit) ; int getInteger (scope *s, const char *name, long *rval, int inherit) ; int getBool (scope *s, const char *name, int *rval, int inherit) ; int getString (scope *s, const char *name, char **rval, int inherit) ; int getWord (scope *s, const char *name, char **rval, int inherit) ; void freeScopeTree (scope *s) ; char *addInteger (scope *s, const char *name, long val) ; char *addChar (scope *s, const char *name, char val) ; char *addBoolean (scope *s, const char *name, int val) ; char *addName (scope *s, const char *name, char *val) ; char *addWord (scope *s, const char *name, char *val) ; char *addReal (scope *s, const char *name, double val) ; char *addString (scope *s, const char *name, const char *val) ; scope *findScope (scope *s, const char *name, int mustExist) ; value *findValue (scope *s, const char *name, int inherit) ; value *findPeer (const char *name) ; value *getNextPeer (int *cookie) ; void configAddLoadCallback (PFIVP func,void *arg) ; void configRemoveLoadCallback (PFIVP func) ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) ; int buildPeerTable (FILE *fp, scope *currScope); void configCleanup (void) ; #define ARTICLE_TIMEOUT "article-timeout" #define BACKLOG_LIMIT "backlog-limit" #define INITIAL_CONNECTIONS "initial-connections" #define IP_NAME "ip-name" #define MAX_CONNECTIONS "max-connections" #define MAX_QUEUE_SIZE "max-queue-size" #define NO_CHECK_HIGH "no-check-high" #define NO_CHECK_LOW "no-check-low" #define PORT_NUMBER "port-number" #define RESP_TIMEOUT "response-timeout" #define STREAMING "streaming" #define ISPEER(V) (ISSCOPE(V) && strcmp ((V)->v.scope_val->scope_type,"peer") == 0) #define ISSCOPE(V) (V->type == scopeval) #define INHERIT 1 #define NO_INHERIT 0 #endif /* configfile_h__ */ innfeed-0.10.1.7.orig/configfile.l0100644000175100001440000001434106331711677015126 0ustar mdusers%{ /* -*- text -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Fri, 17 Jan 1997 17:08:37 +0100 * Project: INN (innfeed) * File: configfile.l * RCSId: $Id: configfile.l,v 1.2 1997/04/30 18:47:27 scrappy Exp $ * Description: A flex input file for the innfeed config file. * */ #include #include #include #include #include "configfile.h" #include "config_y.h" #include "config.h" #if ! defined (FLEX_SCANNER) #error "You must use FLEX to process the lex input file." #endif #if defined (FLEX_DEBUG) #define YY_USER_INIT yy_flex_debug = (getenv ("YYDEBUG") == NULL ? 0 : 1) #endif char *strPtr = 0 ; int strPtrLen = 0 ; int strIdx = 0 ; int sawBsl ; int lineCount = 0 ; int c ; static void strAppend (int ch); static void strAppend (int ch) { if (strIdx == strPtrLen) { if (strPtr == 0) strPtr = malloc (strPtrLen = 50) ; else strPtr = realloc (strPtr,strPtrLen += 10) ; } strPtr [strIdx++] = ch ; } #define MAX_INCLUDE_DEPTH 11 struct includeFile { YY_BUFFER_STATE state; char *name ; } include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; %} %x incl ID [a-zA-Z][-a-zA-Z0-9._/]+ %% \n lineCount++ ; ":" { return (COLON) ; } "{" { return (LBRACE) ; } "}" { return (RBRACE) ; } [pP][eE][eE][rR] { return (PEER) ; } ^"$INCLUDE" BEGIN(incl); [ \t]* /* eat the whitespace before include filename */ [^ \t\n]+ { if (include_stack_ptr == MAX_INCLUDE_DEPTH - 1) { int i ; fprintf( stderr, "Includes nested too deeply:\n" ); for (i = 1 ; i <= include_stack_ptr ; i++) fprintf (stderr,"\t%s\n",include_stack[i].name) ; syslog (LOG_ERR, "includes nested to deeply") ; exit( 1 ); } if ((yyin = fopen(yytext,"r")) == NULL) { syslog (LOG_CRIT,"include file fopen failed: %s %s", yytext,strerror(errno)); fprintf (stderr,"include file fopen failed: %s %s\n", yytext,strerror(errno)); exit (1) ; } else { fprintf (stderr,"Including (%d) from %s\n", include_stack_ptr + 1,yytext) ; syslog (LOG_NOTICE,"Including (%d) from %s\n", include_stack_ptr + 1, yytext) ; include_stack[include_stack_ptr].state = YY_CURRENT_BUFFER; include_stack[++include_stack_ptr].name = strdup (yytext) ; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); } BEGIN(INITIAL); } <> { if ( include_stack_ptr <= 0 ) yyterminate(); else { free (include_stack[include_stack_ptr].name) ; yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[--include_stack_ptr].state); } } [gG][rR][oO][uU][pP] { return (GROUP) ; } #[^\n]* { (void) 0 ; } [ \t]+ { (void) 1 ; } '\\[\\abfnrtv]' { switch (yytext[2]) { case '\\': yylval.chr = '\\' ; break ; case 'a': yylval.chr = 007 ; break ; case 'b': yylval.chr = 010 ; break ; case 'f': yylval.chr = 014 ; break ; case 'n': yylval.chr = 012 ; break ; case 'r': yylval.chr = 015 ; break ; case 't': yylval.chr = 011 ; break ; case 'v': yylval.chr = 013 ; break ; } return (CHAR) ; } '.' { yylval.chr = yytext[1] ; return (CHAR) ; } '\\[0-9][0-9][0-9]' { yylval.chr = (char)strtol(&yytext[2], (char **)NULL, 8); return (CHAR) ;} \"[^\"]* {{ int i ; for (i = 1, strIdx = 0, sawBsl = 0 ; ; i++) { if (i < yyleng) c = yytext [i] ; else c = input() ; if (c != EOF) { switch (c) { case '\\': if (sawBsl) { strAppend (c) ; sawBsl = 0 ; } else sawBsl = 1 ; break ; case '\n': if (!sawBsl) strAppend(c) ; sawBsl = 0 ; lineCount++ ; break ; case '\"': if (sawBsl) { strAppend (c) ; sawBsl = 0 ; } else { strAppend ('\0') ; yylval.string = strPtr ; strPtr = 0 ; strPtrLen = strIdx = 0 ; return (XSTRING) ; } break ; case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': if (sawBsl) { switch (c) { case 'a': strAppend (007) ; break ; case 'b': strAppend (010) ; break ; case 'f': strAppend (014) ; break ; case 'n': strAppend (012) ; break ; case 'r': strAppend (015) ; break ; case 't': strAppend (011) ; break ; case 'v': strAppend (013) ; break ; } sawBsl = 0 ; } else strAppend (c) ; break ; default: strAppend (c) ; sawBsl = 0 ; break ; } } else { return (XSTRING) ; } } }} [-0-9][0-9]* { yylval.integer = atoi (yytext) ; return (IVAL) ; } [-0-9][0-9]*\.[0-9]* { yylval.real = atof (yytext) ; return (RVAL) ; } [^#:\'\" \t\n]+ { yylval.name = strdup (yytext) ; if (strcasecmp (yylval.name,"false") == 0) return (FALSEBVAL) ; else if (strcasecmp (yylval.name,"true") == 0) return (TRUEBVAL) ; else return (WORD) ; } %% innfeed-0.10.1.7.orig/configfile.y0100644000175100001440000005357306364205157015152 0ustar mdusers%{ /* -*- text -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Fri, 17 Jan 1997 16:09:10 +0100 * Project: INN (innfeed) * File: config.y * RCSId: $Id: configfile.y,v 1.4 1997/07/19 18:41:51 scrappy Exp $ * Description: * */ #include #include #include #include #include #include "config.h" #include "configfile.h" #include "msgs.h" #include "misc.h" #define UNKNOWN_SCOPE_TYPE "line %d: unknown scope type: %s" #define SYNTAX_ERROR "line %d: syntax error" extern int lineCount ; scope *topScope = NULL ; static scope *currScope = NULL ; char *errbuff = NULL ; static void appendName (scope *s, char *p) ; static char *valueScopedName (value *v) ; static void freeValue (value *v) ; static char *checkName (scope *s, const char *name) ; static void addValue (scope *s, value *v) ; static char *addScope (scope *s, const char *name, scope *val) ; static void printScope (FILE *fp, scope *s, int indent) ; static void printValue (FILE *fp, value *v, int indent) ; static scope *newScope (const char *type) ; #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) ; #endif #if 0 int isString (scope *s, const char *name, int inherit) { value *v = findValue (s,name,inherit) ; return (v != NULL && v->type == stringval) ; } #endif int getBool (scope *s, const char *name, int *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != boolval) return 0 ; *rval = v->v.bool_val ; return 1 ; } int getString (scope *s, const char *name, char **rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != stringval) return 0 ; *rval = strdup (v->v.charp_val) ; return 1 ; } int getReal (scope *s, const char *name, double *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != realval) return 0 ; *rval = v->v.real_val ; return 1 ; } int getInteger (scope *s, const char *name, long *rval, int inherit) { value *v = findValue (s,name,inherit) ; if (v == NULL) return 0 ; else if (v->type != intval) return 0 ; *rval = v->v.int_val ; return 1 ; } void freeScopeTree (scope *s) { int i ; if (s == NULL) return ; if (s->parent == NULL && s->me != NULL) { /* top level scope */ free (s->me->name) ; free (s->me) ; } for (i = 0 ; i < s->value_idx ; i++) if (s->values[i] != NULL) freeValue (s->values [i]) ; free (s->values) ; free (s->scope_type) ; s->parent = NULL ; s->values = NULL ; free (s) ; } char *addInteger (scope *s, const char *name, long val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = intval ; v->v.int_val = val ; addValue (s,v) ; return NULL ; } char *addChar (scope *s, const char *name, char val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = charval ; v->v.char_val = val ; addValue (s,v) ; return NULL ; } char *addBoolean (scope *s, const char *name, int val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = boolval ; v->v.bool_val = val ; addValue (s,v) ; return NULL ; } char *addReal (scope *s, const char *name, double val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = realval ; v->v.real_val = val ; addValue (s,v) ; return NULL ; } char *addString (scope *s, const char *name, const char *val) { value *v ; char *error ; if ((error = checkName (currScope,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = stringval ; v->v.charp_val = strdup (val) ; addValue (s,v) ; return NULL ; } value *findValue (scope *s, const char *name, int inherit) { const char *p ; if (name == NULL || *name == '\0') return NULL ; if (*name == ':') return findValue (topScope,name + 1,0) ; else if (s == NULL) return findValue (topScope,name,0) ; else { int i ; if ((p = strchr (name,':')) == NULL) p = name + strlen (name) ; for (i = 0 ; i < s->value_idx ; i++) { if (strncmp (s->values[i]->name,name,p - name) == 0) { if (*p == '\0') /* last segment of name */ return s->values[i] ; else if (s->values[i]->type != scopeval) errbuff = strdup ("Component not a scope") ; else return findValue (s->values[i]->v.scope_val,p + 1,0) ; } } /* not in this scope. Go up if inheriting values and only if no ':' in name */ if (inherit && *p == '\0') return findValue (s->parent,name,inherit) ; } return NULL ; } /* find the scope that name belongs to. If mustExist is true then the name must be a fully scoped name of a value. relative scopes start at s. */ scope *findScope (scope *s, const char *name, int mustExist) { scope *p = NULL ; char *q ; int i ; if ((q = strchr (name,':')) == NULL) { if (!mustExist) p = s ; else for (i = 0 ; p == NULL && i < s->value_idx ; i++) if (strcmp (s->values[i]->name,name) == 0) p = s ; return p ; } else if (*name == ':') { while (s->parent != NULL) s = s->parent ; return findScope (s,name + 1,mustExist) ; } else { for (i = 0 ; i < s->value_idx ; i++) if (strncmp (s->values[i]->name,name,q - name) == 0) if (s->values[i]->type == scopeval) return findScope (s->values[i]->v.scope_val,q + 1,mustExist) ; } return NULL ; } /****************************************************************************/ /* */ /****************************************************************************/ static void appendName (scope *s, char *p) { if (s == NULL) return ; else { appendName (s->parent,p) ; strcat (p,s->me->name) ; strcat (p,":") ; } } static char *valueScopedName (value *v) { scope *p = v->myscope ; int len = strlen (v->name) ; char *q ; while (p != NULL) { len += strlen (p->me->name) + 1 ; p = p->parent ; } q = malloc (len + 1) ; q [0] = '\0' ; appendName (v->myscope,q) ; strcat (q,v->name) ; return q ; } static void freeValue (value *v) { free (v->name) ; switch (v->type) { case scopeval: freeScopeTree (v->v.scope_val) ; break ; case stringval: free (v->v.charp_val) ; break ; default: break ; } free (v) ; } static char *checkName (scope *s, const char *name) { int i ; char *error = NULL ; if (s == NULL) return NULL ; for (i = 0 ; i < s->value_idx ; i++) { char *n = NULL ; if (strcmp (name,s->values [i]->name) == 0) { #define FMT "Two definitions of %s" n = valueScopedName (s->values[i]) ; error = malloc (strlen (FMT) + strlen (n) + 2) ; sprintf (error,FMT,n) ; free (n) ; return error ; } } return error ; } static void addValue (scope *s, value *v) { v->myscope = s ; if (s == NULL) return ; if (s->value_count == s->value_idx) { if (s->values == 0) { s->values = (value **) calloc (10,sizeof (value *)) ; s->value_count = 10 ; } else { s->value_count += 10 ; s->values = (value **) realloc (s->values, sizeof (value *) * s->value_count); } } s->values [s->value_idx++] = v ; } static char *addScope (scope *s, const char *name, scope *val) { value *v ; char *error ; if ((error = checkName (s,name)) != NULL) return error ; v = (value *) calloc (1,sizeof (value)) ; v->name = strdup (name) ; v->type = scopeval ; v->v.scope_val = val ; val->me = v ; val->parent = s ; addValue (s,v) ; currScope = val ; return NULL ; } static void printScope (FILE *fp, scope *s, int indent) { int i ; for (i = 0 ; i < s->value_idx ; i++) printValue (fp,s->values [i],indent + 5) ; } static void printValue (FILE *fp, value *v, int indent) { int i ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; switch (v->type) { case intval: fprintf (fp,"%s : %ld # INTEGER\n",v->name,v->v.int_val) ; break ; case stringval: fprintf (fp,"%s : \"",v->name) ; { char *p = v->v.charp_val ; while (*p) { if (*p == '"' || *p == '\\') fputc ('\\',fp) ; fputc (*p,fp) ; p++ ; } } fprintf (fp,"\" # STRING\n") ; break ; case charval: fprintf (fp,"%s : %c",v->name,047) ; switch (v->v.char_val) { case '\\': fprintf (fp,"\\\\") ; break ; default: if (isprint (v->v.char_val)) fprintf (fp,"%c",v->v.char_val) ; else fprintf (fp,"\\%03o",v->v.char_val) ; } fprintf (fp,"%c # CHARACTER\n",047) ; break ; case realval: fprintf (fp,"%s : %f # REAL\n",v->name,v->v.real_val) ; break ; case boolval: fprintf (fp,"%s : %s # BOOLEAN\n", v->name,(v->v.bool_val ? "true" : "false")) ; break ; case scopeval: fprintf (fp,"%s %s { # SCOPE\n",v->v.scope_val->scope_type,v->name) ; printScope (fp,v->v.scope_val,indent + 5) ; for (i = 0 ; i < indent ; i++) fputc (' ',fp) ; fprintf (fp,"}\n") ; break ; default: fprintf (fp,"UNKNOWN value type: %d\n",v->type) ; exit (1) ; } } static scope *newScope (const char *type) { scope *t ; int i ; t = (scope *) calloc (1,sizeof (scope)) ; t->parent = NULL ; t->scope_type = strdup (type) ; for (i = 0 ; t->scope_type[i] != '\0' ; i++) t->scope_type[i] = tolower (t->scope_type[i]) ; return t ; } #if 0 static int strNCaseCmp (const char *a, const char *b, size_t len) { while (a && b && *a && *b && (tolower (*a) == tolower (*b)) && len > 0) a++, b++, len-- ; if (a == NULL && b == NULL) return 0 ; else if (a == NULL) return 1 ; else if (b == NULL) return -1 ; else if (*a == '\0' && *b == '\0') return 0 ; else if (*a == '\0') return 1 ; else if (*b == '\0') return -1 ; else if (*a < *b) return 1 ; else if (*a > *b) return -1 ; else return 0 ; abort () ; } #endif #define BAD_KEY "line %d: illegal key name: %s" #define NON_ALPHA "line %d: keys must start with a letter: %s" static char *keyOk (const char *key) { const char *p = key ; char *rval ; if (key == NULL) return strdup ("NULL key") ; else if (*key == '\0') return strdup ("EMPTY KEY") ; if (!isalpha(*p)) { rval = malloc (strlen (NON_ALPHA) + strlen (key) + 15) ; sprintf (rval,NON_ALPHA,lineCount, key) ; return rval ; } p++ ; while (*p) { if (!(isalnum (*p) || *p == '_' || *p == '-')) { rval = malloc (strlen (BAD_KEY) + strlen (key) + 15) ; sprintf (rval,BAD_KEY,lineCount,key) ; return rval ; } p++ ; } return NULL ; } static PFIVP *funcs = NULL ; static void **args = NULL ; static int funcCount ; static int funcIdx ; void configAddLoadCallback (PFIVP func,void *arg) { if (func == NULL) return ; if (funcIdx == funcCount) { funcCount += 10 ; if (funcs == NULL) { funcs = (PFIVP *) malloc (sizeof (PFIVP) * funcCount); args = (void **) malloc (sizeof (void *) * funcCount) ; } else { funcs = (PFIVP *) realloc (funcs,sizeof (PFIVP) * funcCount); args = (void **) realloc (args,sizeof (void *) * funcCount) ; } } args [funcIdx] = arg ; funcs [funcIdx++] = func ; } void configRemoveLoadCallback (PFIVP func) { int i, j ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] == func) break ; for (j = i ; j < funcIdx - 1 ; j++) { funcs [j] = funcs [j + 1] ; args [j] = args [j + 1] ; } if (funcIdx > 1 && i < funcIdx) { funcs [i - 2] = funcs [i - 1] ; args [i - 2] = args [i - 1] ; } if (funcIdx > 0 && i < funcIdx) funcIdx-- ; } static int doCallbacks (void) { int i ; int rval = 1 ; for (i = 0 ; i < funcIdx ; i++) if (funcs [i] != NULL) rval = (funcs[i](args [i]) && rval) ; return rval ; } static char *key ; %} %union{ scope *scp ; value *val ; char *name ; int integer ; double real ; char *string ; char chr ; } %token PEER %token GROUP %token IVAL %token RVAL %token NAME %token XSTRING %token SCOPE %token COLON %token LBRACE %token RBRACE %token TRUEBVAL %token FALSEBVAL %token CHAR %token WORD %token IP_ADDRESS %type IVAL %type RVAL %type XSTRING %type CHAR %type TRUEBVAL FALSEBVAL WORD %% input: { lineCount = 1 ; addScope (NULL,"",newScope ("")) ; topScope = currScope ; } entries { if (!doCallbacks()) YYABORT ; } ; scope: entries ; entries: | entries entry | entries error { errbuff = malloc (strlen(SYNTAX_ERROR) + 12) ; sprintf (errbuff,SYNTAX_ERROR,lineCount) ; YYABORT ; } ; entry: PEER WORD LBRACE { errbuff = addScope (currScope,$2,newScope ("peer")) ; free ($2) ; if (errbuff != NULL) YYABORT ; } scope RBRACE { currScope = currScope->parent ; } | GROUP WORD LBRACE { errbuff = addScope (currScope,$2,newScope ("group")) ; free ($2) ; if (errbuff != NULL) YYABORT ; } scope RBRACE { currScope = currScope->parent ; } | WORD WORD LBRACE { errbuff = malloc (strlen(UNKNOWN_SCOPE_TYPE) + 15 + strlen ($1)) ; sprintf (errbuff,UNKNOWN_SCOPE_TYPE,lineCount,$1) ; free ($1) ; free ($2) ; YYABORT ; } | WORD { if ((errbuff = keyOk($1)) != NULL) { YYABORT ; } else key = $1 ; } COLON value ; value: WORD { if ((errbuff = addString (currScope, key, $1)) != NULL) YYABORT ; free (key) ; free ($1) ; } | IVAL { if ((errbuff = addInteger(currScope, key, $1)) != NULL) YYABORT; free (key) ; } | TRUEBVAL { if ((errbuff = addBoolean (currScope, key, 1)) != NULL) YYABORT ; free (key) ; free ($1) ; } | FALSEBVAL { if ((errbuff = addBoolean (currScope, key, 0)) != NULL) YYABORT ; free (key) ; free ($1) ; } | RVAL { if ((errbuff = addReal (currScope, key, $1)) != NULL) YYABORT ; free (key) ; } | XSTRING { if ((errbuff = addString (currScope, key, $1)) != NULL) YYABORT; free (key) ; } | CHAR { if ((errbuff = addChar (currScope, key, $1)) != NULL) YYABORT ; free (key) ; } ; %% int yyerror (const char *s) { #undef FMT #define FMT "line %d: %s" errbuff = malloc (strlen (s) + strlen (FMT) + 20) ; sprintf (errbuff,FMT,lineCount,s) ; return 0 ; } int yywrap (void) { return 1 ; } extern FILE *yyin ; extern int yydebug ; #define NO_INHERIT 0 #if ! defined (WANT_MAIN) struct peer_table_s { char *peerName ; value *peerValue ; } ; static struct peer_table_s *peerTable ; static int peerTableCount ; static int peerTableIdx ; void configCleanup (void) { int i ; for (i = 0 ; i < peerTableIdx ; i++) free (peerTable[i].peerName) ; free (peerTable) ; freeScopeTree (topScope); free (funcs) ; free (args) ; } int buildPeerTable (FILE *fp, scope *s) { int rval = 1 ; int i, j ; for (i = 0 ; i < s->value_idx ; i++) { if (ISSCOPE (s->values[i]) && ISPEER (s->values[i])) { for (j = 0 ; j < peerTableIdx ; j++) { if (strcmp (peerTable[j].peerName,s->values[i]->name) == 0) { logOrPrint (LOG_ERR,fp,DUP_PEER_NAME,peerTable[j].peerName) ; rval = 0 ; break ; } } if (j == peerTableIdx) { if (peerTableCount == peerTableIdx) { peerTableCount += 10 ; if (peerTable == NULL) peerTable = ALLOC(struct peer_table_s,peerTableCount) ; else peerTable = REALLOC (peerTable,struct peer_table_s, peerTableCount) ; } peerTable[peerTableIdx].peerName = strdup (s->values[i]->name); peerTable[peerTableIdx].peerValue = s->values[i] ; peerTableIdx++ ; } } else if (ISSCOPE (s->values[i])) rval = (buildPeerTable (fp,s->values[i]->v.scope_val) && rval) ; } return rval ; } /* read the config file. Any errors go to errorDest if it is non-NULL, otherwise they are syslogged. If justCheck is true then return after parsing */ static int inited = 0 ; int readConfig (const char *file, FILE *errorDest, int justCheck, int dump) { scope *oldTop = topScope ; FILE *fp ; int rval ; if (!inited) { inited = 1 ; yydebug = (getenv ("YYDEBUG") == NULL ? 0 : 1) ; if (yydebug) atexit (configCleanup) ; } if (file == NULL || strlen (file) == 0 || !fileExistsP (file)) { logOrPrint (LOG_ERR,errorDest,NOSUCH_CONFIG, file ? file : "(null)") ; dprintf (1,"No such config file: %s\n", file ? file : "(null)") ; exit (1) ; } if ((fp = fopen (file,"r")) == NULL) { logOrPrint (LOG_ERR,errorDest,CFG_FOPEN_FAILURE, file) ; exit (1) ; } logOrPrint (LOG_NOTICE,errorDest,"loading %s", file) ; yyin = fp ; topScope = NULL ; rval = yyparse () ; fclose (fp) ; if (rval != 0) /* failure */ { freeScopeTree (topScope) ; if (justCheck) freeScopeTree (oldTop) ; else topScope = oldTop ; topScope = NULL ; if (errbuff != NULL) { if (errorDest != NULL) fprintf (errorDest,CONFIG_PARSE_FAILED,"",errbuff) ; else syslog (LOG_ERR,CONFIG_PARSE_FAILED,"ME ",errbuff) ; free (errbuff) ; } return 0 ; } if (dump) { fprintf (errorDest ? errorDest : stderr,"Parsed config file:\n") ; printScope (errorDest ? errorDest : stderr,topScope,-5) ; fprintf (errorDest ? errorDest : stderr,"\n") ; } if (justCheck) { freeScopeTree (topScope) ; freeScopeTree (oldTop) ; topScope = NULL ; } else { for (peerTableIdx-- ; peerTableIdx >= 0 ; peerTableIdx--) { free (peerTable [peerTableIdx].peerName) ; peerTable [peerTableIdx].peerName = NULL ; peerTable [peerTableIdx].peerValue = NULL ; } peerTableIdx = 0 ; if (!buildPeerTable (errorDest,topScope)) logAndExit (1,"Failed to build list of peers") ; } return 1 ; } value *getNextPeer (int *cookie) { value *rval ; if (*cookie < 0 || *cookie >= peerTableIdx) return NULL ; rval = peerTable[*cookie].peerValue ; (*cookie)++ ; return rval ; } value *findPeer (const char *name) { value *v = NULL ; int i ; for (i = 0 ; i < peerTableIdx ; i++) if (strcmp (peerTable[i].peerName,name) == 0) { v = peerTable[i].peerValue ; break ; } return v ; } #endif #if defined (WANT_MAIN) int main (int argc, char **argv) { if ( yyparse() ) printf ("parsing failed: %s\n",errbuff ? errbuff : "NONE") ; else { printScope (stdout,topScope,-5) ; if (argc == 3) { #if 0 printf ("Looking for %s of type %s: ",argv[2],argv[1]) ; if (strncmp (argv[1],"int",3) == 0) { int i = 0 ; if (!getInteger (topScope,argv[2],&i)) printf ("wasn't found.\n") ; else printf (" %d\n",i) ; } else if (strncmp (argv[1],"real",4) == 0) { double d = 0.0 ; if (!getReal (topScope,argv[2],&d)) printf ("wasn't found.\n") ; else printf (" %0.5f\n",d) ; } #else value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { long ival = 987654 ; if (getInteger (v->v.scope_val,argv[2],&ival,1)) printf ("Getting %s : %ld",argv[2],ival) ; else printf ("Name is not legal: %s\n",argv[2]) ; } #endif } else if (argc == 2) { #if 1 value *v = findValue (topScope,argv[1],1) ; if (v == NULL) printf ("Can't find %s\n",argv[1]) ; else { printf ("Getting %s : ",argv[1]) ; printValue (stdout,v,0) ; } #else if (findScope (topScope,argv[1],1) == NULL) printf ("Can't find the scope of %s\n",argv[1]) ; #endif } } freeScopeTree (topScope) ; return 0 ; } #endif /* defined (WANT_MAIN) */ innfeed-0.10.1.7.orig/connection.c0100644000175100001440000034746706400214357015157 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Thu Dec 28 13:34:47 1995 * Project: INN (innfeed) * File: connection.c * RCSId: $Id: connection.c,v 1.11 1997/08/25 05:32:31 scrappy Exp $ * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The implementation of the Connection class. * * The Connection object is what manages the NNTP * protocol. If the remote doesn't do streaming, then the * standard IHAVE lock-step protcol is performed. In the * streaming situation we have two cases. One where we must * send CHECK commands, and the other where we can directly * send TAKETHIS commands without a prior CHECK. * * The Connection object maintains four article queues. The first * one is where new articles are put if they need to have an * IHAVE or CHECK command sent for them. The second queue is * where the articles move from the first after their IHAVE/CHECK * command is sent, but the reply has not yet been seen. The * third queue is where articles go after the IHAVE/CHECK reply * has been seen (and the reply says to send the article). It is * articles in the third queue that have the TAKETHIS command * sent, or the body of an IHAVE. The third queue is also where * new articles go if the connection is running in no-CHECK * mode. The fourth queue is where the articles move to from the * third queue after their IHAVE-body or TAKETHIS command has * been sent. When the response to the IHAVE-body or TAKETHIS is * received the articles are removed from the fourth queue and * the Host object controlling this Connection is notified of * the success or failure of the transfer. * * The whole system is event-driven by the EndPoint class and the * Host via calls to prepareRead() and prepareWrite() and * prepareSleep(). * */ /* We should probably store the results of gethostbyname in the connection so we can rotate through the address when one fails for connecting. Perhaps the gethostbyname should be done in the Host and the connection should just be given the address to use. Should we worry about articles being stuck on a queue for ever if the remote forgets to send a response to a CHECK? Perhaps instead of killing the connection on some of the more simple errors, we should perhaps try to flush the input and keep going. Worry about counter overflow. Worry about stats gathering when switch to no-check mode. XXX if issueQUIT() has a problem and the state goes to cxnDeadS this is not handled properly everywhere yet. */ #if ! defined (lint) static const char *rcsid = "$Id: connection.c,v 1.11 1997/08/25 05:32:31 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #include #include #include #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #include #if defined (DO_NEED_TIME) #include #endif #include #include #include #include #include #include #include #include #include #if defined (__FreeBSD__) #include #endif #ifdef XXX_RAWHACK /* From: Sang-yong Suh */ #if defined (linux) && ! defined (_SYS_IOCTL_H) #include #endif #endif /* XXX_RAWHACK */ #include "buffer.h" #include "connection.h" #include "endpoint.h" #include "host.h" #include "article.h" #include "msgs.h" #include "configfile.h" #if defined (NDEBUG) #define VALIDATE_CONNECTION(x) ((void) 0) #else #define VALIDATE_CONNECTION(x) validateConnection (x) #endif extern char **PointersFreedOnExit ; extern const char *pidFile ; /* * Private types. */ /* We keep a linked list of articles the connection is trying to transmit */ typedef struct art_holder_s { Article article ; struct art_holder_s *next ; } *ArtHolder ; typedef enum { cxnStartingS, /* the connection's start state. */ cxnWaitingS, /* not connected. Waiting for an article. */ cxnConnectingS, /* in the middle of connecting */ cxnIdleS, /* open and ready to feed, has empty queues */ cxnIdleTimeoutS, /* timed out in the idle state */ cxnFeedingS, /* in the processes of feeding articles */ cxnSleepingS, /* blocked on reestablishment timer */ cxnFlushingS, /* am waiting for queues to drain to bounce connection. */ cxnClosingS, /* have been told to close down permanently when queues drained */ cxnDeadS /* connection is dead. */ } CxnState ; /* The Connection class */ struct connection_s { Host myHost ; /* the host who owns the connection */ EndPoint myEp ; /* the endpoint the connection talks through */ u_int ident ; /* an identifier for syslogging. */ CxnState state ; /* the state the connection is in */ /* * The Connection maintains 4 queue of articles. */ ArtHolder checkHead ; /* head of article list to do CHECK/IHAVE */ ArtHolder checkRespHead ; /* head of list waiting on CHECK/IHAVE response */ ArtHolder takeHead ; /* head of list of articles to send TAKETHIS/IHAVE-body */ ArtHolder takeRespHead ; /* list of articles waiting on TAKETHIS/IHAVE-body response */ u_int articleQTotal ; /* number of articles in all four queues */ ArtHolder missing ; /* head of missing list */ Buffer respBuffer ; /* buffer all responses are read into */ char *ipName ; /* the ip name (possibly quad) of the remote */ u_int maxCheck ; /* the max number of CHECKs to send */ u_short port ; /* the port number to use */ /* * Timeout values and their callback IDs */ /* Timer for max amount of time between receiving articles from the Host */ u_int articleReceiptTimeout ; TimeoutId artReceiptTimerId ; /* Timer for the max amount of time to wait for a response from the remote */ u_int readTimeout ; TimeoutId readBlockedTimerId ; /* Timer for the max amount of time to wait for a any amount of data to be written to the remote */ u_int writeTimeout ; TimeoutId writeBlockedTimerId ; /* Timer for the max number of seconds to keep the network connection up (long lasting connections give older nntp servers problems). */ u_int flushTimeout ; TimeoutId flushTimerId ; /* Timer for the number of seconds to sleep before attempting a reconnect. */ u_int sleepTimeout ; TimeoutId sleepTimerId ; bool loggedNoCr ; /* true if we logged the NOCR_MSG */ bool immedRecon ; /* true if we recon immediately after flushing. */ bool doesStreaming ; /* true if remote will handle streaming */ bool quitWasIssued ; /* true if QUIT command was sent. */ bool needsChecks ; /* true if we issue CHECK commands in streaming mode (rather than just sending TAKETHIS commands) */ time_t timeCon ; /* the time the connect happened (including the MODE STREAM command). */ /* * STATISTICS */ u_int artsTaken ; /* the number of articles INN gave this cxn */ u_int checksIssued ; /* the number of CHECKS/IHAVES we sent. Note that if we're running in no-CHECK mode, then we add in the TAKETHIS commands too */ u_int checksRefused ; /* the number of response 435/438 */ u_int takesRejected ; /* the number of response 437/439 recevied */ u_int takesOkayed ; /* the number of response 235/239 received */ double onThreshold ; /* for no-CHECK mode */ double offThreshold ; /* for no-CHECK mode */ double filterValue ; /* current value of IIR filter */ double lowPassFilter ; /* time constant for IIR filter */ Connection next ; /* for global list. */ }; static Connection gCxnList = NULL ; static u_int gCxnCount = 0 ; static u_int max_reconnect_period = MAX_RECON_PER ; static u_int init_reconnect_period = INIT_RECON_PER ; #if 0 static bool inited = false ; #endif static Buffer dotFirstBuffer ; static Buffer dotBuffer ; static Buffer crlfBuffer ; /*************************************************** * * Private function declarations. * ***************************************************/ /* I/O Callbacks */ static void connectionDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getBanner (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void getModeResponse (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void responseIsRead (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void quitWritten (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void ihaveBodyDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void commandWriteDone (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void modeCmdIssued (EndPoint e, IoStatus i, Buffer *b, void *d) ; static void writeProgress (EndPoint e, IoStatus i, Buffer *b, void *d) ; /* Timer callbacks */ static void responseTimeoutCbk (TimeoutId id, void *data) ; static void writeTimeoutCbk (TimeoutId id, void *data) ; static void reopenTimeoutCbk (TimeoutId id, void *data) ; static void flushCxnCbk (TimeoutId, void *data) ; static void articleTimeoutCbk (TimeoutId id, void *data) ; /* Work callbacks */ static void cxnWorkProc (EndPoint ep, void *data) ; static void cxnSleepOrDie (Connection cxn) ; /* Response processing. */ static void processResponse205 (Connection cxn, char *response) ; static void processResponse238 (Connection cxn, char *response) ; static void processResponse431 (Connection cxn, char *response) ; static void processResponse438 (Connection cxn, char *response) ; static void processResponse239 (Connection cxn, char *response) ; static void processResponse439 (Connection cxn, char *response) ; static void processResponse235 (Connection cxn, char *response) ; static void processResponse335 (Connection cxn, char *response) ; static void processResponse400 (Connection cxn, char *response) ; static void processResponse435 (Connection cxn, char *response) ; static void processResponse436 (Connection cxn, char *response) ; static void processResponse437 (Connection cxn, char *response) ; static void processResponse480 (Connection cxn, char *response) ; /* Misc functions */ static void cxnSleep (Connection cxn) ; static void cxnDead (Connection cxn) ; static void cxnIdle (Connection cxn) ; static void noSuchMessageId (Connection cxn, u_int responseCode, const char *msgid, const char *response) ; static void abortConnection (Connection cxn) ; static void resetConnection (Connection cxn) ; static void deferAllArticles (Connection cxn) ; static void deferQueuedArticles (Connection cxn) ; static void doSomeWrites (Connection cxn) ; static bool issueIHAVE (Connection cxn) ; static void issueIHAVEBody (Connection cxn) ; static bool issueStreamingCommands (Connection cxn) ; static Buffer buildCheckBuffer (Connection cxn) ; static Buffer *buildTakethisBuffers (Connection cxn, Buffer checkBuffer) ; static void issueQUIT (Connection cxn) ; static void initReadBlockedTimeout (Connection cxn) ; static int prepareWriteWithTimeout (EndPoint endp, Buffer *buffers, EndpRWCB done, Connection cxn) ; static void delConnection (Connection cxn) ; static void incrFilter (Connection cxn) ; static void decrFilter (Connection cxn) ; static bool writesNeeded (Connection cxn) ; static void validateConnection (Connection cxn) ; static const char *stateToString (CxnState state) ; static void prepareReopenCbk (Connection cxn) ; /* Article queue management routines. */ static ArtHolder newArtHolder (Article art) ; static void delArtHolder (ArtHolder artH) ; static bool remArtHolder (ArtHolder art, ArtHolder *head, u_int *count) ; static void appendArtHolder (ArtHolder artH, ArtHolder *head, u_int *count) ; static ArtHolder artHolderByMsgId (const char *msgid, ArtHolder head) ; static int fudgeFactor (int initVal) ; /*************************************************** * * Public functions implementation. * ***************************************************/ int cxnConfigLoadCbk (void *data) { long iv ; int rval = 1 ; FILE *fp = (FILE *) data ; (void) data ; /* keep lint happy */ if (getInteger (topScope,"max-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ONE,"max-reconnect-time", iv,"global scope",(long) MAX_RECON_PER); iv = MAX_RECON_PER ; } } else iv = MAX_RECON_PER ; max_reconnect_period = (u_int) iv ; if (getInteger (topScope,"initial-reconnect-time",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ONE,"initial-reconnect-time", iv,"global scope",(long)INIT_RECON_PER); iv = INIT_RECON_PER ; } } else iv = INIT_RECON_PER ; init_reconnect_period = (u_int) iv ; return rval ; } /* * Create a new Connection object and return it. All fields are * initialized to reasonable values. */ Connection newConnection (Host host, u_int id, const char *ipname, u_int articleReceiptTimeout, u_int portNum, u_int respTimeout, u_int flushTimeout, double lowPassLow, double lowPassHigh, double lowPassFilter) { Connection cxn ; bool croak = false ; if (ipname == NULL) { dprintf (1,"NULL ipname in newConnection\n") ; croak = true ; } if (ipname && strlen (ipname) == 0) { dprintf (1,"Empty ipname in newConnection\n") ; croak = true ; } if (croak) return NULL ; cxn = CALLOC (struct connection_s, 1) ; ASSERT (cxn != NULL) ; cxn->myHost = host ; cxn->myEp = NULL ; cxn->ident = id ; cxn->checkHead = NULL ; cxn->checkRespHead = NULL ; cxn->takeHead = NULL ; cxn->takeRespHead = NULL ; cxn->articleQTotal = 0 ; cxn->missing = NULL ; cxn->respBuffer = newBuffer (BUFFER_SIZE) ; ASSERT (cxn->respBuffer != NULL) ; cxn->ipName = strdup (ipname) ; cxn->port = portNum ; /* Time out the higher numbered connections faster */ cxn->articleReceiptTimeout = articleReceiptTimeout * 10.0 / (10.0 + id) ; cxn->artReceiptTimerId = 0 ; cxn->readTimeout = respTimeout ; cxn->readBlockedTimerId = 0 ; cxn->writeTimeout = respTimeout ; /* XXX should be a separate value */ cxn->writeBlockedTimerId = 0 ; cxn->flushTimeout = fudgeFactor (flushTimeout) ; cxn->flushTimerId = 0 ; cxn->onThreshold = lowPassHigh * lowPassFilter / 100.0 ; cxn->offThreshold = lowPassLow * lowPassFilter / 100.0 ; cxn->lowPassFilter = lowPassFilter; cxn->sleepTimerId = 0 ; cxn->sleepTimeout = init_reconnect_period ; resetConnection (cxn) ; cxn->next = gCxnList ; gCxnList = cxn ; gCxnCount++ ; cxn->state = cxnStartingS ; return cxn ; } /* Create a new endpoint hooked to a non-blocking socket that is trying to * connect to the host info stored in the Connection. On fast machines * connecting locally the connect() may have already succeeded when this * returns, but typically the connect will still be running and when it * completes. The Connection will be notified via a write callback setup by * prepareWrite below. If nothing goes wrong then this will return true * (even if the connect() has not yet completed). If something fails * (hostname lookup etc.) then it returns false (and the Connection is left * in the sleeping state).. * * Pre-state Reason cxnConnect called * --------- ------------------------ * cxnStartingS Connection owner issued call. * cxnWaitingS side effect of cxnTakeArticle() call * cxnConnecting side effect of cxnFlush() call * cxnSleepingS side effect of reopenTimeoutCbk() call. */ bool cxnConnect (Connection cxn) { struct sockaddr_in cxnAddr ; int fd, rval ; struct in_addr *addr = NULL; const char *peerName = hostPeerName (cxn->myHost) ; ASSERT (cxn->myEp == NULL) ; if (!(cxn->state == cxnStartingS || cxn->state == cxnWaitingS || cxn->state == cxnFlushingS || cxn->state == cxnSleepingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return false; } if (cxn->state == cxnWaitingS) ASSERT (cxn->articleQTotal == 1) ; else ASSERT (cxn->articleQTotal == 0) ; cxn->state = cxnConnectingS ; addr = hostIpAddr (cxn->myHost) ; if (addr == NULL) { cxnSleepOrDie (cxn) ; return false ; } memset (&cxnAddr, 0, sizeof (cxnAddr)) ; cxnAddr.sin_family = AF_INET ; cxnAddr.sin_port = htons(cxn->port) ; cxnAddr.sin_addr.s_addr = addr->s_addr ; if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { syslog (LOG_ERR, SOCKET_CREATE_ERROR, peerName, cxn->ident) ; dprintf (1,"Can't get a socket: %m\n") ; cxnSleepOrDie (cxn) ; return false ; } /* set our file descriptor to non-blocking */ #if defined (NBIO_FCNTL) #if ! defined (FNDELAY) #define FNDELAY O_NDELAY #endif rval = fcntl (fd, F_SETFL, FNDELAY) ; #else { int state = 1 ; rval = ioctl (fd, FIONBIO, (char *) &state) ; } #endif if (rval < 0) { syslog (LOG_ERR, FCNTL_ERROR, peerName, cxn->ident) ; close (fd) ; cxnSleepOrDie (cxn) ; return false ; } rval = connect (fd, (struct sockaddr *) &cxnAddr, sizeof (cxnAddr)) ; if (rval < 0 && errno != EINPROGRESS) { syslog (LOG_ERR, CONNECT_ERROR, peerName, cxn->ident) ; close (fd) ; cxnSleepOrDie (cxn) ; return false ; } if ((cxn->myEp = newEndPoint (fd)) == NULL) { /* If this happens, then fd was bigger than what select could handle, so endpoint.c refused to create the new object. */ close (fd) ; cxnSleepOrDie (cxn) ; return false ; } if (rval < 0) /* when the write callback gets done the connection went through */ prepareWrite (cxn->myEp, NULL, NULL, connectionDone, cxn) ; else connectionDone (cxn->myEp, IoDone, NULL, cxn) ; /* connectionDone() could set state to sleeping */ return (cxn->state == cxnConnectingS ? true : false) ; } /* Put the Connection into the wait state. * * Pre-state Reason cxnWait called * --------- ------------------------ * cxnStartingS - Connection owner called cxnWait() * cxnSleepingS - side effect of cxnFlush() call. * cxnConnectingS - side effect of cxnFlush() call. * cxnFlushingS - side effect of receiving response 205 * and Connection had no articles when * cxnFlush() was issued. * - prepareRead failed. * - I/O failed. * */ void cxnWait (Connection cxn) { ASSERT (cxn->state == cxnStartingS || cxn->state == cxnSleepingS || cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; cxn->state = cxnWaitingS ; hostCxnWaiting (cxn->myHost,cxn) ; /* tell our Host we're waiting */ } /* Tells the Connection to flush itself (i.e. push out all articles, * issue a QUIT and drop the network connection. If necessary a * reconnect will be done immediately after. Called by the Host, or * by the timer callback. * * Pre-state Reason cxnFlush called * --------- ------------------------ * ALL (except cxnDeadS - Connection owner called cxnFlush() * and cxnStartingS) * cxnFeedingS - side effect of flushCxnCbk() call. */ void cxnFlush (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnStartingS) ; ASSERT (cxn->state != cxnDeadS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnSleepingS: cxnWait (cxn) ; break ; case cxnConnectingS: cxnWait (cxn) ; cxnConnect (cxn) ; break ; case cxnIdleTimeoutS: case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnFlushingS ; issueQUIT (cxn) ; break ; case cxnClosingS: case cxnFlushingS: case cxnWaitingS: if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) issueQUIT (cxn) ; break ; case cxnFeedingS: /* we only reconnect immediately if we're not idle when cxnFlush() is called. */ if (!cxn->immedRecon) { cxn->immedRecon = (cxn->articleQTotal > 0 ? true : false) ; dprintf (1,"%s:%d immediate reconnect for a cxnFlush()\n", hostPeerName (cxn->myHost), cxn->ident) ; } clearTimer (cxn->flushTimerId) ; cxn->state = cxnFlushingS ; if (cxn->articleQTotal == 0 && !writeIsPending (cxn->myEp)) issueQUIT (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * Tells the Connection to dump all articles that are queued and to issue a * QUIT as quickly as possible. Much like cxnClose, except queued articles * are not sent, but are given back to the Host. */ void cxnTerminate (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnDeadS) ; ASSERT (cxn->state != cxnStartingS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnFeedingS: dprintf (1,"%s:%d Issuing terminate\n", hostPeerName (cxn->myHost), cxn->ident) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; deferQueuedArticles (cxn) ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnIdleTimeoutS: case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; issueQUIT (cxn) ; break ; case cxnFlushingS: /* we are in the middle of a periodic close. */ dprintf (1,"%s:%d Connection already being flushed\n", hostPeerName (cxn->myHost),cxn->ident); cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnClosingS: dprintf (1,"%s:%d Connection already closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: case cxnConnectingS: case cxnSleepingS: cxnDead (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state == cxnDeadS) { dprintf (1,"%s:%d Deleting connection\n",hostPeerName (cxn->myHost), cxn->ident) ; delConnection (cxn) ; } } /* Tells the Connection to do a disconnect and then when it is * disconnected to delete itself. * * Pre-state Reason cxnClose called * --------- ------------------------ * ALL (except cxnDeadS - Connecton owner called directly. * and cxnStartingS). */ void cxnClose (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnDeadS) ; ASSERT (cxn->state != cxnStartingS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnFeedingS: dprintf (1,"%s:%d Issuing disconnect\n", hostPeerName (cxn->myHost), cxn->ident) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnIdleS: case cxnIdleTimeoutS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->state != cxnIdleTimeoutS) clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->flushTimerId) ; cxn->state = cxnClosingS ; issueQUIT (cxn) ; break ; case cxnFlushingS: /* we are in the middle of a periodic close. */ dprintf (1,"%s:%d Connection already being flushed\n", hostPeerName (cxn->myHost),cxn->ident); cxn->state = cxnClosingS ; if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* send out the QUIT if we can */ break ; case cxnClosingS: dprintf (1,"%s:%d Connection already closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: case cxnConnectingS: case cxnSleepingS: cxnDead (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state == cxnDeadS) { dprintf (1,"%s:%d Deleting connection\n",hostPeerName (cxn->myHost), cxn->ident) ; delConnection (cxn) ; } } /* This is what the Host calls to get us to tranfer an article. If * we're running the IHAVE sequence, then we can't take it if we've * got an article already. If we're running the CHECK/TAKETHIS * sequence, then we'll take as many as we can (up to our MAXCHECK * limit). */ bool cxnTakeArticle (Connection cxn, Article art) { bool rval = true ; ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; if ( !cxnQueueArticle (cxn,art) ) /* might change cxnIdleS to cxnFeedingS */ return false ; if (!(cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnWaitingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return false ; } if (cxn->state != cxnWaitingS) /* because articleQTotal == 1 */ VALIDATE_CONNECTION (cxn) ; else ASSERT (cxn->articleQTotal == 1) ; switch (cxn->state) { case cxnWaitingS: cxnConnect (cxn) ; break ; case cxnFeedingS: doSomeWrites (cxn) ; break ; case cxnConnectingS: break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } return rval ; } /* if there's room in the Connection then stick the article on the * queue, otherwise return false. */ bool cxnQueueArticle (Connection cxn, Article art) { ArtHolder newArt ; bool rval = false ; ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnStartingS) ; ASSERT (cxn->state != cxnDeadS) ; VALIDATE_CONNECTION (cxn) ; switch (cxn->state) { case cxnClosingS: dprintf (5,"%s:%d Refusing article due to closing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnFlushingS: dprintf (5,"%s:%d Refusing article due to flushing\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnSleepingS: dprintf (5,"%s:%d Refusing article due to sleeping\n", hostPeerName (cxn->myHost),cxn->ident) ; break ; case cxnWaitingS: rval = true ; newArt = newArtHolder (art) ; appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; break ; case cxnConnectingS: if (cxn->articleQTotal != 0) break ; rval = true ; newArt = newArtHolder (art) ; appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; break ; case cxnIdleS: case cxnFeedingS: if (cxn->articleQTotal >= cxn->maxCheck) dprintf (5, "%s:%d Refusing article due to articleQTotal >= maxCheck (%d > %d)\n", hostPeerName (cxn->myHost), cxn->ident, cxn->articleQTotal, cxn->maxCheck) ; else { rval = true ; newArt = newArtHolder (art) ; if (cxn->needsChecks) appendArtHolder (newArt, &cxn->checkHead, &cxn->articleQTotal) ; else appendArtHolder (newArt, &cxn->takeHead, &cxn->articleQTotal) ; if (cxn->state == cxnIdleS) { cxn->state = cxnFeedingS ; clearTimer (cxn->artReceiptTimerId) ; } } break ; default: die ("Invalid state: %s\n", stateToString (cxn->state)) ; } if (rval) { dprintf (5,"%s:%d accepting article %s\n",hostPeerName (cxn->myHost), cxn->ident,artMsgId (art)) ; cxn->artsTaken++ ; } return rval ; } /* * generate a log message for activity. Usually called by the Connection's * owner */ void cxnLogStats (Connection cxn, bool final) { const char *peerName ; time_t now = theTime() ; ASSERT (cxn != NULL) ; /* only log stats when in one of these three states. */ switch (cxn->state) { case cxnFeedingS: case cxnFlushingS: case cxnClosingS: break ; default: return ; } peerName = hostPeerName (cxn->myHost) ; syslog (LOG_NOTICE,STATS_MSG, peerName, cxn->ident, (final ? "final" : "checkpoint"), (long) (now - cxn->timeCon), cxn->checksIssued, cxn->takesOkayed, cxn->checksRefused, cxn->takesRejected) ; if (final) { cxn->artsTaken = 0 ; cxn->checksIssued = 0 ; cxn->checksRefused = 0 ; cxn->takesRejected = 0 ; cxn->takesOkayed = 0 ; if (cxn->timeCon > 0) cxn->timeCon = theTime() ; } } /* * return the number of articles the connection will accept. */ size_t cxnQueueSpace (Connection cxn) { int rval = 0 ; ASSERT (cxn != NULL) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnConnectingS || cxn->state == cxnWaitingS) rval = cxn->maxCheck - cxn->articleQTotal ; return rval ; } /* * Print info on all the connections that currently exist. */ void gPrintCxnInfo (FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; Connection cxn ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Connection list : (count %d) {\n", indent,gCxnCount) ; for (cxn = gCxnList ; cxn != NULL ; cxn = cxn->next) printCxnInfo (cxn,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } /* * Print the info about the given connection. */ void printCxnInfo (Connection cxn, FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; ArtHolder artH ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sConnection : %p {\n",indent,cxn) ; fprintf (fp,"%s host : %p\n",indent, (void *) cxn->myHost) ; fprintf (fp,"%s endpoint : %p\n",indent,cxn->myEp) ; fprintf (fp,"%s state : %s\n",indent, stateToString (cxn->state)) ; fprintf (fp,"%s ident : %d\n",indent,cxn->ident) ; fprintf (fp,"%s ip-name : %s\n", indent, cxn->ipName) ; fprintf (fp,"%s port-number : %d\n",indent,cxn->port) ; fprintf (fp,"%s max-checks : %d\n",indent,cxn->maxCheck) ; fprintf (fp,"%s does-streaming : %s\n",indent, boolToString (cxn->doesStreaming)) ; fprintf (fp,"%s quitWasIssued : %s\n",indent, boolToString (cxn->quitWasIssued)) ; fprintf (fp,"%s needs-checks : %s\n",indent, boolToString (cxn->needsChecks)) ; fprintf (fp,"%s time-connected : %ld\n",indent,(long) cxn->timeCon) ; fprintf (fp,"%s articles from INN : %d\n",indent,cxn->artsTaken) ; fprintf (fp,"%s articles offered : %d\n",indent, cxn->checksIssued) ; fprintf (fp,"%s articles refused : %d\n",indent, cxn->checksRefused) ; fprintf (fp,"%s articles rejected : %d\n",indent, cxn->takesRejected) ; fprintf (fp,"%s articles accepted : %d\n",indent, cxn->takesOkayed) ; fprintf (fp,"%s low-pass upper limit : %0.6f\n", indent, cxn->onThreshold) ; fprintf (fp,"%s low-pass lower limit : %0.6f\n", indent, cxn->offThreshold) ; fprintf (fp,"%s low-pass filter tc : %0.6f\n", indent, cxn->lowPassFilter) ; fprintf (fp,"%s low-pass filter : %0.6f\n", indent, cxn->filterValue) ; fprintf (fp,"%s article-timeout : %d\n",indent,cxn->articleReceiptTimeout) ; fprintf (fp,"%s article-callback : %d\n",indent,cxn->artReceiptTimerId) ; fprintf (fp,"%s response-timeout : %d\n",indent,cxn->readTimeout) ; fprintf (fp,"%s response-callback : %d\n",indent,cxn->readBlockedTimerId) ; fprintf (fp,"%s write-timeout : %d\n",indent,cxn->writeTimeout) ; fprintf (fp,"%s write-callback : %d\n",indent,cxn->writeBlockedTimerId) ; fprintf (fp,"%s flushTimeout : %d\n",indent,cxn->flushTimeout) ; fprintf (fp,"%s flushTimerId : %d\n",indent,cxn->flushTimerId) ; fprintf (fp,"%s reopen wait : %d\n",indent,cxn->sleepTimeout) ; fprintf (fp,"%s reopen id : %d\n",indent,cxn->sleepTimerId) ; fprintf (fp,"%s CHECK queue {\n",indent) ; for (artH = cxn->checkHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s CHECK Response queue {\n",indent) ; for (artH = cxn->checkRespHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s TAKE queue {\n",indent) ; for (artH = cxn->takeHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s TAKE response queue {\n",indent) ; for (artH = cxn->takeRespHead ; artH != NULL ; artH = artH->next) printArticleInfo (artH->article,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s response buffer {\n",indent) ; printBufferInfo (cxn->respBuffer,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* * return the number of articles the connection will accept. */ bool cxnCheckstate (Connection cxn) { bool rval = false ; ASSERT (cxn != NULL) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnConnectingS) rval = true ; return rval ; } /**********************************************************************/ /** STATIC PRIVATE FUNCTIONS **/ /**********************************************************************/ /* * ENDPOINT CALLBACK AREA. * * All the functions in this next section are callbacks fired by the * EndPoint objects/class (either timers or i/o completion callbacks).. */ /* * this is the first stage of the NNTP FSM. This function is called * when the tcp/ip network connection is setup and we should get * ready to read the banner message. When this function returns the * state of the Connection will still be cxnConnectingS unless * something broken, in which case it probably went into the * cxnSleepingS state. */ static void connectionDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Buffer *readBuffers ; Connection cxn = (Connection) d ; const char *peerName ; int optval, size ; ASSERT (b == NULL) ; ASSERT (cxn->state == cxnConnectingS) ; ASSERT (!writeIsPending (cxn->myEp)) ; size = sizeof (optval) ; peerName = hostPeerName (cxn->myHost) ; if (i != IoDone) { errno = endPointErrno (e) ; syslog (LOG_ERR,IO_FAILED,peerName,cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (getsockopt (endPointFd (e), SOL_SOCKET, SO_ERROR, (GETSOCKOPT_ARG) &optval, &size) != 0) { /* This is bad. Can't even get the SO_ERROR value out of the socket */ syslog (LOG_ERR,GETSOCKOPT_FAILED, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (optval != 0) { /* if the connect failed then the only way to know is by getting the SO_ERROR value out of the socket. */ errno = optval ; syslog (LOG_NOTICE,CONNECTION_FAILURE,peerName,cxn->ident) ; cxnSleepOrDie (cxn) ; } else { readBuffers = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; if ( !prepareRead (e, readBuffers, getBanner, cxn, 1) ) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else { initReadBlockedTimeout (cxn) ; /* set up the callback for closing down the connection at regular intervals (due to problems with long running nntpd). */ if (cxn->flushTimeout > 0) cxn->flushTimerId = prepareSleep (flushCxnCbk, cxn->flushTimeout,cxn) ; /* The state doesn't change yet until we've read the banner and tried the MODE STREAM command. */ } } VALIDATE_CONNECTION (cxn) ; } /* * Called when the banner message has been read off the wire and is * in the buffer(s). When this function returns the state of the * Connection will still be cxnConnectingS unless something broken, * in which case it probably went into the cxnSleepiongS state. */ static void getBanner (EndPoint e, IoStatus i, Buffer *b, void *d) { Buffer *modeCmdBuffers, *readBuffers ; Connection cxn = (Connection) d ; char *p = bufferBase (b[0]) ; int code ; bool isOk = false ; const char *peerName ; char *rest ; ASSERT (e == cxn->myEp) ; ASSERT (b[0] == cxn->respBuffer) ; ASSERT (b[1] == NULL) ; ASSERT (cxn->state == cxnConnectingS) ; ASSERT (!writeIsPending (cxn->myEp)); peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; if (i != IoDone) { errno = endPointErrno (cxn->myEp) ; syslog (LOG_ERR, BANNER_READ_FAILED, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read. expand buffer and retry */ expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ; readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ; if ( !prepareRead (e, readBuffers, getBanner, cxn, 1) ) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } } else if ( !getNntpResponse (p, &code, &rest) ) { trim_ws (p) ; syslog (LOG_ERR, INVALID_RESP_FORMAT, peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { trim_ws (p) ; switch (code) { case 200: /* normal */ case 201: /* can transfer but not post -- old nntpd */ isOk = true ; break ; case 400: cxnSleepOrDie (cxn) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; case 502: syslog (LOG_NOTICE,NO_TALK_NNRPD,peerName,cxn->ident,p) ; cxnSleepOrDie (cxn) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; default: syslog (LOG_NOTICE,UNKNOWN_BANNER, peerName, cxn->ident, code, p) ; dprintf (1,"%s:%d Unknown response code: %d: %s\n", hostPeerName (cxn->myHost),cxn->ident, code, p) ; cxnSleepOrDie (cxn) ; hostCxnBlocked (cxn->myHost, cxn, rest) ; break ; } if ( isOk ) { Buffer modeBuffer ; #define MODE_CMD "MODE STREAM\r\n" modeBuffer = newBuffer (strlen (MODE_CMD) + 1) ; p = bufferBase (modeBuffer) ; /* now issue the MODE STREAM command */ dprintf (1,"%s:%d Issuing the streaming command: %s", hostPeerName (cxn->myHost),cxn->ident,MODE_CMD) ; strcpy (p, MODE_CMD) ; bufferSetDataSize (modeBuffer, strlen (p)) ; modeCmdBuffers = makeBufferArray (modeBuffer, NULL) ; if ( !prepareWriteWithTimeout (e, modeCmdBuffers, modeCmdIssued, cxn) ) { syslog (LOG_ERR, PREPARE_WRITE_FAILED, peerName, cxn->ident) ; die ("Prepare write for mode stream failed") ; } bufferSetDataSize (cxn->respBuffer, 0) ; readBuffers = makeBufferArray (bufferTakeRef(cxn->respBuffer),NULL); if ( !prepareRead (e, readBuffers, getModeResponse, cxn, 1) ) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; freeBufferArray (readBuffers) ; cxnSleepOrDie (cxn) ; } } } freeBufferArray (b) ; } /* * Process the remote's response to our MODE STREAM command. This is where * the Connection moves into the cxnFeedingS state. If the remote has given * us a good welcome banner, but then immediately dropped the connection, * we'll arrive here with the MODE STREAM command still queued up. */ static void getModeResponse (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; int code ; char *p = bufferBase (b[0]) ; Buffer *buffers ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (b [1] == NULL) ; /* only ever one buffer on this read */ ASSERT (cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; peerName = hostPeerName (cxn->myHost) ; bufferAddNullByte (b[0]) ; dprintf (1,"%s:%d Processing mode response: %s", /* no NL */ hostPeerName (cxn->myHost), cxn->ident, p) ; if (i == IoDone && writeIsPending (cxn->myEp)) { /* badness. should never happen */ syslog (LOG_ERR, MODE_WRITE_PENDING, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (i != IoDone) { errno = endPointErrno (e) ; syslog (LOG_ERR, RESPONSE_READ_FAILED, peerName, cxn->ident) ; cxnSleepOrDie (cxn) ; } else if (strchr (p, '\n') == NULL) { /* partial read */ expandBuffer (b [0], BUFFER_EXPAND_AMOUNT) ; buffers = makeBufferArray (bufferTakeRef (b [0]), NULL) ; if ( !prepareRead (e, buffers, getModeResponse, cxn, 1) ) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } } else { clearTimer (cxn->readBlockedTimerId) ; if ( !getNntpResponse (p, &code, NULL) ) { syslog (LOG_ERR, BAD_MODE_RESPONSE, peerName, cxn->ident, p) ; cxnSleepOrDie (cxn) ; } else { syslog (LOG_NOTICE,CONNECTED,peerName, cxn->ident) ; switch (code) { case 203: /* will do streaming */ hostRemoteStreams (cxn->myHost, cxn, true) ; if (hostWantsStreaming (cxn->myHost)) { cxn->doesStreaming = true ; cxn->maxCheck = hostMaxChecks (cxn->myHost) ; } else cxn->maxCheck = 1 ; break ; default: /* won't do it */ hostRemoteStreams (cxn->myHost, cxn, false) ; cxn->maxCheck = 1 ; break ; } /* now we consider ourselves completly connected. */ cxn->timeCon = theTime () ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; else cxn->state = cxnFeedingS ; /* one for the connection and one for the buffer array */ ASSERT (bufferRefCount (cxn->respBuffer) == 2) ; /* there was only one line in there, right? */ bufferSetDataSize (cxn->respBuffer, 0) ; buffers = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; /* sleepTimeout get changed at each failed attempt, so reset. */ cxn->sleepTimeout = init_reconnect_period ; if ( !prepareRead (cxn->myEp, buffers, responseIsRead, cxn, 1) ) { freeBufferArray (buffers) ; cxnSleepOrDie (cxn) ; } else { /* now we wait for articles from our Host, or we have some articles already. On infrequently used connections, the network link is torn down and rebuilt as needed. So we may be rebuilding the connection here in which case we have an article to send. */ if (writesNeeded (cxn) || hostGimmeArticle (cxn->myHost,cxn)) doSomeWrites (cxn) ; } } } freeBufferArray (b) ; } /* * called when a response has been read from the socket. This is * where the bulk of the processing starts. */ static void responseIsRead (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; char *response ; char *endr ; char *bufBase ; u_int respSize ; int code ; char *rest = NULL ; Buffer buf ; Buffer *bArr ; const char *peerName ; ASSERT (e == cxn->myEp) ; ASSERT (b != NULL) ; ASSERT (b [1] == NULL) ; ASSERT (b [0] == cxn->respBuffer) ; ASSERT (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnClosingS || cxn->state == cxnFlushingS) ; VALIDATE_CONNECTION (cxn) ; bufferAddNullByte (b [0]) ; peerName = hostPeerName (cxn->myHost) ; if (i != IoDone) { /* uh oh. */ errno = endPointErrno (e) ; syslog (LOG_ERR, RESPONSE_READ_FAILED, peerName, cxn->ident) ; freeBufferArray (b) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; return ; } buf = b [0] ; bufBase = bufferBase (buf) ; /* check that we have (at least) a full line response. If not expand the buffer and resubmit the read. */ if (strchr (bufBase, '\n') == 0) { if (!expandBuffer (buf, BUFFER_EXPAND_AMOUNT)) { syslog (LOG_ERR, CXN_BUFFER_EXPAND_ERROR, peerName, cxn->ident) ; freeBufferArray (b) ; cxnSleepOrDie (cxn) ; } else if ( !prepareRead (cxn->myEp, b, responseIsRead, cxn, 1)) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; freeBufferArray (b) ; cxnSleepOrDie (cxn) ; } return ; } freeBufferArray (b) ; /* connection still has reference to buffer */ /* * Now process all the full responses that we have. */ response = bufBase ; respSize = bufferDataSize (cxn->respBuffer) ; while ((endr = strchr (response, '\n')) != NULL) { char *next = endr + 1 ; if (*next == '\r') next++ ; endr-- ; if (*endr != '\r') endr++ ; if (next - endr != 2 && !cxn->loggedNoCr) { /* only a newline there. we'll live with it */ syslog (LOG_ERR, NOCR_MSG, peerName, cxn->ident) ; cxn->loggedNoCr = true ; } *endr = '\0' ; if ( !getNntpResponse (response, &code, &rest) ) { syslog (LOG_ERR, INVALID_RESP_FORMAT, peerName, cxn->ident, response) ; cxnSleepOrDie (cxn) ; return ; } dprintf (5,"%s:%d Response %d: %s\n", peerName, cxn->ident, code, response) ; /* now handle the response code. I'm not using symbolic names on purpose--the numbers are all you see in the RFC's. */ switch (code) { case 205: /* OK response to QUIT. */ processResponse205 (cxn, response) ; break ; /* These three are from the CHECK command */ case 238: /* no such article found */ /* Do not incrFilter (cxn) now, wait till after subsequent TAKETHIS */ processResponse238 (cxn, response) ; break ; case 431: /* try again later (also for TAKETHIS) */ decrFilter (cxn) ; processResponse431 (cxn, response) ; break ; case 438: /* already have it */ decrFilter (cxn) ; processResponse438 (cxn, response) ; break ; /* These are from the TAKETHIS command */ case 239: /* article transferred OK */ incrFilter (cxn) ; processResponse239 (cxn, response) ; break ; case 439: /* article rejected */ decrFilter (cxn) ; processResponse439 (cxn, response) ; break ; /* These are from the IHAVE command */ case 335: /* send article */ processResponse335 (cxn, response) ; break ; case 435: /* article not wanted */ processResponse435 (cxn, response) ; break ; case 436: /* transfer failed try again later */ processResponse436 (cxn, response) ; break ; case 437: /* article rejected */ processResponse437 (cxn, response) ; break ; case 400: /* has stopped accepting articles */ processResponse400 (cxn, response) ; break ; case 235: /* article transfered OK (IHAVE-body) */ processResponse235 (cxn, response) ; break ; case 480: /* Transfer permission denied. */ processResponse480 (cxn,response) ; break ; default: syslog (LOG_ERR, UNKNOWN_RESPONSE, peerName, cxn->ident, code, response) ; cxnSleepOrDie (cxn) ; break ; } VALIDATE_CONNECTION (cxn) ; if (cxn->state != cxnFeedingS && cxn->state != cxnClosingS && cxn->state != cxnFlushingS && cxn->state != cxnIdleS /* XXX */) break ; /* connection is terminated */ response = next ; } dprintf (5,"%s:%d done with responses\n",hostPeerName (cxn->myHost), cxn->ident) ; switch (cxn->state) { case cxnIdleS: case cxnFeedingS: case cxnClosingS: case cxnFlushingS: /* see if we need to drop in to or out of no-CHECK mode */ if (cxn->state == cxnFeedingS && cxn->doesStreaming) { if ((cxn->filterValue > cxn->onThreshold) && cxn->needsChecks) hostLogNoCheckMode (cxn->myHost, !(cxn->needsChecks = false), cxn->offThreshold/cxn->lowPassFilter, cxn->filterValue/cxn->lowPassFilter, cxn->onThreshold/cxn->lowPassFilter) ; /* on and log */ else if ((cxn->filterValue < cxn->offThreshold) && !cxn->needsChecks) hostLogNoCheckMode (cxn->myHost, !(cxn->needsChecks = true), cxn->offThreshold/cxn->lowPassFilter, cxn->filterValue/cxn->lowPassFilter, cxn->onThreshold/cxn->lowPassFilter) ; /* off and log */ } /* Now handle possible remaining partial reponse and set up for next read. */ if (*response != '\0') { /* partial response */ u_int leftAmt = respSize - (response - bufBase) ; dprintf (2,"%s:%d handling a partial response\n", hostPeerName (cxn->myHost),cxn->ident) ; /* first we shift what's left in the buffer down to the bottom, if needed, or just expand the buffer */ if (response != bufBase) { /* so next read appends */ memcpy (bufBase, response, leftAmt) ; bufferSetDataSize (cxn->respBuffer, leftAmt) ; } else if (!expandBuffer (cxn->respBuffer, BUFFER_EXPAND_AMOUNT)) die (CXN_BUFFER_EXPAND_ERROR,peerName,cxn->ident); } else bufferSetDataSize (cxn->respBuffer, 0) ; bArr = makeBufferArray (bufferTakeRef (cxn->respBuffer), NULL) ; if ( !prepareRead (e, bArr, responseIsRead, cxn, 1) ) { syslog (LOG_ERR, PREPARE_READ_FAILED, peerName, cxn->ident) ; freeBufferArray (bArr) ; cxnWait (cxn) ; return ; } else { /* only setup the timer if we're still waiting for a response to something. There's not necessarily a 1-to-1 mapping between reads and writes in streaming mode. May have been set already above (that would be unlikely I think). */ VALIDATE_CONNECTION (cxn) ; dprintf (5,"%s:%d about to do some writes\n", hostPeerName (cxn->myHost),cxn->ident) ; doSomeWrites (cxn) ; /* If the read timer is (still) running, update it to give those terminally slow hosts that take forever to drain the network buffers and just dribble out responses the benefit of the doubt. XXX - maybe should just increase timeout for these! */ if (cxn->readBlockedTimerId) cxn->readBlockedTimerId = updateSleep (cxn->readBlockedTimerId, responseTimeoutCbk, cxn->readTimeout, cxn) ; } VALIDATE_CONNECTION (cxn) ; break ; case cxnWaitingS: /* presumably after a code 205 or 400 */ case cxnConnectingS: /* presumably after a code 205 or 400 */ case cxnSleepingS: /* probably after a 480 */ break ; case cxnDeadS: delConnection (cxn) ; break ; case cxnStartingS: default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * called when the write of the QUIT command has completed. */ static void quitWritten (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; const char *peerName ; peerName = hostPeerName (cxn->myHost) ; clearTimer (cxn->writeBlockedTimerId) ; ASSERT (cxn->myEp == e) ; VALIDATE_CONNECTION (cxn) ; if (i != IoDone) { errno = endPointErrno (e) ; syslog (LOG_ERR, QUIT_WRITE_FAILED, peerName, cxn->ident) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnWait (cxn) ; } else /* The QUIT command has been sent, so start the response timer. */ initReadBlockedTimeout (cxn) ; freeBufferArray (b) ; } /* * called when the write of the IHAVE-body data is finished */ static void ihaveBodyDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; if (i != IoDone) { errno = endPointErrno (e) ; syslog (LOG_ERR, IHAVE_WRITE_FAILED, hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; } else /* The article has been sent, so start the response timer. */ initReadBlockedTimeout (cxn) ; freeBufferArray (b) ; return ; } /* * Called when a command set (IHAVE, CHECK, TAKETHIS) has been * written to the remote. */ static void commandWriteDone (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; const char *peerName ; ASSERT (e == cxn->myEp) ; peerName = hostPeerName (cxn->myHost) ; freeBufferArray (b) ; clearTimer (cxn->writeBlockedTimerId) ; if (i != IoDone) { errno = endPointErrno (e) ; syslog (LOG_ERR, COMMAND_WRITE_FAILED, peerName, cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { cxnDead (cxn) ; delConnection (cxn) ; } else { /* XXX - so cxnSleep() doesn't die in VALIDATE_CONNECTION () */ deferAllArticles (cxn) ; cxnIdle (cxn) ; cxnSleep (cxn) ; } } else { /* Some(?) hosts return the 439 response even before we're done sending, so don't go idle until here */ if (cxn->state == cxnFeedingS && cxn->articleQTotal == 0) cxnIdle (cxn) ; else /* The command set has been sent, so start the response timer. XXX - we'd like finer grained control */ initReadBlockedTimeout (cxn) ; if ( cxn->doesStreaming ) doSomeWrites (cxn) ; /* pump data as fast as possible */ /* XXX - will clear the read timeout */ } } /* * Called when the MODE STREAM command has been written down the pipe. */ static void modeCmdIssued (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (e == cxn->myEp) ; clearTimer (cxn->writeBlockedTimerId) ; /* The mode command has been sent, so start the response timer */ initReadBlockedTimeout (cxn) ; if (i != IoDone) { dprintf (1,"%s:%d MODE STREAM command failed to write\n", hostPeerName (cxn->myHost), cxn->ident) ; syslog (LOG_ERR,MODE_STREAM_FAILED,hostPeerName (cxn->myHost), cxn->ident) ; cxnSleepOrDie (cxn) ; } freeBufferArray (b) ; } /* * Called whenever some amount of data has been written to the pipe but * more data remains to be written */ static void writeProgress (EndPoint e, IoStatus i, Buffer *b, void *d) { Connection cxn = (Connection) d ; ASSERT (i == IoProgress) ; if (cxn->writeTimeout > 0) cxn->writeBlockedTimerId = updateSleep (cxn->writeBlockedTimerId, writeTimeoutCbk, cxn->writeTimeout, cxn) ; } /* * Timers. */ /* * This is called when the timeout for the reponse from the remote * goes off. We tear down the connection and notify our host. */ static void responseTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->readBlockedTimerId) ; ASSERT (cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; VALIDATE_CONNECTION (cxn) ; /* XXX - let abortConnection clear readBlockedTimerId, otherwise VALIDATE_CONNECTION() will croak */ peerName = hostPeerName (cxn->myHost) ; syslog (LOG_WARNING, RESPONSE_TIMEOUT, peerName, cxn->ident) ; dprintf (1,"%s:%d shutting down non-repsonsive connection\n", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { abortConnection (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; /* will notify the Host */ } /* * This is called when the data write timeout for the remote * goes off. We tear down the connection and notify our host. */ static void writeTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName ; ASSERT (id == cxn->writeBlockedTimerId) ; ASSERT (cxn->state == cxnConnectingS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; VALIDATE_CONNECTION (cxn) ; /* XXX - let abortConnection clear writeBlockedTimerId, otherwise VALIDATE_CONNECTION() will croak */ peerName = hostPeerName (cxn->myHost) ; syslog (LOG_WARNING, WRITE_TIMEOUT, peerName, cxn->ident) ; dprintf (1,"%s:%d shutting down non-responsive connection\n", hostPeerName (cxn->myHost), cxn->ident) ; cxnLogStats (cxn,true) ; if (cxn->state == cxnClosingS) { abortConnection (cxn) ; delConnection (cxn) ; } else cxnSleep (cxn) ; /* will notify the Host */ } /* * Called by the EndPoint class when the timer goes off */ void reopenTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->sleepTimerId) ; cxn->sleepTimerId = 0 ; if (cxn->state != cxnSleepingS) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; } else (void) cxnConnect (cxn) ; } /* * timeout callback to close down long running connection. */ static void flushCxnCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; ASSERT (id == cxn->flushTimerId) ; VALIDATE_CONNECTION (cxn) ; cxn->flushTimerId = 0 ; if (!(cxn->state == cxnFeedingS || cxn->state == cxnConnectingS || cxn->state == cxnIdleS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; } else { dprintf (1,"%s:%d Handling periodic connection close.\n", hostPeerName (cxn->myHost), cxn->ident) ; syslog (LOG_NOTICE, CXN_PERIODIC_CLOSE, hostPeerName (cxn->myHost), cxn->ident) ; cxnFlush (cxn) ; } } /* * Timer callback for when the connection has not received an * article from INN. When that happens we tear down the network * connection to help recycle fds */ static void articleTimeoutCbk (TimeoutId id, void *data) { Connection cxn = (Connection) data ; const char *peerName = hostPeerName (cxn->myHost) ; (void) id ; /* keep lint happy */ ASSERT (cxn->artReceiptTimerId == id) ; VALIDATE_CONNECTION (cxn) ; cxn->artReceiptTimerId = 0 ; if (cxn->state != cxnIdleS) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } /* it's doubtful (right?) that this timer could go off and there'd still be articles in the queue. */ if (cxn->articleQTotal > 0) { syslog (LOG_WARNING, ARTICLE_TIMEOUT_W_Q_MSG, peerName, cxn->ident) ; } else { syslog (LOG_WARNING, ARTICLE_TIMEOUT_MSG, peerName, cxn->ident) ; cxn->state = cxnIdleTimeoutS ; cxnFlush (cxn) ; } } /* * function to be called when the fd is not ready for reading, but there is * an article on tape or in the queue to be done. Things are done this way * so that a Connection doesn't hog time trying to find the next good * article for writing. With a large backlog of expired articles that would * take a long time. Instead the Connection just tries its next article on * tape or queue, and if that's no good then it registers this callback so * that other Connections have a chance of being serviced. */ static void cxnWorkProc (EndPoint ep, void *data) { Connection cxn = (Connection) data ; (void) ep ; /* keep lint happy */ dprintf (2,"%s:%d calling work proc\n", hostPeerName (cxn->myHost),cxn->ident) ; if (writesNeeded (cxn)) doSomeWrites (cxn) ; /* may re-register the work proc... */ else if (cxn->state == cxnFlushingS || cxn->state == cxnClosingS) { if (cxn->articleQTotal == 0) issueQUIT (cxn) ; } else dprintf (2,"%s:%d no writes were needed....\n", hostPeerName (cxn->myHost), cxn->ident) ; } /**************************************************************************** * * END EndPoint callback area. * ****************************************************************************/ /**************************************************************************** * * REPONSE CODE PROCESSING. * ***************************************************************************/ /* * A connection needs to sleep, but if it's closing it needs to die instead. */ static void cxnSleepOrDie (Connection cxn) { if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnSleep (cxn) ; } /* * Handle the response 205 to our QUIT command, which means the * remote is going away and we can happily cleanup */ static void processResponse205 (Connection cxn, char *response) { bool immedRecon ; VALIDATE_CONNECTION (cxn) ; (void) response ; /* keep lint happy */ if (!(cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } switch (cxn->state) { case cxnFlushingS: case cxnClosingS: ASSERT (cxn->articleQTotal == 0) ; cxnLogStats (cxn,true) ; immedRecon = cxn->immedRecon ; hostCxnDead (cxn->myHost,cxn) ; if (cxn->state == cxnFlushingS && immedRecon) { abortConnection (cxn) ; if (!cxnConnect (cxn)) syslog (LOG_NOTICE,CXN_REOPEN_FAILED,hostPeerName (cxn->myHost), cxn->ident) ; } else if (cxn->state == cxnFlushingS) cxnWait (cxn) ; else cxnDead (cxn) ; break ; case cxnIdleS: case cxnFeedingS: /* this shouldn't ever happen... */ syslog (LOG_NOTICE,BAD_RESPONSE,hostPeerName (cxn->myHost), cxn->ident, 205) ; cxnSleepOrDie (cxn) ; break ; default: die ("Bad connection state: %s\n",stateToString (cxn->state)) ; } } /* * Handle a response code of 238 which is the "no such article" * reply to the CHECK command (i.e. remote wants it). */ static void processResponse238 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { syslog (LOG_ERR,CXN_STREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,238) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,238,msgid,response) ; else { /* now remove the article from the check queue and move it onto the transmit queue. Another function wil take care of transmitting */ remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->state != cxnClosingS) appendArtHolder (artHolder, &cxn->takeHead, &cxn->articleQTotal) ; else { hostTakeBackArticle (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } } if (msgid != NULL) FREE (msgid) ; } /* * process the response "try again later" to the CHECK command If this * returns true then the connection is still usable. */ static void processResponse431 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { syslog (LOG_ERR,CXN_STREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,431) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,431,msgid,response) ; else { remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleDeferred (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) FREE (msgid) ; } /* * process the "already have it" response to the CHECK command. If this * returns true then the connection is still usable. */ static void processResponse438 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { syslog (LOG_ERR,CXN_STREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->checkRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,438) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->checkRespHead)) == NULL) noSuchMessageId (cxn,438,msgid,response) ; else { cxn->checksRefused++ ; remArtHolder (artHolder, &cxn->checkRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleNotWanted (cxn->myHost, cxn, artHolder->article); delArtHolder (artHolder) ; } if (msgid != NULL) FREE (msgid) ; } /* * process the "article transferred ok" response to the TAKETHIS. */ static void processResponse239 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { syslog (LOG_ERR,CXN_STREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,239) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->takeRespHead)) == NULL) noSuchMessageId (cxn,239,msgid,response) ; else { cxn->takesOkayed++ ; remArtHolder (artHolder, &cxn->takeRespHead, &cxn->articleQTotal) ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleAccepted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) FREE (msgid) ; } /* * Set the thresholds for no-CHECK mode; negative means leave existing value */ void cxnSetCheckThresholds (Connection cxn, double lowFilter, double highFilter, double lowPassFilter) { /* Adjust current value for new scaling */ if (cxn->lowPassFilter > 0.0) cxn->filterValue = cxn->filterValue / cxn->lowPassFilter * lowPassFilter; /* Stick in new values */ if (highFilter >= 0) cxn->onThreshold = highFilter * lowPassFilter / 100.0; if (lowFilter >= 0) cxn->offThreshold = lowFilter * lowPassFilter / 100.0; cxn->lowPassFilter = lowPassFilter; } /* * Blow away the connection gracelessly and immedately clean up */ void cxnNuke (Connection cxn) { abortConnection (cxn) ; hostCxnDead (cxn->myHost,cxn) ; delConnection(cxn) ; } /* * process a "article rejected do not try again" response to the * TAKETHIS. */ static void processResponse439 (Connection cxn, char *response) { char *msgid ; ArtHolder artHolder ; if (!cxn->doesStreaming) { syslog (LOG_ERR,CXN_STREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; msgid = getMsgId (response) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,239) ; cxnSleepOrDie (cxn) ; } else if (msgid == NULL || strlen (msgid) == 0 || (artHolder = artHolderByMsgId (msgid, cxn->takeRespHead)) == NULL) noSuchMessageId (cxn,439,msgid,response) ; else { cxn->takesRejected++ ; remArtHolder (artHolder, &cxn->takeRespHead, &cxn->articleQTotal) ; /* Some(?) hosts return the 439 response even before we're done sending */ if (cxn->articleQTotal == 0 && !writeIsPending(cxn->myEp)) cxnIdle (cxn) ; hostArticleRejected (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } if (msgid != NULL) FREE (msgid) ; } /* * process the "article transferred ok" response to the IHAVE-body. */ static void processResponse235 (Connection cxn, char *response) { ArtHolder artHolder ; (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; if (cxn->takeRespHead == NULL) /* peer is confused */ { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,235) ; cxnSleepOrDie (cxn) ; } else { /* now remove the article from the queue and tell the Host to forget about it. */ artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; cxn->articleQTotal = 0 ; cxn->takesOkayed++ ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleAccepted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } } /* * process the "send article to be transfered" reponse to the IHAVE. */ static void processResponse335 (Connection cxn, char *response) { (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } if (cxn->checkRespHead == NULL) { syslog (LOG_NOTICE,BAD_RESPONSE, hostPeerName (cxn->myHost),cxn->ident,335) ; cxnSleepOrDie (cxn) ; } else { VALIDATE_CONNECTION (cxn) ; /* now move the article into the third queue */ cxn->takeHead = cxn->checkRespHead ; cxn->checkRespHead = NULL ; issueIHAVEBody (cxn) ; } } /* * process the "not accepting articles" response. This could be to any of * the IHAVE/CHECK/TAKETHIS command, but not the banner--that's handled * elsewhere. */ static void processResponse400 (Connection cxn, char *response) { if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; /* We may get a response 400 multiple times when in streaming mode. */ syslog (LOG_NOTICE,CXN_BLOCKED,hostPeerName(cxn->myHost),cxn->ident, response) ; /* right here there may still be data queued to write and so we'll fail trying to issue the quit ('cause a write will be pending). Furthermore, the data pending may be half way through an command, and so just tossing the buffer is nt sufficient. But figuring out where we are and doing a tidy job is hard */ if (writeIsPending (cxn->myEp)) cxnSleepOrDie (cxn) ; else { if (cxn->articleQTotal > 0) { /* Defer the articles here so that cxnFlush() doesn't set up an immediate reconnect. */ deferAllArticles (cxn) ; clearTimer (cxn->readBlockedTimerId) ; /* XXX - so cxnSleep() doesn't die when it validates the connection */ cxnIdle (cxn) ; } /* XXX - it would be nice if we QUIT first, but we'd have to go into a state where we just search for the 205 response, and only go into the sleep state at that point */ cxnSleepOrDie (cxn) ; } } /* * process the "not wanted" reponse to the IHAVE. */ static void processResponse435 (Connection cxn, char *response) { ArtHolder artHolder ; (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->checkRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; cxn->checksRefused++ ; artHolder = cxn->checkRespHead ; cxn->checkRespHead = NULL ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleNotWanted (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; #if 0 dprintf (1,"%s:%d On exiting 435 article queue total is %d (%d %d %d %d)\n", hostPeerName (cxn->myHost), cxn->ident, cxn->articleQTotal, (int) (cxn->checkHead != NULL), (int) (cxn->checkRespHead != NULL), (int) (cxn->takeHead != NULL), (int) (cxn->takeRespHead != NULL)); #endif } /* * process the "transfer failed" response to the IHAVE-body, (seems this * can come from the IHAVE too). */ static void processResponse436 (Connection cxn, char *response) { ArtHolder artHolder ; (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL || cxn->checkRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; if (cxn->takeRespHead != NULL) /* IHAVE-body command barfed */ { artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; } else /* IHAVE command barfed */ { artHolder = cxn->checkRespHead ; cxn->checkRespHead = NULL ; } if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleDeferred (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } /* * Process the "article rejected do not try again" response to the * IHAVE-body. */ static void processResponse437 (Connection cxn, char *response) { ArtHolder artHolder ; (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeRespHead != NULL) ; VALIDATE_CONNECTION (cxn) ; cxn->articleQTotal-- ; cxn->takesRejected++ ; artHolder = cxn->takeRespHead ; cxn->takeRespHead = NULL ; if (cxn->articleQTotal == 0) cxnIdle (cxn) ; hostArticleRejected (cxn->myHost, cxn, artHolder->article) ; delArtHolder (artHolder) ; } /* Process the response 480 Transfer permission defined. We're probably talking to a remote nnrpd on a system that forgot to put us in the hosts.nntp */ static void processResponse480 (Connection cxn, char *response) { (void) response ; /* keep lint happy */ if (cxn->doesStreaming) { syslog (LOG_ERR,CXN_NONSTREAM_RESP,hostPeerName (cxn->myHost), cxn->ident,response) ; cxnSleepOrDie (cxn) ; return ; } if (!(cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } VALIDATE_CONNECTION (cxn) ; syslog (LOG_NOTICE,NO_TRANSFER_NNRPD,hostPeerName (cxn->myHost), cxn->ident) ; if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnSleep (cxn) ; } /**************************************************************************** * * END REPONSE CODE PROCESSING. * ***************************************************************************/ /* * puts the Connection into the sleep state. */ static void cxnSleep (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnIdleS || cxn->state == cxnFeedingS || cxn->state == cxnConnectingS) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; prepareReopenCbk (cxn) ; /* XXX - we don't want to reopen if idle */ cxn->state = cxnSleepingS ; /* tell our Host we're asleep so it doesn't try to give us articles */ hostCxnSleeping (cxn->myHost,cxn) ; } static void cxnDead (Connection cxn) { ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; abortConnection (cxn) ; cxn->state = cxnDeadS ; } /* * Sets the idle timer. If no articles arrive before the timer expires, the * connection will be closed. */ static void cxnIdle (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state == cxnFeedingS || cxn->state == cxnConnectingS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; ASSERT (cxn->sleepTimerId == 0) ; if (cxn->state == cxnFeedingS || cxn->state == cxnConnectingS) { if (cxn->articleReceiptTimeout > 0) { clearTimer (cxn->artReceiptTimerId) ; cxn->artReceiptTimerId = prepareSleep (articleTimeoutCbk, cxn->articleReceiptTimeout, cxn) ; } if (cxn->readTimeout > 0 && cxn->state == cxnFeedingS) clearTimer (cxn->readBlockedTimerId) ; cxn->state = cxnIdleS ; ASSERT (cxn->readBlockedTimerId == 0) ; } } /* * Called when a response from the remote refers to a non-existant * message-id. The network connection is aborted and the Connection * object goes into sleep mode. */ static void noSuchMessageId (Connection cxn, u_int responseCode, const char *msgid, const char *response) { const char *peerName = hostPeerName (cxn->myHost) ; if (msgid == NULL || strlen (msgid) == 0) syslog (LOG_ERR, NOMSGID, peerName, cxn->ident, responseCode, response) ; else syslog (LOG_ERR, INVALID_MSGID, peerName, cxn->ident, responseCode, msgid) ; cxnLogStats (cxn,true) ; if (cxn->state != cxnClosingS) cxnSleep (cxn) ; else cxnDead (cxn) ; } /* * a processing error has occured (for example in parsing a response), or * we're at the end of the FSM and we're cleaning up. */ static void abortConnection (Connection cxn) { ASSERT (cxn != NULL) ; VALIDATE_CONNECTION (cxn) ; /* cxn->myEp could be NULL if we get here during cxnConnect (via cxnWait()) */ if (cxn->myEp != NULL) { delEndPoint (cxn->myEp) ; cxn->myEp = NULL ; } clearTimer (cxn->sleepTimerId) ; clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->readBlockedTimerId) ; clearTimer (cxn->writeBlockedTimerId) ; clearTimer (cxn->flushTimerId) ; deferAllArticles (cxn) ; /* give any articles back to Host */ bufferSetDataSize (cxn->respBuffer,0) ; resetConnection (cxn) ; if (cxn->state == cxnFeedingS || cxn->state == cxnIdleS || cxn->state == cxnFlushingS || cxn->state == cxnClosingS) hostCxnDead (cxn->myHost,cxn) ; } /* * Set up the callback used when the Connection is sleeping (i.e. will try * to reopen the connection). */ static void prepareReopenCbk (Connection cxn) { ASSERT (cxn->sleepTimerId == 0) ; if (!(cxn->state == cxnConnectingS || cxn->state == cxnIdleS || cxn->state == cxnFeedingS || cxn->state == cxnFlushingS || cxn->state == cxnStartingS)) { syslog (LOG_ERR,CXN_BAD_STATE,hostPeerName (cxn->myHost), cxn->ident,stateToString (cxn->state)) ; cxnSleepOrDie (cxn) ; return ; } dprintf (1,"%s:%d Setting up a reopen callback\n", hostPeerName (cxn->myHost), cxn->ident) ; cxn->sleepTimerId = prepareSleep (reopenTimeoutCbk, cxn->sleepTimeout, cxn) ; /* bump the sleep timer amount each time to wait longer and longer. Gets reset in resetConnection() */ cxn->sleepTimeout *= 2 ; if (cxn->sleepTimeout > max_reconnect_period) cxn->sleepTimeout = max_reconnect_period ; } /* * (re)set all state variables to inital condition. */ static void resetConnection (Connection cxn) { ASSERT (cxn != NULL) ; bufferSetDataSize (cxn->respBuffer,0) ; cxn->loggedNoCr = false ; cxn->maxCheck = 1 ; cxn->immedRecon = false ; cxn->doesStreaming = false ; /* who knows, next time around maybe... */ cxn->quitWasIssued = false ; cxn->needsChecks = true ; cxn->timeCon = 0 ; cxn->artsTaken = 0 ; cxn->checksIssued = 0 ; cxn->checksRefused = 0 ; cxn->takesRejected = 0 ; cxn->takesOkayed = 0 ; cxn->filterValue = 0.0 ; } /* * Give back all articles that are queued, but not actually in progress. * XXX merge come of this with deferAllArticles */ static void deferQueuedArticles (Connection cxn) { ArtHolder p, q ; for (q = NULL, p = cxn->checkHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkHead = NULL ; for (q = NULL, p = cxn->takeHead ; cxn->doesStreaming && p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeHead = NULL ; } /* * Give back any articles we have to the Host for later retrys. */ static void deferAllArticles (Connection cxn) { ArtHolder p, q ; for (q = NULL, p = cxn->checkHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkHead = NULL ; for (q = NULL, p = cxn->checkRespHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->checkRespHead = NULL ; for (q = NULL, p = cxn->takeHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeHead = NULL ; for (q = NULL, p = cxn->takeRespHead ; p != NULL ; p = q) { q = p->next ; hostTakeBackArticle (cxn->myHost, cxn, p->article) ; delArtHolder (p) ; cxn->articleQTotal-- ; } cxn->takeRespHead = NULL ; ASSERT (cxn->articleQTotal == 0) ; } /* * Called when there's an article to be pushed out to the remote. Even if * the Connection has an article it's possible that nothing will be written * (e.g. if the article on the queue doesn't exist any more) */ static void doSomeWrites (Connection cxn) { bool doneSome = false ; /* If there's a write pending we can't do anything now. */ if ( writeIsPending (cxn->myEp) ) return ; else if ( writesNeeded (cxn) ) /* something on a queue. */ { if (cxn->doesStreaming) doneSome = issueStreamingCommands (cxn) ; else doneSome = issueIHAVE (cxn) ; /* doneSome will be false if article(s) were gone, but if the Host has something available, then it would have been put on the queue for next time around. */ if (!doneSome) { if (writesNeeded (cxn)) /* Host gave us something */ addWorkCallback (cxn->myEp,cxnWorkProc,cxn) ; /* for next time. */ else if (cxn->articleQTotal == 0) { /* if we were in cxnFeedingS, then issueStreamingCommands already called cxnIdle(). */ if (cxn->state == cxnClosingS || cxn->state == cxnFlushingS) issueQUIT (cxn) ; /* and nothing to wait for... */ } } } else if (cxn->state == cxnClosingS || cxn->state == cxnFlushingS) { /* nothing to do... */ if (cxn->articleQTotal == 0) issueQUIT (cxn) ; /* and nothing to wait for before closing */ } } /* Queue up a buffer with the IHAVE command in it for the article at * the head of the transmisson queue. * * If the article is missing, then the Host will be notified and * another article may be put on the Connections queue. This new * article is ignored for now, but a work callback is registered so * that it can be looked at later. */ static bool issueIHAVE (Connection cxn) { Buffer ihaveBuff, *writeArr ; ArtHolder artH ; Article article ; const char *msgid ; char *p ; u_int tmp ; size_t bufLen = 256 ; bool rval = false ; ASSERT (!cxn->doesStreaming) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->checkHead != NULL) ; ASSERT (writeIsPending (cxn->myEp) == false) ; VALIDATE_CONNECTION (cxn) ; artH = cxn->checkHead ; article = cxn->checkHead->article ; msgid = artMsgId (artH->article) ; ASSERT (msgid != NULL) ; ASSERT (article != NULL) ; #if 0 if ( !artFileIsValid (article) ) { cxn->checkHead = NULL ; cxn->articleQTotal-- ; hostArticleIsMissing (cxn->myHost, cxn, article) ; delArtHolder (artH) ; } else #endif { if ((tmp = (strlen (msgid) + 10)) > bufLen) bufLen = tmp ; ihaveBuff = newBuffer (bufLen) ; ASSERT (ihaveBuff != NULL) ; p = bufferBase (ihaveBuff) ; sprintf (p, "IHAVE %s\r\n", msgid) ; bufferSetDataSize (ihaveBuff, strlen (p)) ; dprintf (5,"%s:%d Command IHAVE %s\n", hostPeerName (cxn->myHost),cxn->ident,msgid) ; writeArr = makeBufferArray (ihaveBuff, NULL) ; if ( !prepareWriteWithTimeout (cxn->myEp, writeArr, commandWriteDone, cxn) ) { syslog (LOG_ERR, PREPARE_WRITE_FAILED, hostPeerName (cxn->myHost), cxn->ident) ; die ("Prepare write for IHAVE failed") ; } /* now move the article to the second queue */ cxn->checkRespHead = cxn->checkHead ; cxn->checkHead = NULL ; cxn->checksIssued++ ; hostArticleOffered (cxn->myHost, cxn) ; rval = true ; } return rval ; } /* * Do a prepare write with the article body as the body portion of the * IHAVE command */ static void issueIHAVEBody (Connection cxn) { Buffer *writeArray ; Article article ; #ifdef XXX_RAWHACK static int XXXboguscount = 0; time_t now = theTime(); #endif /* XXX_RAWHACK */ ASSERT (cxn != NULL) ; ASSERT (!cxn->doesStreaming) ; ASSERT (cxn->state == cxnFlushingS || cxn->state == cxnFeedingS || cxn->state == cxnClosingS) ; ASSERT (cxn->articleQTotal == 1) ; ASSERT (cxn->takeHead != NULL) ; VALIDATE_CONNECTION (cxn) ; article = cxn->takeHead->article ; ASSERT (article != NULL) ; if (cxn->state != cxnClosingS) writeArray = artGetNntpBuffers (article) ; else writeArray = NULL ; if (writeArray == NULL) { /* missing article (expired for example) will get us here. */ if (dotBuffer == NULL) { dotBuffer = newBufferByCharP (".\r\n",3,3) ; dotFirstBuffer = newBufferByCharP ("\r\n.",3,3) ; crlfBuffer = newBufferByCharP ("\r\n",2,2) ; } /* we'll just write the empty buffer and the remote will complain with 437 */ writeArray = makeBufferArray (bufferTakeRef (dotBuffer),NULL) ; } if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, ihaveBodyDone, cxn) ) { syslog (LOG_ERR, PREPARE_WRITE_FAILED, hostPeerName (cxn->myHost), cxn->ident) ; die ("Preparewrite failed in issueIHAVEBody") ; } else { dprintf (5,"%s:%d prepared write for IHAVE body.\n", hostPeerName (cxn->myHost),cxn->ident) ; } /* now move the article to the last queue */ cxn->takeRespHead = cxn->takeHead ; cxn->takeHead = NULL ; return ; } /* Process the two command queues. Slaps all the CHECKs together and * then does the TAKETHIS commands. * * If no articles on the queue(s) are valid, then the Host is * notified. It may queue up new articles on the Connection, but * these are ignored for now. A work proc is registered so the * articles can be processed later. */ static bool issueStreamingCommands (Connection cxn) { Buffer checkBuffer = NULL ; /* the buffer with the CHECK commands in it. */ Buffer *writeArray = NULL ; ArtHolder p, q ; bool rval = false ; ASSERT (cxn != NULL) ; ASSERT (cxn->myEp != NULL) ; ASSERT (cxn->doesStreaming) ; VALIDATE_CONNECTION (cxn) ; checkBuffer = buildCheckBuffer (cxn) ; /* may be null if none to issue */ if (checkBuffer != NULL) { /* Now shift the articles to their new queue. */ for (p = cxn->checkRespHead ; p != NULL && p->next != NULL ; p = p->next) /* nada--finding end of queue*/ ; if (p == NULL) cxn->checkRespHead = cxn->checkHead ; else p->next = cxn->checkHead ; cxn->checkHead = NULL ; } writeArray = buildTakethisBuffers (cxn,checkBuffer) ; /* may be null */ /* If not null, then writeArray will have checkBuffer (if it wasn't NULL) in the first spot and the takethis buffers after that. */ if (writeArray) { if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, commandWriteDone, cxn) ) { syslog (LOG_ERR, PREPARE_WRITE_FAILED, hostPeerName (cxn->myHost), cxn->ident) ; die ("Prepare write for STREAMING commands failed") ; } rval = true ; /* now shift articles over to their new queue. */ for (p = cxn->takeRespHead ; p != NULL && p->next != NULL ; p = p->next) /* nada--finding end of queue */ ; if (p == NULL) cxn->takeRespHead = cxn->takeHead ; else p->next = cxn->takeHead ; cxn->takeHead = NULL ; } /* we defer the missing article notification to here because if there was a big backlog of missing articles *and* we're running in no-CHECK mode, then the Host would be putting bad articles on the queue we're taking them off of. */ if (cxn->missing && cxn->articleQTotal == 0) cxnIdle (cxn) ; for (p = cxn->missing ; p != NULL ; p = q) { hostArticleIsMissing (cxn->myHost, cxn, p->article) ; q = p->next ; delArtHolder (p) ; } cxn->missing = NULL ; return rval ; } /* * build up the buffer of all the CHECK commands. */ static Buffer buildCheckBuffer (Connection cxn) { ArtHolder p ; size_t lenBuff = 0 ; Buffer checkBuffer = NULL ; const char *peerName = hostPeerName (cxn->myHost) ; p = cxn->checkHead ; while (p != NULL) { Article article = p->article ; const char *msgid ; msgid = artMsgId (article) ; lenBuff += (8 + strlen (msgid)) ; /* 8 == strlen("CHECK \r\n") */ p = p->next ; } if (lenBuff > 0) lenBuff++ ; /* for the null byte */ /* now build up the single buffer that contains all the CHECK commands */ if (lenBuff > 0) { char *t ; size_t tlen = 0 ; checkBuffer = newBuffer (lenBuff) ; t = bufferBase (checkBuffer) ; p = cxn->checkHead ; while (p != NULL) { const char *msgid = artMsgId (p->article) ; sprintf (t,"CHECK %s\r\n", msgid) ; dprintf (5,"%s:%d Command %s", peerName, cxn->ident, t) ; tlen += strlen (t) ; while ( *t ) t++ ; cxn->checksIssued++ ; hostArticleOffered (cxn->myHost,cxn) ; p = p->next ; } ASSERT (tlen + 1 == lenBuff) ; bufferSetDataSize (checkBuffer, tlen) ; } return checkBuffer ; } /* * Construct and array of TAKETHIS commands and the command bodies. Any * articles on the queue that are missing will be removed and the Host will * be informed. */ static Buffer *buildTakethisBuffers (Connection cxn, Buffer checkBuffer) { size_t lenArray = 0 ; ArtHolder p, q ; Buffer *rval = NULL ; const char *peerName = hostPeerName (cxn->myHost) ; if (checkBuffer != NULL) lenArray++ ; if (cxn->takeHead != NULL) /* some TAKETHIS commands to be done. */ { Buffer takeBuffer ; u_int takeBuffLen ; u_int writeIdx = 0 ; /* count up all the buffers we'll be writing. One extra each time for the TAKETHIS command buffer*/ for (p = cxn->takeHead ; p != NULL ; p = p->next) if (artContentsOk (p->article)) lenArray += (1 + artNntpBufferCount (p->article)) ; /* now allocate the array for the buffers and put them all in it */ rval = ALLOC (Buffer, lenArray + 1) ; /* 1 for the terminator */ ASSERT (rval != NULL) ; if (checkBuffer != NULL) rval [writeIdx++] = checkBuffer ; q = NULL ; p = cxn->takeHead ; while (p != NULL) { char *t ; const char *msgid ; Article article ; Buffer *articleBuffers ; int i, nntpLen ; article = p->article ; nntpLen = artNntpBufferCount (article) ; msgid = artMsgId (article) ; if (nntpLen == 0) { /* file no longer valid so drop from queue */ ArtHolder ta = p ; if (q == NULL) /* it's the first in the queue */ cxn->takeHead = p->next ; else q->next = p->next ; p = p->next ; ASSERT (cxn->articleQTotal > 0) ; cxn->articleQTotal-- ; ta->next = cxn->missing ; cxn->missing = ta ; } else { articleBuffers = artGetNntpBuffers (article) ; /* set up the buffer with the TAKETHIS command in it. 12 == strlen ("TAKETHIS \n\r") */ takeBuffLen = 12 + strlen (msgid) ; takeBuffer = newBuffer (takeBuffLen) ; t = bufferBase (takeBuffer) ; sprintf (t, "TAKETHIS %s\r\n", msgid) ; bufferSetDataSize (takeBuffer, strlen (t)) ; dprintf (5,"%s:%d Command %s", peerName, cxn->ident, t) ; ASSERT (writeIdx <= lenArray) ; rval [writeIdx++] = takeBuffer ; /* now add all the buffers that make up the body of the TAKETHIS command */ for (i = 0 ; i < nntpLen ; i++) { ASSERT (writeIdx <= lenArray) ; rval [writeIdx++] = bufferTakeRef (articleBuffers [i]) ; } freeBufferArray (articleBuffers) ; if ( !cxn->needsChecks ) { /* this isn't quite right. An article may be counted twice if we switch to no-CHECK mode after its CHECK was issued, but before its TAKETHIS was done just now. I'm not going to worry unless someone complains. */ cxn->checksIssued++ ; hostArticleOffered (cxn->myHost,cxn) ; } q = p ; p = p->next ; } } if (writeIdx > 0) rval [writeIdx] = NULL ; else { /* all articles were missing and no CHECKS */ FREE (rval) ; rval = NULL ; } } else if (checkBuffer != NULL) /* no TAKETHIS to do, but some CHECKS */ rval = makeBufferArray (checkBuffer, NULL) ; return rval ; } /* * for one reason or another we need to disconnect gracefully. We send a * QUIT command. */ static void issueQUIT (Connection cxn) { Buffer quitBuffer, *writeArray ; const char *peerName = hostPeerName (cxn->myHost) ; ASSERT (cxn->takeHead == NULL) ; ASSERT (cxn->checkHead == NULL) ; VALIDATE_CONNECTION (cxn) ; if (cxn->quitWasIssued) return ; if (writeIsPending (cxn->myEp)) { syslog (LOG_ERR, QUIT_WHILE_WRITING, peerName, cxn->ident) ; if (cxn->state == cxnClosingS) cxnDead (cxn) ; else cxnWait (cxn) ; } else { quitBuffer = newBuffer (7) ; strcpy (bufferBase (quitBuffer), "QUIT\r\n") ; bufferSetDataSize (quitBuffer, 6) ; writeArray = makeBufferArray (quitBuffer, NULL) ; dprintf (1,"%s:%d Sending a quit command\n", hostPeerName (cxn->myHost),cxn->ident) ; cxn->quitWasIssued = true ; /* not exactly true, but good enough */ if ( !prepareWriteWithTimeout (cxn->myEp, writeArray, quitWritten, cxn) ) { syslog (LOG_ERR, PREPARE_WRITE_FAILED, peerName, cxn->ident) ; die ("Prepare write for QUIT command failed") ; } } } /* * Set up the timer for the blocked reads */ static void initReadBlockedTimeout (Connection cxn) { ASSERT (cxn != NULL) ; ASSERT (cxn->state != cxnIdleS ) ; /* set up the response timer. */ clearTimer (cxn->readBlockedTimerId) ; if (cxn->readTimeout > 0) cxn->readBlockedTimerId = prepareSleep (responseTimeoutCbk, cxn->readTimeout, cxn) ; } /* * Set up the timer for the blocked reads */ static int prepareWriteWithTimeout (EndPoint endp, Buffer *buffers, EndpRWCB done, Connection cxn) { /* Clear the read timer, since we can't expect a response until everything is sent. XXX - would be nice to have a timeout for reponses if we're sending a string of commands. */ clearTimer (cxn->readBlockedTimerId) ; /* set up the write timer. */ clearTimer (cxn->writeBlockedTimerId) ; if (cxn->writeTimeout > 0) cxn->writeBlockedTimerId = prepareSleep (writeTimeoutCbk, cxn->writeTimeout, cxn) ; /* set up the write. */ return prepareWrite (endp, buffers, writeProgress, done, cxn) ; } /* * Does the actual deletion of a connection and all its private data. */ static void delConnection (Connection cxn) { bool shutDown; Connection c, q; if (cxn == NULL) return ; dprintf (1,"Deleting connection: %s:%d\n", hostPeerName (cxn->myHost),cxn->ident) ; for (c = gCxnList, q = NULL ; c != NULL ; q = c, c = c->next) if (c == cxn) { if (gCxnList == c) gCxnList = gCxnList->next ; else q->next = c->next ; break ; } ASSERT (c != NULL) ; if (cxn->myEp != NULL) delEndPoint (cxn->myEp) ; ASSERT (cxn->checkHead == NULL) ; ASSERT (cxn->checkRespHead == NULL) ; ASSERT (cxn->takeHead == NULL) ; ASSERT (cxn->takeRespHead == NULL) ; delBuffer (cxn->respBuffer) ; /* tell the Host we're outta here. */ shutDown = hostCxnGone (cxn->myHost, cxn) ; cxn->ident = 0 ; cxn->timeCon = 0 ; FREE (cxn->ipName) ; clearTimer (cxn->artReceiptTimerId) ; clearTimer (cxn->readBlockedTimerId) ; clearTimer (cxn->writeBlockedTimerId) ; clearTimer (cxn->flushTimerId) ; FREE (cxn) ; if (shutDown) { /* exit program if that was the last connexion for the last host */ /* XXX what about if there are ever multiple listeners? XXX this will be executed if all hosts on only one of the XXX listeners have gone */ time_t now = theTime () ; char dateString [30] ; register char **p = PointersFreedOnExit ; /* finish out all outstanding memory */ while (*p) FREE (*p++) ; FREE (PointersFreedOnExit) ; freeTimeoutQueue () ; strcpy (dateString,ctime (&now)) ; dateString [24] = '\0' ; syslog (LOG_NOTICE,STOPPING_PROGRAM,dateString) ; exit (0) ; } } /* * Bump up the value of the low pass filter on the connection. */ static void incrFilter (Connection cxn) { cxn->filterValue *= (1.0 - (1.0 / cxn->lowPassFilter)) ; cxn->filterValue += 1.0 ; } /* * decrement the value of the low pass filter on the connection. */ static void decrFilter (Connection cxn) { cxn->filterValue *= (1.0 - (1.0 / cxn->lowPassFilter)) ; } /* * return true if we have articles we need to issue commands for. */ static bool writesNeeded (Connection cxn) { return (cxn->checkHead != NULL || cxn->takeHead != NULL ? true : false) ; } /* * do some simple tests to make sure it's OK. */ static void validateConnection (Connection cxn) { u_int i ; u_int old ; ArtHolder p ; i = 0 ; /* count up the articles the Connection has and make sure that matches. */ for (p = cxn->takeHead ; p != NULL ; p = p->next) i++ ; dprintf (4,"TAKE queue: %d\n",i) ; old = i ; for (p = cxn->takeRespHead ; p != NULL ; p = p->next) i++ ; dprintf (4,"TAKE response queue: %d\n",i - old) ; old = i ; for (p = cxn->checkHead ; p != NULL ; p = p->next) i++ ; dprintf (4,"CHECK queue: %d\n",i - old) ; old = i ; for (p = cxn->checkRespHead ; p != NULL ; p = p->next) i++ ; dprintf (4,"CHECK response queue: %d\n",i - old) ; ASSERT (i == cxn->articleQTotal) ; switch (cxn->state) { case cxnConnectingS: ASSERT (cxn->doesStreaming == false) ; ASSERT (cxn->articleQTotal <= 1) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; /* ASSERT (cxn->timeCon == 0) ; */ break ; case cxnWaitingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnFlushingS: case cxnClosingS: if (!cxn->doesStreaming) ASSERT (cxn->articleQTotal <= 1) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (cxn->doesStreaming || cxn->maxCheck == 1) ; break ; case cxnFeedingS: if (cxn->doesStreaming) /* Some(?) hosts return the 439 response even before we're done sending, so don't go idle until here */ ASSERT (cxn->articleQTotal > 0 || writeIsPending (cxn->myEp)) ; else ASSERT (cxn->articleQTotal == 1) ; if (cxn->readTimeout > 0 && !writeIsPending (cxn->myEp) && cxn->checkRespHead != NULL && cxn->takeRespHead != NULL) ASSERT (cxn->readBlockedTimerId != 0) ; if (cxn->writeTimeout > 0 && writeIsPending (cxn->myEp)) ASSERT (cxn->writeBlockedTimerId != 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (cxn->doesStreaming || cxn->maxCheck == 1) ; break; case cxnIdleS: ASSERT (cxn->articleQTotal == 0) ; if (cxn->articleReceiptTimeout > 0) ASSERT (cxn->artReceiptTimerId != 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; break ; case cxnIdleTimeoutS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon != 0) ; ASSERT (!writeIsPending (cxn->myEp)) ; break ; case cxnSleepingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnStartingS: ASSERT (cxn->articleQTotal == 0) ; ASSERT (cxn->myEp == NULL) ; ASSERT (cxn->artReceiptTimerId == 0) ; ASSERT (cxn->readBlockedTimerId == 0) ; ASSERT (cxn->writeBlockedTimerId == 0) ; ASSERT (cxn->flushTimerId == 0) ; ASSERT (cxn->sleepTimerId == 0) ; ASSERT (cxn->timeCon == 0) ; break ; case cxnDeadS: break ; } } /* * Generate a printable string of the parameter. */ static const char *stateToString (CxnState state) { static char rval [64] ; switch (state) { case cxnStartingS: strcpy (rval,"cxnStartingS") ; break ; case cxnWaitingS: strcpy (rval,"cxnWaitingS") ; break ; case cxnConnectingS: strcpy (rval,"cxnConnectingS") ; break ; case cxnIdleS: strcpy (rval,"cxnIdleS") ; break ; case cxnIdleTimeoutS: strcpy (rval,"cxnIdleTimeoutS") ; break ; case cxnFeedingS: strcpy (rval,"cxnFeedingS") ; break ; case cxnSleepingS: strcpy (rval,"cxnSleepingS") ; break ; case cxnFlushingS: strcpy (rval,"cxnFlushingS") ; break ; case cxnClosingS: strcpy (rval,"cxnClosingS") ; break ; case cxnDeadS: strcpy (rval,"cxnDeadS") ; break ; default: sprintf (rval,"UNKNOWN STATE: %d",state) ; break ; } return rval ; } /**************************************************************************** * * Functions for managing the internal queue of Articles on each Connection. * ****************************************************************************/ static ArtHolder newArtHolder (Article article) { ArtHolder a = ALLOC (struct art_holder_s, 1) ; ASSERT (a != NULL) ; a->article = article ; a->next = NULL ; return a ; } /* * Deletes the article holder */ static void delArtHolder (ArtHolder artH) { if (artH != NULL) FREE (artH) ; } /* * remove the article holder from the queue. Adjust the count and if nxtPtr * points at the element then adjust that too. */ static bool remArtHolder (ArtHolder artH, ArtHolder *head, u_int *count) { ArtHolder h, i ; ASSERT (head != NULL) ; ASSERT (count != NULL) ; h = *head ; i = NULL ; while (h != NULL && h != artH) { i = h ; h = h->next ; } if (h == NULL) return false ; if (i == NULL) *head = (*head)->next ; else i->next = artH->next ; (*count)-- ; return true ; } /* * append the ArticleHolder to the queue */ static void appendArtHolder (ArtHolder artH, ArtHolder *head, u_int *count) { ArtHolder p ; ASSERT (head != NULL) ; ASSERT (count != NULL) ; for (p = *head ; p != NULL && p->next != NULL ; p = p->next) /* nada */ ; if (p == NULL) *head = artH ; else p->next = artH ; artH->next = NULL ; (*count)++ ; } /* * find the article holder on the queue by comparing the message-id. */ static ArtHolder artHolderByMsgId (const char *msgid, ArtHolder head) { while (head != NULL) { if (strcmp (msgid, artMsgId (head->article)) == 0) return head ; head = head->next ; } return NULL ; } /* * Randomize a numeber by the given percentage */ static int fudgeFactor (int initVal) { int newValue ; static bool seeded ; if ( !seeded ) { time_t t = theTime () ; /* this may have been done already in endpoint.c. Is that a problem??? */ srand (t) ; seeded = true ; } newValue = initVal + (initVal / 10 - (rand() % (initVal / 5))); return newValue ; } innfeed-0.10.1.7.orig/connection.h0100644000175100001440000001336706364205342015154 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 08:45:24 1995 * Project: INN (innfeed) * File: connection.h * RCSId: $Id: connection.h,v 1.3 1997/07/19 18:43:46 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Public interface to the Connection class. * * The Connection class encapulates an NNTP protocol * endpoint (either regular or extended with the * streaming protocol). Each Connection is owned by a * single Host object. * * It manages the network connection (via an EndPoint) * the the pumping of articles to the remote host. It * gets these articles from its Host object. If the * remote doesn't handle the streaming extension, then * the Connection will only manage one article at a * time. If the remote handles the extension, then the * connection will queue up articles while sending the * CHECK and TAKETHIS commands. * * If the network connection drops while the Connection * object has articles queued up, then it will hand * them back to its Host object. * */ #if ! defined ( connection_h__ ) #define connection_h__ #include #include #include "misc.h" /* * Create a new Connection. * * HOST is the host object we're owned by. * IDENT is an identifier to be added to syslog entries so we can tell * what's happening on different connections to the same peer. * IPNAME is the name (or ip address) of the remote) * MAXTOUT is the maximum amount of time to wait for a response before * considering the remote host dead. * PORTNUM is the portnum to contact on the remote end. * RESPTIMEOUT is the amount of time to wait for a response from a remote * before considering the connection dead. * CLOSEPERIOD is the number of seconds after connecting that the * connections should be closed down and reinitialized (due to problems * with old NNTP servers that hold history files open. Value of 0 means * no close down. */ Connection newConnection (Host host, u_int ident, const char *ipname, u_int artTout, u_int portNum, u_int respTimeout, u_int closePeriod, double lowPassLow, double lowPassHigh, double lowPassFilter) ; /* Causes the Connection to build the network connection. */ bool cxnConnect (Connection cxn) ; /* puts the connection into the wait state (i.e. waits for an article before initiating a connect). Can only be called right after newConnection returns, or while the Connection is in the (internal) Sleeping state. */ void cxnWait (Connection cxn) ; /* The Connection will disconnect as if cxnDisconnect were called and then it automatically reconnects to the remote. */ void cxnFlush (Connection cxn) ; /* The Connection sends remaining articles, then issues a QUIT and then deletes itself */ void cxnClose (Connection cxn) ; /* The Connection drops all queueed articles, then issues a QUIT and then deletes itself */ void cxnTerminate (Connection cxn) ; /* Blow away the connection gracelessly and immedately clean up */ void cxnNuke (Connection cxn) ; /* Tells the Connection to take the article and handle its transmission. If it can't (due to queue size or whatever), then the function returns false. The connection assumes ownership of the article if it accepts it (returns true). */ bool cxnTakeArticle (Connection cxn, Article art) ; /* Tell the Connection to take the article (if it can) for later processing. Assumes ownership of it if it takes it. */ bool cxnQueueArticle (Connection cxn, Article art) ; /* generate a syslog message for the connections activity. Called by Host. */ void cxnLogStats (Connection cxn, bool final) ; /* return the number of articles the connection can be given. This lets the host shovel in as many as possible. May be zero. */ size_t cxnQueueSpace (Connection cxn) ; /* adjust the mode no-CHECK filter values */ void cxnSetCheckThresholds (Connection cxn, double lowFilter, double highFilter, double lowPassFilter) ; /* print some debugging info. */ void gPrintCxnInfo (FILE *fp, u_int indentAmt) ; void printCxnInfo (Connection cxn, FILE *fp, u_int indentAmt) ; /* config file load callback */ int cxnConfigLoadCbk (void *data) ; /* check connection state is in cxnWaitingS, cxnConnectingS or cxnIdleS */ bool cxnCheckstate (Connection cxn) ; #endif /* connection_h__ */ innfeed-0.10.1.7.orig/convertconfig.pl0100644000175100001440000001141006331417055016032 0ustar mdusers#!/usr/bin/perl # # Author: James Brister -- berkeley-unix -- # Start Date: Sun, 19 Jan 1997 21:19:24 +0100 # Project: INN (innfeed) # File: convertconfig.pl # RCSId: $Id: convertconfig.pl,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ # Description: Read in a old version of innfeed.conf on the command line # or on stdin, and write a new version on stdout. # require 'ctime.pl' ; @keyorder = ( 'news-spool', 'pid-file', 'debug-level', 'use-mmap', 'log-file', 'stdio-fdmax', 'backlog-directory', 'backlog-rotate-period', 'backlog-ckpt-period', 'backlog-newfile-period', 'dns-retry', 'dns-expire', 'close-period', 'gen-html', 'status-file', 'connection-stats', 'host-queue-highwater', 'stats-period', 'stats-reset', 'max-reconnect-time', 'initial-reconnect-time', 'article-timeout', 'response-timeout', 'initial-connections', 'max-connections', 'max-queue-size', 'streaming', 'no-check-high', 'no-check-low', 'port-number', 'backlog-limit', 'backlog-factor', 'backlog-limit-highwater' ); %procDefaults = ( 'news-spool', '/var/news/spool/articles', 'pid-file', 'innfeed.pid', 'debug-level', '0', 'use-mmap', 'false', 'log-file', 'innfeed.log', 'stdio-fdmax', '0', 'backlog-directory', '/var/news/spool/innfeed', 'backlog-rotate-period', '60', 'backlog-ckpt-period', '30', 'backlog-newfile-period', '600', 'dns-retry', '900', 'dns-expire', '86400', 'close-period', '3600', 'gen-html', 'false', 'status-file', 'innfeed.status', 'connection-stats', 'false', 'host-queue-highwater', '10', 'stats-period', '600', 'stats-reset', '43200', 'max-reconnect-time', '3600', 'initial-reconnect-time', '30', 'article-timeout', '600', 'response-timeout', '300', 'initial-connections', '1', 'max-connections', '5', 'max-queue-size', '25', 'streaming', 'true', 'no-check-high', '195.0', 'no-check-low', '90.0', 'port-number', '119', 'backlog-limit', '0', 'backlog-factor', '1.10', 'backlog-limit-highwater', '0', ); %defaultKeys = ('article-timeout', 1, 'response-timeout', 1, 'initial-connections', 1, 'max-connections', 1, 'max-queue-size', 1, 'streaming', 1, 'no-check-high', 1, 'no-check-low', 1, 'port-number', 1, 'backlog-limit', 1) ; @defaultOrder = ('article-timeout', 'response-timeout', 'initial-connections', 'max-connections', 'max-queue-size', 'streaming', 'no-check-high', 'no-check-low', 'port-number', 'backlog-limit') ; %formats = () ; foreach $key (keys %procDefaults) { $max = length ($key) if length ($key) > $max ; if ($procDefaults{$key} =~ /^true$/i || $procDefaults{$key} =~ /^false$/i){ $formats{$key} = "%s" ; } elsif ($procDefaults{$key} =~ /^\d+$/) { $formats{$key} = "%d" ; } elsif ($procDefaults{$key} =~ /^\d+\.\d*$/) { $formats{$key} = "%.4f" ; } else { $formats{$key} = "%s" ; } } while (<>) { next if /^\s*$/ ; next if /^#/ ; chop ; @F = split (':') ; if ($F[0] eq "default") { $procDefaults{'article-timeout'} = $F[2] ; $procDefaults{'response-timeout'} = $F[3] ; $procDefaults{'initial-connections'} = $F[4] ; $procDefaults{'max-connections'} = $F[5] ; $procDefaults{'max-queue-size'} = $F[6] ; $procDefaults{'streaming'} = $F[7] ; $procDefaults{'no-check-low'} = $F[8] * 10.0 ; $procDefaults{'no-check-high'} = $F[9] * 10.0 ; $procDefaults{'port-number'} = $F[10] ; printf "## This file was automatically generated created by $0\n" ; printf "## On %s##\n\n", &ctime(time) ; foreach $key (@keyorder) { next if $defaultKeys{$key} ; die "No format for $key\n" unless $formats{$key} ; $format = "%${max}s:\t" . $formats{$key} . "\n" ; printf $format, $key, $procDefaults{$key} ; } printf "\n\n## Defaults merged from:\n##\t$_\n\n" ; foreach $key (@defaultOrder) { die "No format for $key\n" unless $formats{$key} ; $format ="%${max}s:\t" . $formats{$key} . "\n" ; printf $format, $key, $procDefaults{$key} ; } print "\n\n\n" ; $gotDefault = 1 ; } elsif (@F == 0) { die "Badly formed line: $0\n" ; } else { if (!$gotDefault) { $gotDefault = 1 ; # warn only one time. warn "No default line was present.\n" ; } print "## Peer created from:\n" ; print "##\t$_\n\n" ; printf "peer %s {\n", $F[0] ; printf "\tip-name: $F[1]\n" if $F[1] && $F[0] ne $F[1] ; printf "\tarticle-timeout: %d\n", $F[2] if $F[2] ; printf "\tresponse-timeout: %d\n", $F[3] if $F[3] ; printf "\tinitial-connections: %d\n", $F[4] if ($F[4] ne "") ; printf "\tmax-connections: %d\n", $F[5] if ($F[5] ne "") ; printf "\tmax-queue-size: %d\n", $F[6] if ($F[6] ne "") ; printf "\tstreaming: %s\n", $F[7] if ($F[7] ne "") ; printf "\tno-check-high: %0.2f\n", $F[9] * 10.0 if ($F[9] ne "") ; printf "\tno-check-low: %0.2f\n", $F[8] * 10.0 if ($F[8] ne "") ; printf "\tport-number: %d\n", $F[10] if ($F[10] ne "") ; print "}\n\n\n" ; } } innfeed-0.10.1.7.orig/endpoint.c0100644000175100001440000013530106364205167014626 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Nov 29 23:08:24 1995 * Project: INN (innfeed) * File: endpoint.c * RCSId: $Id: endpoint.c,v 1.10 1997/07/19 18:41:59 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The implementation of the EndPoint object class. * * The EndPoint class is what gives the illusion 9sort * of, kind of) of threading. Basically it controls a * select loop and a set of EndPoint objects. Each * EndPoint has a file descriptor it is interested * in. The users of the EndPoint tell the EndPoints to * notify them when a read or write has been completed * (or simple if the file descriptor is read or write * ready). * */ #if ! defined (lint) static char rcsid [] = "$Id: endpoint.c,v 1.10 1997/07/19 18:41:59 scrappy Exp $" ; static void use_rcsid (const char *rId) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rId) ; } #endif #include "config.h" #if defined (DO_HAVE_UNISTD) #include #endif /* defined (DO_HAVE_UNISTD) */ #include #include #include #include #include #include #include #include #include #if defined (DO_NEED_SYS_SELECT) #include #endif #if defined (DO_NEED_TIME) #include #endif /* defined (DO_NEED_TIME) */ #include #include #include #include #if defined (DO_NEED_STREAM) #include #endif #include "configfile.h" #include "endpoint.h" #include "buffer.h" #include "msgs.h" #include "host.h" #if defined (__bsdi__) && (defined (_ANSI_SOURCE) || defined (_POSIX_SOURCE)) /* why do I have to do this???? */ struct timeval { long tv_sec; /* seconds */ long tv_usec; /* and microseconds */ }; #endif #if ! defined (UIO_MAXIOV) && defined (MAX_WRITEV_VEC) #define UIO_MAXIOV MAX_WRITEV_VEC #elif ! defined (UIO_MAXIOV) && defined (DEF_MAX_IOV) /* svr4 */ #define UIO_MAXIOV DEF_MAX_IOV #endif #if ! defined (UIO_MAXIOV) #error Neither UIO_MAXIOV nor MAX_WRITEV_VEC are defined -- see sysconfig.h #endif #if ! defined (NSIG) #define NSIG 32 #endif /* This is the structure that is the EndPoint */ struct endpoint_s { /* fields for managing multiple reads into the inBuffer. */ Buffer *inBuffer ; /* list of buffers to read into */ u_int inBufferIdx ; /* where is list we're at. */ size_t inIndex ; /* where in current read we're at */ size_t inMinLen ; /* minimum amount to read */ size_t inAmtRead ; /* amount read so far */ EndpRWCB inCbk ; /* callback for when read complete */ void *inClientData ; /* callback data */ /* fields for managing multiple writes from the outBuffer */ Buffer *outBuffer ; /* list of buffers to write */ u_int outBufferIdx ; /* index into buffer list to start write */ size_t outIndex ; /* where in current buffer we write from */ size_t outSize ; /* total of all the buffers */ size_t outAmtWritten ; /* amount written so far */ EndpRWCB outProgressCbk ; /* callback when done */ EndpRWCB outDoneCbk ; /* callback when done */ void *outClientData ; /* callback data */ EndpWorkCbk workCbk ; /* callback to run if no I/O to do */ void *workData ; /* data for callback */ int myFd ; /* the file descriptor we're handling */ int myErrno ; /* the errno when I/O fails */ double selectHits ; /* indicates how often it's ready */ }; /* A private structure. These hold the information on the timer callbacks. */ typedef struct timerqelem_s { TimeoutId id ; /* the id we gave out */ time_t when ; /* The time the timer should go off */ EndpTCB func ; /* the function to call */ void *data ; /* the client callback data */ struct timerqelem_s *next ; /* next in the queue */ } *TimerElem, TimerElemStruct ; /* set to 1 elsewhere if you want stderr to get what's also being written in doWrite. */ int debugWrites ; extern const char *InputFile ; static EndPoint mainEndPoint ; static bool mainEpIsReg = false ; static u_int stdioFdMax = MAX_STDIO_FD ; time_t PrivateTime; typedef void (*sigfn) (int) ; static sigfn *sigHandlers ; /* XXX - should be sig_atomic_t if we have it */ static VOLATILE int *sigFlags ; /* private functions */ static IoStatus doRead (EndPoint endp) ; static IoStatus doWrite (EndPoint endp) ; static IoStatus doExcept (EndPoint endp) ; static void pipeHandler (int s) ; static void signalHandler (int s) ; static int hitCompare (const void *v1, const void *v2) ; static void reorderPriorityList (void) ; static TimerElem newTimerElem (TimeoutId i, time_t w, EndpTCB f, void *d) ; static TimeoutId timerElemAdd (time_t when, EndpTCB func, void *data) ; static struct timeval *getTimeout (struct timeval *tout) ; static void doTimeout (void) ; static void handleSignals (void) ; #if 0 static int ff_set (fd_set *set, u_int start) ; static int ff_free (fd_set *set, u_int start) ; #endif static void endpointCleanup (void) ; /* Private data */ static size_t maxEndPoints ; static EndPoint *endPoints ; /* endpoints indexed on fd */ static EndPoint *priorityList ; /* endpoints indexed on priority */ static int absHighestFd = 0 ; /* never goes down */ static int highestFd = -1 ; static u_int endPointCount = 0 ; static u_int priorityCount = 0 ; static fd_set rdSet ; static fd_set wrSet ; static fd_set exSet ; static int keepSelecting ; static TimerElem timeoutQueue ; static TimerElem timeoutPool ; static TimeoutId nextId ; static int timeoutQueueLength ; /* Create a new EndPoint and hook it to the give file descriptor. All fields are initialized to appropriate values. On the first time this function is called the global data structs that manages lists of endpoints are intialized. */ static bool inited = false ; EndPoint newEndPoint (int fd) { EndPoint ep ; if (!inited) { inited = true ; atexit (endpointCleanup) ; } if (fd < 0) return NULL ; /* try to dup the fd to a larger number to leave lower values free for broken stdio implementations. */ if (stdioFdMax > 0 && ((u_int) fd) <= stdioFdMax) { int newfd = fcntl(fd, F_DUPFD, stdioFdMax + 1); if (newfd >= 0) { dprintf (1,"Dupped fd %d to %d\n",fd,newfd) ; if (close (fd) != 0) syslog (LOG_ERR,CLOSE_FAILED,fd) ; } else { dprintf (1,"Couldn't dup fd %d to above %d\n",fd,stdioFdMax) ; newfd = fd ; } fd = newfd ; } if ((u_int) fd >= maxEndPoints) { u_int i = maxEndPoints ; maxEndPoints = (((fd + 256) / 256) * 256); /* round up to nearest 256 */ if (endPoints == NULL) { endPoints = ALLOC (EndPoint,maxEndPoints) ; priorityList = ALLOC (EndPoint,maxEndPoints) ; } else { endPoints = REALLOC (endPoints,EndPoint,maxEndPoints) ; priorityList = REALLOC (priorityList,EndPoint,maxEndPoints) ; } for ( ; i < maxEndPoints ; i++) endPoints [i] = priorityList [i] = NULL ; } ASSERT (endPoints [fd] == NULL) ; if (fd > absHighestFd) { static bool sizelogged = false ; #if defined (FD_SETSIZE) if (fd >= FD_SETSIZE) { sizelogged = true ; syslog (LOG_ERR,FD_TOO_BIG,fd,FD_SETSIZE,"FD_SETSIZE") ; return NULL ; } #else if (fd > (sizeof (fd_set) * CHAR_BIT)) { sizelogged = true ; syslog (LOG_ERR,FD_TOO_BIG,fd,(sizeof (fd_set) * CHAR_BIT), "(sizeof (fd_set) * CHAR_BIT)"); return NULL ; } #endif absHighestFd = fd ; } ep = CALLOC (struct endpoint_s, 1) ; ASSERT (ep != NULL) ; ep->inBuffer = NULL ; ep->inBufferIdx = 0 ; ep->inIndex = 0 ; ep->inMinLen = 0 ; ep->inAmtRead = 0 ; ep->inCbk = NULL ; ep->inClientData = NULL ; ep->outBuffer = 0 ; ep->outBufferIdx = 0 ; ep->outIndex = 0 ; ep->outSize = 0 ; ep->outProgressCbk = NULL ; ep->outDoneCbk = NULL ; ep->outClientData = NULL ; ep->outAmtWritten = 0 ; ep->workCbk = NULL ; ep->workData = NULL ; ep->myFd = fd ; ep->myErrno = 0 ; ep->selectHits = 0.0 ; endPoints [fd] = ep ; priorityList [priorityCount++] = ep ; endPointCount++ ; highestFd = (fd > highestFd ? fd : highestFd) ; return ep ; } /* Delete the given endpoint. The files descriptor is closed and the two Buffer arrays are released. */ void delEndPoint (EndPoint ep) { u_int idx ; if (ep == NULL) return ; ASSERT (endPoints [ep->myFd] == ep) ; if (mainEndPoint == ep) mainEndPoint = NULL ; if (ep->inBuffer != NULL) freeBufferArray (ep->inBuffer) ; if (ep->outBuffer != NULL) freeBufferArray (ep->outBuffer) ; close (ep->myFd) ; /* remove from selectable bits */ FD_CLR (ep->myFd,&rdSet) ; FD_CLR (ep->myFd,&wrSet) ; FD_CLR (ep->myFd,&exSet) ; /* Adjust the global arrays to account for deleted endpoint. */ endPoints [ep->myFd] = NULL ; if (ep->myFd == highestFd) while (endPoints [highestFd] == NULL && highestFd >= 0) highestFd-- ; for (idx = 0 ; idx < priorityCount ; idx++) if (priorityList [idx] == ep) break ; ASSERT (idx < priorityCount) ; /* i.e. was found */ ASSERT (priorityList [idx] == ep) ; /* redundant */ /* this hole will removed in the reorder routine */ priorityList [idx] = NULL ; endPointCount-- ; FREE (ep) ; } int endPointFd (EndPoint endp) { ASSERT (endp != NULL) ; return endp->myFd ; } /* Request a read to be done next time there's data. The endpoint * ENDP is what will do the read. BUFF is the Buffer the data should * go into. FUNC is the callback function to call when the read is * complete. CLIENTDATA is the client data to pass back into the * callback function. MINLEN is the minimum amount of data to * read. If MINLEN is 0 then then BUFF must be filled, otherwise at * least MINLEN bytes must be read. * * BUFF can be null, in which case no read is actually done, but the * callback function will be called still. This is useful for * listening sockets. * * Returns 0 if the read couln't be prepared (for example if a read * is already outstanding). */ int prepareRead (EndPoint endp, Buffer *buffers, EndpRWCB func, void *clientData, int minlen) { int bufferSizeTotal = 0 ; int idx ; ASSERT (endp != NULL) ; if (endp->inBuffer != NULL || FD_ISSET (endp->myFd,&rdSet)) return 0 ; /* something already there */ for (idx = 0 ; buffers != NULL && buffers [idx] != NULL ; idx++) { size_t bs = bufferSize (buffers [idx]) ; size_t bds = bufferDataSize (buffers [idx]) ; bufferSizeTotal += (bs - bds) ; } endp->inBuffer = buffers ; endp->inBufferIdx = 0 ; endp->inIndex = 0 ; endp->inMinLen = (minlen > 0 ? minlen : bufferSizeTotal) ; endp->inCbk = func ; endp->inAmtRead = 0 ; endp->inClientData = clientData ; FD_SET (endp->myFd, &rdSet) ; if ( InputFile == NULL ) FD_SET (endp->myFd, &exSet) ; return 1 ; } /* Request a write to be done at a later point. ENDP is the EndPoint * to do the write. BUFF is the Buffer to write from. FUNC is the * function to call when the write is complete. CLIENTDATA is some * data to hand back to the callback function. * * If BUFF is null, then no write will actually by done, but the * callback function will still be called. This is useful for * connecting sockets. * * Returns 0 if the write couldn't be prepared (like if a write is * still in process. */ int prepareWrite (EndPoint endp, Buffer *buffers, EndpRWCB progress, EndpRWCB done, void *clientData) { int bufferSizeTotal = 0 ; int idx ; ASSERT (endp != NULL) ; if (endp->outBuffer != NULL || FD_ISSET (endp->myFd,&wrSet)) return 0 ; /* something already there */ for (idx = 0 ; buffers != NULL && buffers [idx] != NULL ; idx++) bufferSizeTotal += bufferDataSize (buffers [idx]) ; endp->outBuffer = buffers ; endp->outBufferIdx = 0 ; endp->outIndex = 0 ; endp->outProgressCbk = progress ; endp->outDoneCbk = done ; endp->outClientData = clientData ; endp->outSize = bufferSizeTotal ; endp->outAmtWritten = 0 ; FD_SET (endp->myFd, &wrSet) ; FD_SET (endp->myFd, &exSet) ; return 1 ; } /* Cancel the pending read. */ void cancelRead (EndPoint endp) { FD_CLR (endp->myFd,&rdSet) ; if (!FD_ISSET (endp->myFd, &wrSet)) FD_CLR (endp->myFd,&exSet) ; freeBufferArray (endp->inBuffer) ; endp->inBuffer = NULL ; endp->inBufferIdx = 0 ; endp->inIndex = 0 ; endp->inMinLen = 0 ; endp->inAmtRead = 0 ; endp->inCbk = NULL ; endp->inClientData = NULL ; } /* cancel all pending writes. The first len bytes of the queued write are copied to buffer. The number of bytes copied (if it is less than *len) is copied to len. If no write was outstanding then len will have 0 stored in it. */ void cancelWrite (EndPoint endp, char *buffer, size_t *len) { (void) buffer ; (void) len ; FD_CLR (endp->myFd, &wrSet) ; if (!FD_ISSET (endp->myFd, &rdSet)) FD_CLR (endp->myFd, &exSet) ; #if 0 #error XXX need to copy data to buffer and *len #endif freeBufferArray (endp->outBuffer) ; endp->outBuffer = NULL ; endp->outBufferIdx = 0 ; endp->outIndex = 0 ; endp->outProgressCbk = NULL ; endp->outDoneCbk = NULL ; endp->outClientData = NULL ; endp->outSize = 0 ; endp->outAmtWritten = 0 ; } /* queue up a new timeout request. to go off at a specific time. */ TimeoutId prepareWake (EndpTCB func, time_t timeToWake, void *clientData) { TimeoutId id ; time_t now = theTime() ; if (now > timeToWake) return 0 ; id = timerElemAdd (timeToWake,func,clientData) ; #if 0 dprintf (1,"Preparing wake %d at date %ld for %d seconds\n", (int) id, (long) now, timeToWake - now) ; #endif return id ; } /* queue up a new timeout request to off TIMETOSLEEP seconds from now */ TimeoutId prepareSleep (EndpTCB func, int timeToSleep, void *clientData) { time_t now = theTime() ; TimeoutId id ; id = timerElemAdd (now + timeToSleep,func,clientData) ; #if 0 dprintf (1,"Preparing sleep %d at date %ld for %d seconds\n", (int) id, (long) now, timeToSleep) ; #endif return id ; } /* Updates a an existing timeout request to go off TIMETOSLEEP seconds from now, or queues a new request. Always returns a new ID. */ TimeoutId updateSleep (TimeoutId tid, EndpTCB func, int timeToSleep, void *clientData) { if (tid == 0) return prepareSleep (func, timeToSleep, clientData) ; else { /* XXX - quick and dirty but CPU wasteful implementation */ removeTimeout (tid) ; return prepareSleep (func, timeToSleep, clientData) ; } } /* Remove a timeout that was previously prepared. 0 is a legal value that is just ignored. */ bool removeTimeout (TimeoutId tid) { TimerElem n = timeoutQueue ; TimerElem p = NULL ; if (tid == 0) return true ; while (n != NULL && n->id != tid) { p = n ; n = n->next ; } if (n == NULL) return false ; if (p == NULL) /* at the head. */ timeoutQueue = n->next ; else p->next = n->next ; n->next = timeoutPool ; timeoutPool = n ; timeoutQueueLength-- ; return true ; } /* The main routine. This is a near-infinite loop that drives the whole program. */ void Run (void) { fd_set rSet ; fd_set wSet ; fd_set eSet ; keepSelecting = 1 ; signal (SIGPIPE, pipeHandler) ; while (keepSelecting) { struct timeval timeout ; struct timeval *twait ; int sval ; u_int idx ; bool modifiedTime = false ; twait = getTimeout (&timeout) ; memcpy (&rSet,&rdSet,sizeof (rdSet)) ; memcpy (&wSet,&wrSet,sizeof (wrSet)) ; memcpy (&eSet,&exSet,sizeof (exSet)) ; if (highestFd < 0 && twait == NULL) /* no fds and no timeout */ break ; else if (twait != NULL && (twait->tv_sec != 0 || twait->tv_usec != 0)) { /* if we have any workprocs registered we poll rather than block on the fds */ for (idx = 0 ; idx < priorityCount ; idx++) if (priorityList [idx] != NULL && priorityList [idx]->workCbk != NULL) { modifiedTime = true ; twait->tv_sec = 0 ; twait->tv_usec = 0 ; break ; } } /* calculate host backlog statistics */ gCalcHostBlStat (); sval = select (highestFd + 1, &rSet, &wSet, &eSet, twait) ; timePasses () ; if (sval == 0 && twait == NULL) die ("No fd's ready and no timeouts") ; else if (sval < 0 && errno == EINTR) { handleSignals () ; } else if (sval < 0) { syslog (LOG_ERR,BAD_SELECT,sval) ; stopRun () ; } else if (sval > 0) { IoStatus rval ; int readyCount = sval ; int endpointsServiced = 0 ; handleSignals() ; for (idx = 0 ; idx < priorityCount ; idx++) { EndPoint ep = priorityList [idx] ; bool specialCheck = false ; if (readyCount > 0 && ep != NULL) { int fd = ep->myFd ; int selectHit = 0, readMiss = 0, writeMiss = 0 ; /* Every SELECT_RATIO times we service an endpoint in this loop we check to see if the mainEndPoint fd is ready to read or write. If so we process it and do the current endpoint next time around. */ if (((endpointsServiced % SELECT_RATIO) == 0) && ep != mainEndPoint && mainEndPoint != NULL && !mainEpIsReg) { fd_set trSet, twSet ; struct timeval tw ; int checkRead = FD_ISSET (mainEndPoint->myFd,&rdSet) ; int checkWrite = FD_ISSET (mainEndPoint->myFd,&wrSet) ; if (checkRead || checkWrite) { fd = mainEndPoint->myFd ; tw.tv_sec = tw.tv_usec = 0 ; memset (&trSet,0,sizeof (trSet)) ; memset (&twSet,0,sizeof (twSet)) ; if (checkRead) FD_SET (fd,&trSet) ; if (checkWrite) FD_SET (fd,&twSet) ; sval = select (fd + 1,&trSet,&twSet,0,&tw) ; if (sval > 0) { idx-- ; ep = mainEndPoint ; specialCheck = true ; if (checkRead && FD_ISSET (fd,&trSet)) { FD_SET (fd,&rSet) ; readyCount++ ; } if (checkWrite && FD_ISSET (fd,&twSet)) { FD_SET (fd,&wSet) ; readyCount++ ; } } else if (sval < 0) { syslog (LOG_ERR,BAD_SELECT,sval) ; stopRun () ; return ; } else fd = ep->myFd ; /* back to original fd. */ } } FD_CLR (fd, &exSet) ; if (FD_ISSET (fd,&rSet)) { readyCount-- ; endpointsServiced++ ; selectHit = 1 ; if ((rval = doRead (ep)) != IoIncomplete) { Buffer *buff = ep->inBuffer ; FD_CLR (fd, &rdSet) ; /* incase callback wants to issue read */ ep->inBuffer = NULL ; if (ep->inCbk != NULL) (*ep->inCbk) (ep,rval,buff,ep->inClientData) ; else freeBufferArray (buff) ; } else { if ( InputFile == NULL ) FD_SET (ep->myFd, &exSet) ; } } else if (FD_ISSET(fd,&rdSet)) readMiss = 1; /* get it again as the read callback may have deleted the */ /* endpoint */ if (specialCheck) ep = mainEndPoint ; else ep = priorityList [idx] ; if (readyCount > 0 && ep != NULL && FD_ISSET (fd,&wSet)) { readyCount-- ; endpointsServiced++ ; selectHit = 1 ; if ((rval = doWrite (ep)) != IoIncomplete && rval != IoProgress) { Buffer *buff = ep->outBuffer ; FD_CLR (fd, &wrSet) ; /* incase callback wants to issue a write */ ep->outBuffer = NULL ; if (ep->outDoneCbk != NULL) (*ep->outDoneCbk) (ep,rval,buff,ep->outClientData) ; else freeBufferArray (buff) ; } else if (rval == IoProgress) { Buffer *buff = ep->outBuffer ; if (ep->outProgressCbk != NULL) (*ep->outProgressCbk) (ep,rval,buff,ep->outClientData) ; } else { FD_SET (ep->myFd, &exSet) ; } } else if (FD_ISSET(fd,&wrSet)) writeMiss = 1; /* get it again as the write callback may have deleted the */ /* endpoint */ if (specialCheck) ep = mainEndPoint ; else ep = priorityList [idx] ; if (ep != NULL) { ep->selectHits *= 0.9 ; if (selectHit) ep->selectHits += 1.0 ; else if (readMiss && writeMiss) ep->selectHits -= 1.0 ; } if (readyCount > 0 && ep != NULL && FD_ISSET (fd,&eSet)) doExcept (ep) ; } } reorderPriorityList () ; } else if (sval == 0 && !modifiedTime) doTimeout () ; /* now we're done processing all read fds and/or the timeout(s). Next we do the work callbacks for all the endpoints whose fds weren't ready. */ for (idx = 0 ; idx < priorityCount ; idx++) { EndPoint ep = priorityList [idx] ; if (ep != NULL) { int fd = ep->myFd ; if ( !FD_ISSET (fd,&rSet) && !FD_ISSET (fd,&wSet) ) if (ep->workCbk != NULL) { EndpWorkCbk func = ep->workCbk ; void *data = ep->workData ; ep->workCbk = NULL ; ep->workData = NULL ; func (ep,data) ; } } } } } void *addWorkCallback (EndPoint endp, EndpWorkCbk cbk, void *data) { void *oldBk = endp->workData ; endp->workCbk = cbk ; endp->workData = data ; return oldBk ; } /* Tell the Run routine to stop next time around. */ void stopRun (void) { keepSelecting = 0 ; } int endPointErrno (EndPoint endp) { return endp->myErrno ; } bool readIsPending (EndPoint endp) { return (endp->inBuffer != NULL ? true : false) ; } bool writeIsPending (EndPoint endp) { return (endp->outBuffer != NULL ? true : false) ; } void setMainEndPoint (EndPoint endp) { struct stat buf ; mainEndPoint = endp ; if (endp->myFd >= 0 && fstat (endp->myFd,&buf) < 0) syslog (LOG_ERR,"Can't fstat mainEndPoint fd (%d): %m", endp->myFd) ; else if (endp->myFd < 0) syslog (LOG_ERR,"Negative fd for mainEndPoint???") ; else mainEpIsReg = (S_ISREG(buf.st_mode) ? true : false) ; } int getMainEndPointFd (void) { return(mainEndPoint->myFd) ; } void freeTimeoutQueue (void) { TimerElem p, n ; p = timeoutQueue ; while (p) { n = p->next ; p->next = timeoutPool ; timeoutPool = p; p = n ; timeoutQueueLength-- ; } } /***********************************************************************/ /* STATIC FUNCTIONS BELOW HERE */ /***********************************************************************/ /* * called when the file descriptor on this endpoint is read ready. */ static IoStatus doRead (EndPoint endp) { int i = 0 ; u_int idx ; u_int bCount = 0 ; struct iovec *vp = NULL ; Buffer *buffers = endp->inBuffer ; u_int currIdx = endp->inBufferIdx ; size_t amt = 0 ; IoStatus rval = IoIncomplete ; for (i = currIdx ; buffers && buffers [i] != NULL ; i++) bCount++ ; /* if UIO_MAXIOV is undefined then set MAX_WRITEV_VEC in config.h */ bCount = (bCount > UIO_MAXIOV ? UIO_MAXIOV : bCount) ; i = 0 ; /* now set up the iovecs for the readv */ if (bCount > 0) { char *bbase ; size_t bds, bs ; vp = CALLOC (struct iovec, bCount) ; ASSERT (vp != NULL) ; bbase = bufferBase (buffers[currIdx]) ; bds = bufferDataSize (buffers[currIdx]) ; bs = bufferSize (buffers [currIdx]) ; /* inIndex is an index in the virtual array of the read, not directly into the buffer. */ vp[0].iov_base = bbase + bds + endp->inIndex ; vp[0].iov_len = bs - bds - endp->inIndex ; amt = vp[0].iov_len ; for (idx = currIdx + 1 ; idx < bCount ; idx++) { bbase = bufferBase (buffers[idx]) ; bds = bufferDataSize (buffers[idx]) ; bs = bufferSize (buffers [idx]) ; vp [idx].iov_base = bbase ; vp [idx].iov_len = bs - bds ; amt += (bs - bds) ; } i = readv (endp->myFd,vp,(int) bCount) ; if (i > 0) { size_t readAmt = (size_t) i ; endp->inAmtRead += readAmt ; /* check if we filled the first buffer */ if (readAmt >= vp[0].iov_len) { /* we did */ bufferIncrDataSize (buffers[currIdx], vp[0].iov_len) ; readAmt -= vp [0].iov_len ; endp->inBufferIdx++ ; } else { endp->inIndex += readAmt ; bufferIncrDataSize (buffers[currIdx], readAmt) ; readAmt = 0 ; } /* now check the rest of the buffers */ for (idx = 1 ; readAmt > 0 ; idx++) { ASSERT (idx < bCount) ; bs = bufferSize (buffers [currIdx + idx]) ; bbase = bufferBase (buffers [currIdx + idx]) ; bds = bufferDataSize (buffers [currIdx + idx]) ; if (readAmt >= (bs - bds)) { bufferSetDataSize (buffers [currIdx + idx],bs) ; readAmt -= bs ; endp->inBufferIdx++ ; } else { endp->inIndex = readAmt ; bufferIncrDataSize (buffers [currIdx + idx], readAmt) ; memset (bbase + bds + readAmt, 0, bs - bds - readAmt) ; readAmt = 0 ; } } if (endp->inAmtRead >= endp->inMinLen) { endp->inIndex = 0 ; rval = IoDone ; } } else if (i < 0 && errno != EINTR && errno != EAGAIN) { endp->myErrno = errno ; rval = IoFailed ; } else if (i < 0 && errno == EINTR) { handleSignals () ; } else if (i == 0) rval = IoEOF ; else /* i < 0 && errno == EAGAIN */ rval = IoIncomplete ; FREE (vp) ; } else rval = IoDone ; return rval ; } /* called when the file descriptor on the endpoint is write ready. */ static IoStatus doWrite (EndPoint endp) { u_int idx ; int i = 0 ; size_t amt = 0 ; u_int bCount = 0 ; struct iovec *vp = NULL ; Buffer *buffers = endp->outBuffer ; u_int currIdx = endp->outBufferIdx ; IoStatus rval = IoIncomplete ; for (i = currIdx ; buffers && buffers [i] != NULL ; i++) bCount++ ; /* if UIO_MAXIOV is undefined then set MAX_WRITEV_VEC in config.h */ bCount = (bCount > UIO_MAXIOV ? UIO_MAXIOV : bCount) ; i = 0 ; if (bCount > 0) { vp = CALLOC (struct iovec, bCount) ; ASSERT (vp != NULL) ; vp[0].iov_base = bufferBase (buffers [currIdx]) ; vp[0].iov_base = (char *) vp[0].iov_base + endp->outIndex ; vp[0].iov_len = bufferDataSize (buffers [currIdx]) - endp->outIndex ; amt = vp[0].iov_len ; for (idx = 1 ; idx < bCount ; idx++) { vp [idx].iov_base = bufferBase (buffers [idx + currIdx]) ; vp [idx].iov_len = bufferDataSize (buffers [idx + currIdx]) ; amt += vp[idx].iov_len ; } #if 1 if (debugWrites) { /* nasty mixing, but stderr is unbuffered usually. It's debugging only */ dprintf (5,"About to write this:================================\n") ; writev (2,vp,bCount) ; dprintf (5,"end=================================================\n") ; } #endif ASSERT (endp->myFd >= 0) ; ASSERT (vp != 0) ; ASSERT (bCount > 0) ; i = writev (endp->myFd,vp,(int) bCount) ; if (i > 0) { size_t writeAmt = (size_t) i ; endp->outAmtWritten += writeAmt ; /* now figure out which buffers got completely written */ for (idx = 0 ; writeAmt > 0 ; idx++) { if (writeAmt >= vp [idx].iov_len) { endp->outBufferIdx++ ; endp->outIndex = 0 ; writeAmt -= vp [idx].iov_len ; } else { /* this buffer was not completly written */ endp->outIndex += writeAmt ; writeAmt = 0 ; } } if (endp->outAmtWritten == endp->outSize) rval = IoDone ; else rval = IoProgress ; } else if (i < 0 && errno == EINTR) { handleSignals () ; } else if (i < 0 && errno == EAGAIN) { rval = IoIncomplete ; } else if (i < 0) { endp->myErrno = errno ; rval = IoFailed ; } else dprintf (1,"Wrote 0 bytes in doWrite()?\n") ; FREE (vp) ; } else rval = IoDone ; return rval ; } static IoStatus doExcept (EndPoint endp) { int optval, size ; int fd = endPointFd (endp) ; if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (GETSOCKOPT_ARG) &optval, &size) != 0) syslog (LOG_ERR,GETSOCKOPT_FAILURE,fd) ; else if (optval != 0) { errno = optval ; syslog (LOG_ERR,EXCEPTION_NOTICE,fd) ; } else syslog (LOG_ERR,UNKNOWN_EXCEPTION,fd) ; #if 0 sleep (5) ; abort () ; #endif /* Not reached */ return IoFailed ; } #if 0 static void endPointPrint (EndPoint ep, FILE *fp) { fprintf (fp,"EndPoint [%p]: fd [%d]\n",(void *) ep, ep->myFd) ; } #endif static void signalHandler (int s) { sigFlags[s] = 1 ; #if !defined(USE_SIGACTION) && !defined(USE_SIGVEC) && !defined(USE_SIGSET) signal (s, signalHandler) ; #endif } static void pipeHandler (int s) { signal (s, pipeHandler) ; } /* compare the hit ratio of two endpoint for qsort. We're sorting the endpoints on their relative activity */ static int hitCompare (const void *v1, const void *v2) { const EndPoint e1 = *((const EndPoint *) v1) ; const EndPoint e2 = *((const EndPoint *) v2) ; double e1Hit = e1->selectHits ; double e2Hit = e2->selectHits ; if (e1 == mainEndPoint) return -1 ; else if (e2 == mainEndPoint) return 1 ; else if (e1Hit < e2Hit) return 1 ; else if (e1Hit > e2Hit) return -1 ; return 0 ; } /* We maintain the endpoints in order of the percent times they're ready for read/write when they've been selected. This helps us favour the more active endpoints. */ static void reorderPriorityList (void) { int i, j ; static int thisTime = 4; /* only sort every 4th time since it's so expensive */ if (--thisTime > 0) return ; thisTime = 4; for (i = j = 0; i < priorityCount; i++) if (priorityList [i] != NULL) { if (i != j) priorityList [j] = priorityList [i] ; j++ ; } for (i = j; i < priorityCount; i++) priorityList [ i ] = NULL; priorityCount = j; qsort (priorityList, (size_t)priorityCount, sizeof (EndPoint), &hitCompare); } #define TIMEOUT_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (TimerElemStruct))) /* create a new timeout data structure properly initialized. */ static TimerElem newTimerElem (TimeoutId i, time_t w, EndpTCB f, void *d) { TimerElem p ; if (timeoutPool == NULL) { int i ; timeoutPool = ALLOC (TimerElemStruct, TIMEOUT_POOL_SIZE) ; ASSERT (timeoutPool != NULL) ; for (i = 0; i < TIMEOUT_POOL_SIZE - 1; i++) timeoutPool[i] . next = &(timeoutPool [i + 1]) ; timeoutPool [TIMEOUT_POOL_SIZE-1] . next = NULL ; } p = timeoutPool ; timeoutPool = timeoutPool->next ; ASSERT (p != NULL) ; p->id = i ; p->when = w ; p->func = f ; p->data = d ; p->next = NULL ; return p ; } /* add a new timeout structure to the global list. */ static TimeoutId timerElemAdd (time_t when, EndpTCB func, void *data) { TimerElem p = newTimerElem (++nextId ? nextId : ++nextId,when,func,data) ; TimerElem n = timeoutQueue ; TimerElem q = NULL ; while (n != NULL && n->when <= when) { q = n ; n = n->next ; } if (n == NULL && q == NULL) /* empty list so put at head */ timeoutQueue = p ; else if (q == NULL) /* put at head of list */ { p->next = timeoutQueue ; timeoutQueue = p ; } else if (n == NULL) /* put at end of list */ q->next = p ; else /* in middle of list */ { p->next = q->next ; q->next = p ; } timeoutQueueLength++ ; return p->id ; } /* Fills in TOUT with the timeout to use on the next call to * select. Returns TOUT. If there is no timeout, then returns NULL. If the * timeout has already passed, then it calls the timeout handling routine * first. */ static struct timeval *getTimeout (struct timeval *tout) { struct timeval *rval = NULL ; if (timeoutQueue != NULL) { time_t now = theTime() ; while (timeoutQueue && now > timeoutQueue->when) doTimeout () ; if (timeoutQueue != NULL && now == timeoutQueue->when) { tout->tv_sec = 0 ; tout->tv_usec = 0 ; rval = tout ; } else if (timeoutQueue != NULL) { tout->tv_sec = timeoutQueue->when - now ; tout->tv_usec = 0 ; rval = tout ; } } return rval ; } static void doTimeout (void) { EndpTCB cbk = timeoutQueue->func ; void *data = timeoutQueue->data ; TimerElem p = timeoutQueue ; TimeoutId tid = timeoutQueue->id ; timeoutQueue = timeoutQueue->next ; p->next = timeoutPool ; timeoutPool = p ; timeoutQueueLength-- ; if (cbk) (*cbk) (tid, data) ; /* call the callback function */ } #if defined (WANT_MAIN) #include #include #include #include #define BUFF_SIZE 100 void timerCallback (void *cd) ; void timerCallback (void *cd) { dprintf (1,"Callback \n") ; } void lineIsWritten (EndPoint ep, IoStatus status, Buffer *buffer, void *data); void lineIsWritten (EndPoint ep, IoStatus status, Buffer *buffer, void *data) { int i ; if (status == IoDone) dprintf (1,"LINE was written\n") ; else { int oldErrno = errno ; errno = endPointErrno (ep) ; perror ("write failed") ; errno = oldErrno ; } for (i = 0 ; buffer && buffer [i] ; i++) delBuffer (buffer [i]) ; } void lineIsRead (EndPoint myEp, IoStatus status, Buffer *buffer, void *data); void lineIsRead (EndPoint myEp, IoStatus status, Buffer *buffer, void *d) { Buffer *writeBuffers, *readBuffers ; Buffer newBuff1, newBuff2 ; Buffer newInputBuffer ; char *data, *p ; size_t len ; if (status == IoFailed) { int oldErrno = errno ; errno = endPointErrno (myEp) ; perror ("read failed") ; errno = oldErrno ; return ; } else if (status == IoEOF) { dprintf (1,"EOF on endpoint.\n") ; delEndPoint (myEp) ; return ; } data = bufferBase (buffer[0]) ; len = bufferDataSize (buffer[0]) ; if (data [len - 1] == '\r' || data [len - 1] == '\n') bufferDecrDataSize (buffer [0],1) ; if (data [len - 1] == '\r' || data [len - 1] == '\n') bufferDecrDataSize (buffer [0],1) ; data [len] = '\0' ; dprintf (1,"Got a line: %s\n", data) ; newBuff1 = newBuffer (len + 50) ; newBuff2 = newBuffer (len + 50) ; newInputBuffer = newBuffer (BUFF_SIZE) ; p = bufferBase (newBuff1) ; strcpy (p, "Thanks for that \"") ; bufferSetDataSize (newBuff1,strlen (p)) ; p = bufferBase (newBuff2) ; strcpy (p,"\" very tasty\n") ; bufferSetDataSize (newBuff2,strlen (p)) ; writeBuffers = makeBufferArray (newBuff1,buffer[0],newBuff2,NULL) ; readBuffers = makeBufferArray (newInputBuffer,NULL) ; prepareWrite (myEp,writeBuffers,lineIsWritten,NULL) ; prepareRead (myEp,readBuffers,lineIsRead,NULL,1) ; #if 0 myEp->registerWake (&timerCallback,theTime() + 7,0) ; #endif } static void printDate (TimeoutId tid, void *data) ; static void printDate (TimeoutId tid, void *data) { time_t t ; t = theTime() ; dprintf (1,"Timeout (%d) time now is %ld %s", (int) tid,(long) t,ctime(&t)) ; if (timeoutQueue == NULL) { int ti = (rand () % 10) + 1 ; prepareSleep (printDate,ti,data) ; } } TimeoutId rm ; static void Timeout (TimeoutId tid, void *data) ; static void Timeout (TimeoutId tid, void *data) { static int seeded ; static int howMany ; static int i ; time_t t = theTime() ; if ( !seeded ) { srand (t) ; seeded = 1 ; } dprintf (1,"Timeout (%d) time now is %ld %s", (int) tid, (long) t,ctime(&t)) ; if (timeoutQueue != NULL && timeoutQueue->next != NULL) dprintf (1,"%s timeout id %d\n", (removeTimeout (rm) ? "REMOVED" : "FAILED TO REMOVE"), rm) ; rm = 0 ; howMany = (rand() % 10) + (timeoutQueue == NULL ? 1 : 0) ; for (i = 0 ; i < howMany ; i++ ) { TimeoutId id ; int count = (rand () % 30) + 1 ; id = (i % 2 == 0 ? prepareSleep (Timeout,count,data) : prepareWake (Timeout,t + count,data)) ; if (rm == 0) rm = id ; } } void newConn (EndPoint ep, IoStatus status, Buffer *buffer, void *d) ; void newConn (EndPoint ep, IoStatus status, Buffer *buffer, void *d) { EndPoint newEp ; struct sockaddr_in in ; Buffer *readBuffers ; Buffer newBuff = newBuffer (BUFF_SIZE) ; int len = sizeof (in) ; int fd ; memset (&in, 0, sizeof (in)) ; fd = accept (ep->myFd, (struct sockaddr *) &in, &len) ; if (fd < 0) { perror ("::accept") ; return ; } newEp = newEndPoint (fd) ; prepareRead (ep, NULL, newConn,NULL,0) ; readBuffers = makeBufferArray (newBuff,NULL) ; prepareRead (newEp, readBuffers, lineIsRead, NULL, 1) ; dprintf (1,"Set up a new connection\n"); } int main (int argc, char **argv) { EndPoint accConn ; struct sockaddr_in accNet ; int accFd = socket (AF_INET,SOCK_STREAM,0) ; u_short port = atoi (argc > 1 ? argv[1] : "10000") ; time_t t = theTime() ; program = strrchr (argv[0],'/') ; if (!program) program = argv [0] ; else program++ ; ASSERT (accFd >= 0) ; memset (&accNet,0,sizeof (accNet)) ; accNet.sin_family = AF_INET ; accNet.sin_addr.s_addr = htonl (INADDR_ANY) ; accNet.sin_port = htons (port) ; openlog (program, LOG_PERROR | LOG_PID, LOG_NEWS) ; if (bind (accFd, (struct sockaddr *) &accNet, sizeof (accNet)) < 0) { perror ("bind: %m") ; exit (1) ; } listen (accFd,5) ; accConn = newEndPoint (accFd) ; prepareRead (accConn,NULL,newConn,NULL,0) ; prepareSleep (Timeout,5,(void *) 0x10) ; t = theTime() ; dprintf (1,"Time now is %s",ctime(&t)) ; prepareWake (printDate,t + 16,NULL) ; Run () ; return 0; } #endif /* WANT_MAIN */ /* Probably doesn't do the right thing for SIGCHLD */ void setSigHandler (int signum, void (*ptr)(int)) { u_int i ; if (sigHandlers == NULL) { sigHandlers = ALLOC (sigfn,NSIG) ; sigFlags = ALLOC (int,NSIG) ; for (i = 0 ; i < NSIG ; i++) { sigHandlers [i] = NULL ; sigFlags [i] = 0 ; } } if (signum >= NSIG) { syslog (LOG_ERR,"ME signal number bigger than NSIG: %d vs %d", signum,NSIG) ; return ; } #if defined(USE_SIGACTION) { struct sigaction sa ; sa.sa_handler = signalHandler ; sa.sa_flags = 0 ; sigemptyset (&sa.sa_mask) ; if (sigaction ( signum, &sa, (struct sigaction *)NULL) != 0) die ("sigaction failed: %s", strerror(errno)) ; } #elif defined(USE_SIGVEC) { struct sigvec vec; vec.sv_handler = signalHandler ; vec.sv_mask = 0 ; vec.sv_flags = 0 ; if (sigvec (signum, &vec, (struct sigvec *)NULL) != 0) die ("sigvec failed: %s", strerror(errno)) ; } #elif defined(USE_SIGSET) if (sigset (signum, signalHandler) == SIG_ERR) die ("sigset failed: %s", strerror(errno)) ; #else if (signal (signum, signalHandler) == -1) die ("signal failed: %s", strerror(errno)) ; #endif sigHandlers[signum] = ptr ; } static void handleSignals (void) { int i ; #if defined(USE_SIGVEC) int mask ; #endif for (i = 1; i < NSIG; i++) { if (sigFlags[i]) { #if defined(USE_SIGACTION) sigset_t set, oset ; if (sigemptyset (&set) != 0 || sigaddset (&set, i) != 0) die ("sigemptyset or sigaddset failed") ; if (sigprocmask (SIG_BLOCK, &set, &oset) != 0) die ("sigprocmask failed: %s", strerror(errno)) ; #elif defined(USE_SIGVEC) # ifndef sigmask # define sigmask(s) (1 << ((s) - 1)) # endif int mask ; mask = sigblock (sigmask(i)) ; #elif defined(USE_SIGSET) if (sighold (i) != 0) die ("sighold failed: %s", strerror(errno)) ; #else /* hope for the best */ #endif sigFlags[i] = 0; if (sigHandlers[i] != NULL && sigHandlers[i] != SIG_IGN && sigHandlers[i] != SIG_DFL) (sigHandlers[i])(i) ; #if defined(USE_SIGACTION) if (sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL) != 0) die ("sigprocmask failed: %s", strerror(errno)) ; #elif defined(USE_SIGVEC) (void) sigsetmask (mask) ; #elif defined(USE_SIGSET) if (sigrelse (i) != 0) die ("sigrelse failed: %s", strerror(errno)) ; #else /* hope for the best */ #endif } } } int endpointConfigLoadCbk (void *data) { FILE *fp = (FILE *) data ; long ival ; int rval = 1 ; if (getInteger (topScope,"stdio-fdmax",&ival,NO_INHERIT)) { stdioFdMax = ival ; #if ! defined (FD_SETSIZE) if (stdioFdMax > 0) { logOrPrint (LOG_ERR,fp,NO_STDIO_FDMAX) ; stdioFdMax = 0 ; rval = 0 ; } #else if (stdioFdMax > FD_SETSIZE) { logOrPrint (LOG_ERR,fp,INT_TO_HIGH,"stdio-fdmax",ival,"global scope", (long) FD_SETSIZE, (long) FD_SETSIZE) ; stdioFdMax = FD_SETSIZE ; rval = 0 ; } #endif } else stdioFdMax = 0 ; return rval ; } #if 0 /* definitely not the fastest, but the most portable way to find the first set bit in a mask */ static int ff_set (fd_set *set,u_int start) { u_int i ; for (i = start ; i < FD_SETSIZE ; i++) if (FD_ISSET (i,set)) return (int) i ; return -1 ; } static int ff_free (fd_set *set, u_int start) { u_int i ; for (i = start ; i < FD_SETSIZE ; i++) if (!FD_ISSET (i,set)) return i ; return -1 ; } #endif static void endpointCleanup (void) { FREE (endPoints) ; FREE (priorityList) ; FREE (sigHandlers) ; } innfeed-0.10.1.7.orig/endpoint.h0100644000175100001440000001656106331756143014640 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Nov 29 22:04:49 1995 * Project: INN (innfeed) * File: endpoint.h * RCSId: $Id: endpoint.h,v 1.3 1997/04/30 23:57:23 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Public interface to the EndPoint class. * * The EndPoint objects are encapsulations of file descriptors * that normally do blocking i/o (i.e. NOT fd's hooked to a disk * file). The EndPoint class provides methods for reqesting * read/writes to happen when next possible and for the requestor * to be notified when the i/o is complete (or failed for some * reason). Facilities for timeout notifications are provided too. * */ #if ! defined ( endpoint_h__ ) #define endpoint_h__ #if 0 /* * We should add a way to cancel prepared read/write. */ #endif #include "misc.h" #define clearTimer(timerId) \ do {\ if((timerId)!=0) { \ removeTimeout(timerId); \ timerId=0; \ } \ }while(0) /* These typedefs really lives in misc.h * ***************************************** * * The basic (opqaue to the outside world) type. * * typedef struct endpoint_s *EndPoint ; * ***************************************** * * The returns status of an IO request * * typedef enum { * IoDone, i/o completed successfully * IoIncomplete, i/o still got more to read/write * IoProgress, i/o still got more to read/write * IoFailed i/o failed * } IoStatus ; * * The completion callbacks are never called with the status IoIncomplete or * IoProgress. * ***************************************** * * typedef for function callback when IO is complete (or failed). * E is the EndPoint * I is the status of the IO * B is the buffer the IO was to read to or write from. * D is the client data originally given to prepare{Write,Read} * * typedef void (*EndpRWCB) (EndPoint e, IoStatus i, Buffer b, void *d) ; * ***************************************** * * typedef for function callback when a timer has gone off. D is the client * data given to prepare{Sleep,Wake} * * typedef void (*EndpTCB) (void *d) ; * */ /* create a new EndPoint hooked up to the given file descriptor */ EndPoint newEndPoint (int fd) ; /* shutdown the file descriptor and delete the endpoint. */ void delEndPoint (EndPoint endp) ; /* return the file descriptor the endpoint is managing */ int endPointFd (EndPoint endp) ; /* Request a read when available. Reads MINLEN bytes into the * buffers in BUFFS. BUFFS is an array of Buffers, the last of which * must be NULL. Note that ownership of BUFFS is never asserted, but * the ownership of the Buffers in it is. So if an EndPoint is * deleted while a read is pending the Buffers will be released, but * the array won't be. If MINLEN is 0 then the buffers must be * filled. The FUNC function gets called when the read is * complete. CLIENTDATA is simply passed back to the * callback. Returns non-zero if can be scheduled for processing. */ int prepareRead (EndPoint endp, Buffer *buffs, EndpRWCB func, void *clientData, int minlen) ; /* Request a write when possible. All the data in the buffers in * BUFFS will be written out the endpoint. BUFFS is a NULL * terminated array of Buffers. See prepareWrite for a discussion on * the ownership of BUFFS and the Buffers inside BUFFS. The PROGRESS * callback function will be called and the CLIENTDATA value will be * passed through to it whenever any data is written except for the * final write. The DONE callback function will be called and the * CLIENTDATA value will be passed through to it after the final write. * Returns non-zero if scheduled succesfully. */ int prepareWrite (EndPoint endp, Buffer *buffs, EndpRWCB progress, EndpRWCB done, void *clientData) ; /* cancel any outstanding reads. */ void cancelRead (EndPoint endp) ; /* cancel any outstanding writes. */ void cancelWrite (EndPoint endp, char *buffer, size_t *len) ; /* return true if prepareRead has been called, but not serviced yet */ bool readIsPending (EndPoint endp) ; /* Request a wakeup at a given time. */ TimeoutId prepareWake (EndpTCB func, time_t timeToWake, void *clientData) ; /* return true if prepareWrite has been called, but not serviced yet */ bool writeIsPending (EndPoint endp) ; /* Requests a wakeup TIMETOSLEEP seconds from now. */ TimeoutId prepareSleep (EndpTCB func, int timeToSleep, void *clientData) ; /* Updates tid to wakeup TIMETOSLEEP seconds from now. */ TimeoutId updateSleep (TimeoutId tid, EndpTCB func, int timeToSleep, void *clientData) ; /* Set up a function to be called whenever the endpoint's file descriptor is NOT ready. This is called after all other fd-ready endpoints have been serviced. */ void *addWorkCallback (EndPoint endp, EndpWorkCbk cbk, void *data) ; void setSigHandler (int sig, void (*)(int)) ; /* remove the timeout that was previously requested. Retuesn true if succesfully removed, false otherwise. 0 is a legal parameter value, in which case the function simply returns. */ bool removeTimeout (TimeoutId tid) ; /* start the select loop. An initial prepare(Read|Write) or a timeout better have been setup. Doesn't return unless stopRun called */ void Run (void) ; /* stops the Run loop and makes Run() return */ void stopRun (void) ; /* returns the errno the endpoint got. */ int endPointErrno (EndPoint endp) ; /* Tell the EndPoint class that the given EndPoint should always be considered first for servicing (i.e. the EndPoint connectied to innd) */ void setMainEndPoint (EndPoint endp) ; /* returns the fd of the main endpoint */ int getMainEndPointFd (void) ; void freeTimeoutQueue (void) ; int endpointConfigLoadCbk (void *data) ; /* * kre's cool idea for reducing the number of times time() is called. */ extern time_t PrivateTime; #define theTime() (PrivateTime ? PrivateTime : time(&PrivateTime)) #define timePasses() (PrivateTime = 0) #endif /* endpoint_h__ */ innfeed-0.10.1.7.orig/host.c0100644000175100001440000026725606400214361013765 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Thu Dec 28 17:29:05 1995 * Project: INN (innfeed) * File: host.c * RCSId: $Id: host.c,v 1.21 1997/08/25 05:32:33 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Implementation of the Host class. * */ #if ! defined (lint) static const char *rcsid = "$Id: host.c,v 1.21 1997/08/25 05:32:33 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* LONG_MAX */ #include #include "host.h" #include "tape.h" #include "connection.h" #include "article.h" #include "buffer.h" #include "endpoint.h" #include "innlistener.h" #include "msgs.h" #include "configfile.h" #define REQ 1 #define NOTREQ 0 #define NOTREQNOADD 2 #define VALUE_OK 0 #define VALUE_TOO_HIGH 1 #define VALUE_TOO_LOW 2 #define VALUE_MISSING 3 #define VALUE_WRONG_TYPE 4 #define METHOD_STATIC 0 #define METHOD_APS 1 #define METHOD_QUEUE 2 #define METHOD_COMBINED 3 /* the limit of number of connections open when a host is set to 0 to mean "infinite" */ #define MAXCON 500 #define MAXCONLIMIT(xx) ((xx==0)?MAXCON:xx) #define BACKLOGFILTER 0.7 #define BACKLOGLWM 20.0 #define BACKLOGHWM 50.0 /* time between retrying blocked hosts in seconds */ #define TRYBLOCKEDHOSTPERIOD 120 extern char *configFile ; extern mainLogStatus (FILE *fp) ; /* the host keeps a couple lists of these */ typedef struct proc_q_elem { Article article ; struct proc_q_elem *next ; struct proc_q_elem *prev ; } *ProcQElem ; typedef struct host_param_s { char *peerName; char *ipName; u_int articleTimeout; u_int responseTimeout; u_int initialConnections; u_int absMaxConnections; u_int maxChecks; u_short portNum; u_int closePeriod; u_int dynamicMethod; bool wantStreaming; double lowPassLow; /* as percentages */ double lowPassHigh; double lowPassFilter; u_int backlogLimit ; u_int backlogLimitHigh ; double backlogFactor ; double dynBacklogFilter ; double dynBacklogLowWaterMark ; double dynBacklogHighWaterMark ; } *HostParams ; struct host_s { InnListener listener ; /* who created me. */ char **ipAddrs ; /* the ip addresses of the remote */ char **nextIpAddr ; /* the next ip address to hand out */ Connection *connections ; /* NULL-terminated list of all connections */ bool *cxnActive ; /* true if the corresponding cxn is active */ bool *cxnSleeping ; /* true if the connection is sleeping */ u_int maxConnections; /* maximum no of cxns controlled by method */ u_int activeCxns ; /* number of connections currently active */ u_int sleepingCxns ; /* number of connections currently sleeping */ Connection blockedCxn ; /* the first connection to get the 400 banner*/ Connection notThisCxn ; /* don't offer articles to this connection */ HostParams params; /* Parameters from config file */ bool remoteStreams ; /* true if remote supports streaming */ ProcQElem queued ; /* articles done nothing with yet. */ ProcQElem queuedTail ; ProcQElem processed ; /* articles given to a Connection */ ProcQElem processedTail ; TimeoutId statsId ; /* timeout id for stats logging. */ TimeoutId ChkCxnsId ; /* timeout id for dynamic connections */ Tape myTape ; bool backedUp ; /* set to true when all cxns are full */ u_int backlog ; /* number of arts in `queued' queue */ bool loggedModeOn ; /* true if we logged going into no-CHECK mode */ bool loggedModeOff ; /* true if we logged going out of no-CHECK mode */ bool loggedBacklog ; /* true if we already logged the fact */ bool notifiedChangedRemBlckd ; /* true if we logged a new response 400 */ bool removeOnReload ; /* true if host should be removed at end of * config reload */ bool isDynamic; /* true if host created dynamically */ /* these numbers get reset periodically (after a 'final' logging). */ u_int artsOffered ; /* # of articles we offered to remote. */ u_int artsAccepted ; /* # of articles succesfully transferred */ u_int artsNotWanted ; /* # of articles remote already had */ u_int artsRejected ; /* # of articles remote rejected */ u_int artsDeferred ; /* # of articles remote asked us to retry */ u_int artsMissing ; /* # of articles whose file was missing. */ u_int artsToTape ; /* # of articles given to tape */ u_int artsQueueOverflow ; /* # of articles that overflowed `queued' */ u_int artsCxnDrop ; /* # of articles caught in dead cxn */ u_int artsHostSleep ; /* # of articles spooled by sleeping host */ u_int artsHostClose ; /* # of articles caught by closing host */ u_int artsFromTape ; /* # of articles we pulled off tape */ /* Dynamic Peerage - MGF */ u_int artsProcLastPeriod ; /* # of articles processed in last period */ u_int secsInLastPeriod ; /* Number of seconds in last period */ u_int lastCheckPoint ; /* total articles at end of last period */ u_int lastSentCheckPoint ; /* total articles sent end of last period */ u_int lastTotalCheckPoint ; /* total articles total end of last period */ bool maxCxnChk ; /* check for maxConnections */ time_t lastMaxCxnTime ; /* last time a maxConnections increased */ time_t lastChkTime; /* last time a check was made for maxConnect */ u_int nextCxnTimeChk ; /* next check for maxConnect */ double backlogFilter; /* IIR filter for size of backlog */ /* These numbers are as above, but for the life of the process. */ u_int gArtsOffered ; u_int gArtsAccepted ; u_int gArtsNotWanted ; u_int gArtsRejected ; u_int gArtsDeferred ; u_int gArtsMissing ; u_int gArtsToTape ; u_int gArtsQueueOverflow ; u_int gArtsCxnDrop ; u_int gArtsHostSleep ; u_int gArtsHostClose ; u_int gArtsFromTape ; time_t firstConnectTime ; /* time of first connect. */ time_t connectTime ; /* the time the first connection was fully set up (MODE STREAM and everything else). */ time_t spoolTime ; /* the time the Host had to revert to spooling articles to tape. */ time_t lastSpoolTime ; /* the time the last time the Host had to revert to spooling articles to tape. */ time_t nextIpLookup ; /* time of last IP name resolution */ char *blockedReason ; /* what the 400 from the remote says. */ Host next ; /* for global list of hosts. */ u_int blNone ; /* number of times the backlog was 0 */ u_int blFull ; /* number of times the backlog was full */ u_int blQuartile[4] ; /* number of times in each quartile */ u_long blAccum ; /* cumulative backlog for computing mean */ u_int blCount ; /* the sample count */ }; /* A holder for the info we got out of the config file, but couldn't create the Host object for (normally due to lock-file problems).*/ typedef struct host_holder_s { HostParams params; struct host_holder_s *next ; } *HostHolder ; /* These numbers are as above, but for all hosts over the life of the process. */ long procArtsOffered ; long procArtsAccepted ; long procArtsNotWanted ; long procArtsRejected ; long procArtsDeferred ; long procArtsMissing ; static HostParams defaultParams=NULL; static HostHolder blockedHosts ; /* lists of hosts we can't lock */ static TimeoutId tryBlockedHostsId = 0 ; static time_t lastStatusLog ; /* * Host object private methods. */ static void articleGone (Host host, Connection cxn, Article article) ; static void hostStopSpooling (Host host) ; static void hostStartSpooling (Host host) ; static void hostLogStats (Host host, bool final) ; static void hostStatsTimeoutCbk (TimeoutId tid, void *data) ; static void backlogToTape (Host host) ; static void queuesToTape (Host host) ; static bool amClosing (Host host) ; static void hostLogStatus (void) ; static void hostPrintStatus (Host host, FILE *fp) ; static int validateBool (FILE *fp, const char *name, int required, bool setval, scope * sc, u_int inh); static int validateReal (FILE *fp, const char *name, double low, double high, int required, double setval, scope * sc, u_int inh); static int validateInteger (FILE *fp, const char *name, long low, long high, int required, long setval, scope * sc, u_int inh); static HostParams newHostParams(HostParams p); static void freeHostParams(HostParams params); static HostHolder FindBlockedHost(const char *name); static void addBlockedHost(HostParams params); static void tryBlockedHosts(TimeoutId tid, void *data); static Host newHost (InnListener listener, HostParams p); static HostParams getHostInfo (void); static HostParams hostDetails (scope *s, char *name, bool isDefault, FILE *fp); static Host findHostByName (char *name) ; static void hostCleanup (void) ; static void hostAlterMaxConnections(Host host, u_int absMaxCxns, u_int maxCxns, bool makeConnect); /* article queue management functions */ static Article remHead (ProcQElem *head, ProcQElem *tail) ; static void queueArticle (Article article, ProcQElem *head, ProcQElem *tail) ; static bool remArticle (Article article, ProcQElem *head, ProcQElem *tail) ; /* * Host class data */ /* if true then when a Host logs its stats, it has all its connections log theirs too. */ static bool logConnectionStats = (bool) LOG_CONNECTION_STATS ; /* The frequency in seconds with which a Host will log its stats. */ static time_t statsPeriod = STATS_PERIOD ; static time_t statsResetPeriod = STATS_RESET_PERIOD ; static Host gHostList = NULL ; static u_int gHostCount = 0 ; static u_int maxIpNameLen = 0 ; static u_int maxPeerNameLen = 0 ; static u_int hostHighwater = HOST_HIGHWATER ; static time_t start ; static char startTime [30] ; /* for ctime(3) */ static pid_t myPid ; static char *statusFile = NULL ; static u_int dnsRetPeriod ; static u_int dnsExpPeriod ; static bool genHtml = false ; /*******************************************************************/ /* PUBLIC FUNCTIONS */ /*******************************************************************/ /* function called when the config file is loaded */ int hostConfigLoadCbk (void *data) { int rval = 1, bval ; long iv ; FILE *fp = (FILE *) data ; char *p ; dprintf(1,"hostConfigLoadCbk\n"); if (defaultParams) { freeHostParams(defaultParams); defaultParams=NULL; } /* get optional global defaults */ if (getInteger (topScope,"dns-retry",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ONE,"dns-retry", iv,"global scope",(long)DNS_RETRY_PERIOD) ; iv = DNS_RETRY_PERIOD ; } } else iv = DNS_RETRY_PERIOD ; dnsRetPeriod = (u_int) iv ; if (getInteger (topScope,"dns-expire",&iv,NO_INHERIT)) { if (iv < 1) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ONE,"dns-expire",iv, "global scope",(long)DNS_EXPIRE_PERIOD) ; iv = DNS_EXPIRE_PERIOD ; } } else iv = DNS_EXPIRE_PERIOD ; dnsExpPeriod = (u_int) iv ; if (getBool (topScope,"gen-html",&bval,NO_INHERIT)) genHtml = (bval ? true : false) ; else genHtml = GEN_HTML ; if (getString (topScope,"status-file",&p,NO_INHERIT)) { hostSetStatusFile (p) ; FREE (p) ; } else hostSetStatusFile (INNFEED_STATUS) ; if (getBool (topScope,"connection-stats",&bval,NO_INHERIT)) logConnectionStats = (bval ? true : false) ; else logConnectionStats = (LOG_CONNECTION_STATS ? true : false) ; if (getInteger (topScope,"host-queue-highwater", &iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"host-queue-highwater", iv,"global scope",(long) HOST_HIGHWATER) ; iv = HOST_HIGHWATER ; } } else iv = HOST_HIGHWATER ; hostHighwater = (u_int) iv ; if (getInteger (topScope,"stats-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"stats-period", iv,"global scope",(long)STATS_PERIOD) ; iv = STATS_PERIOD ; } } else iv = STATS_PERIOD ; statsPeriod = (u_int) iv ; if (getInteger (topScope,"stats-reset",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"stats-reset",iv, "global scope",(long)STATS_RESET_PERIOD) ; iv = STATS_RESET_PERIOD ; } } else iv = STATS_RESET_PERIOD ; statsResetPeriod = (u_int) iv ; defaultParams=hostDetails(topScope, NULL, true, fp); ASSERT(defaultParams!=NULL); return rval ; } /* * make a new HostParams structure copying an existing one * or from compiled defaults */ HostParams newHostParams(HostParams p) { HostParams params; params = ALLOC (struct host_param_s,1) ; ASSERT (params != NULL); if (p != NULL) { /* Copy old stuff in */ memcpy ((char *) params, (char *) p, sizeof(struct host_param_s)); if (params->peerName) params->peerName = strdup(params->peerName); if (params->ipName) params->ipName = strdup(params->ipName); } else { /* Fill in defaults */ params->peerName=NULL; params->ipName=NULL; params->articleTimeout=ARTTOUT; params->responseTimeout=RESPTOUT; params->initialConnections=INIT_CXNS; params->absMaxConnections=MAX_CXNS; params->maxChecks=MAX_Q_SIZE; params->portNum=PORTNUM; params->closePeriod=CLOSE_PERIOD; params->dynamicMethod=METHOD_STATIC; params->wantStreaming=STREAM; params->lowPassLow=NOCHECKLOW; params->lowPassHigh=NOCHECKHIGH; params->lowPassFilter=FILTERVALUE; params->backlogLimit=BLOGLIMIT; params->backlogLimitHigh=BLOGLIMIT_HIGH ; params->backlogFactor=LIMIT_FUDGE ; params->dynBacklogFilter = BACKLOGFILTER ; params->dynBacklogLowWaterMark = BACKLOGLWM; params->dynBacklogHighWaterMark = BACKLOGHWM; } return (params); } /* * Free up a param structure */ void freeHostParams(HostParams params) { ASSERT(params != NULL); if (params->peerName) FREE (params->peerName) ; if (params->ipName) FREE (params->ipName) ; FREE (params) ; } void hostReconfigure(Host h, HostParams params) { u_int i, absMaxCxns ; double oldBacklogFilter ; if (strcmp(h->params->ipName, params->ipName) != 0) { FREE (h->params->ipName) ; h->params->ipName = strdup (params->ipName) ; h->nextIpLookup = theTime () ; } /* Put in new parameters Unfortunately we can't blat on top of absMaxConnections as we need to do some resizing here */ ASSERT (h->params != NULL); oldBacklogFilter = h->params->dynBacklogFilter; i = h->params->absMaxConnections; /* keep old value */ absMaxCxns = params->absMaxConnections; /* Use this set of params and allocate, and free * up the old */ freeHostParams(h->params); h->params = params; h->params->absMaxConnections = i; /* restore old value */ /* If the backlog filter value has changed, reset the * filter as the value therein will be screwy */ if (h->params->dynBacklogFilter != oldBacklogFilter) h->backlogFilter = ((h->params->dynBacklogLowWaterMark + h->params->dynBacklogHighWaterMark) /200.0 /(1.0-h->params->dynBacklogFilter)); /* We call this anyway - it does nothing if the values * haven't changed. This is because doing things like * just changing "dynamic-method" requires this call * to be made */ hostAlterMaxConnections(h, absMaxCxns, h->maxConnections, false); for ( i = 0 ; i < MAXCONLIMIT(h->params->absMaxConnections) ; i++ ) if (h->connections[i] != NULL) cxnSetCheckThresholds (h->connections[i], h->params->lowPassLow, h->params->lowPassHigh, h->params->lowPassFilter) ; /* XXX how to handle initCxns change? */ } void configHosts (bool talkSelf) { Host nHost, h, q ; HostHolder hh, hi ; HostParams params; /* Remove the current blocked host list */ for (hh = blockedHosts, hi = NULL ; hh != NULL ; hh = hi) { freeHostParams(hh->params); hi = hh->next ; FREE (hh) ; } blockedHosts = NULL ; closeDroppedArticleFile () ; openDroppedArticleFile () ; while ((params = getHostInfo ()) !=NULL ) { h = findHostByName (params->peerName) ; /* We know the host isn't blocked as we cleared the blocked list */ /* Have we already got this host up and running ?*/ if ( h != NULL ) { hostReconfigure(h, params); h->removeOnReload = false ; /* Don't remove at the end */ } else { /* It's a host we haven't seen from the config file before */ nHost = newHost (mainListener, params); if (nHost == NULL) { addBlockedHost(params); syslog (LOG_ERR,NO_HOST,params->peerName) ; } else { if (params->initialConnections == 0 && talkSelf) syslog (LOG_NOTICE,BATCH_AND_NO_CXNS,params->peerName) ; if ( !listenerAddPeer (mainListener,nHost) ) die ("failed to add a new peer\n") ; } } } for (h = gHostList; h != NULL; h = q) { q = h->next ; if (h->removeOnReload) { if (h->isDynamic) { /* change to the new default parameters */ params = newHostParams(defaultParams); ASSERT(params->peerName == NULL); ASSERT(params->ipName == NULL); ASSERT(h->params->peerName != NULL); ASSERT(h->params->ipName != NULL); params->peerName = strdup(h->params->peerName); params->ipName = strdup(h->params->ipName); hostReconfigure(h, params); h->removeOnReload = true; } else hostClose (h) ; /* h may be deleted in here. */ } else /* prime it for the next config file read */ h->removeOnReload = true ; } hostLogStatus () ; } void hostAlterMaxConnections(Host host, u_int absMaxCxns, u_int maxCxns, bool makeConnect) { u_int lAbsMaxCxns; u_int i; /* Fix 0 unlimited case */ lAbsMaxCxns = MAXCONLIMIT(absMaxCxns); /* Don't accept 0 for maxCxns */ maxCxns=MAXCONLIMIT(maxCxns); if ( host->params->dynamicMethod == METHOD_STATIC) { /* If running static, ignore the maxCxns passed in, we'll just use absMaxCxns */ maxCxns = lAbsMaxCxns; } if ( maxCxns > lAbsMaxCxns) { /* ensure maxCxns is of the correct form */ maxCxns = lAbsMaxCxns; } if ((maxCxns < host->maxConnections) && (host->connections != NULL)) { /* We are going to have to nuke some connections, as the current max is now greater than the new max */ for ( i = host->maxConnections ; i > maxCxns ; i-- ) { /* XXX this is harsh, and arguably there could be a cleaner way of doing it. the problem being addressed by doing it this way is that eventually a connection closed cleanly via cxnClose can end up ultimately calling hostCxnDead after h->maxConnections has been lowered and the relevant arrays downsized. If trashing the old, unallocated space in hostCxnDead doesn't kill the process, the ASSERT against h->maxConnections surely will. */ if (host->connections[i - 1] != NULL) { cxnNuke (host->connections[i-1]) ; host->connections[i-1] = NULL; } } host->maxConnections = maxCxns ; } if (host->connections) for (i = host->maxConnections ; i <= MAXCONLIMIT(host->params->absMaxConnections) ; i++) { /* Ensure we've got an empty values only beyond the maxConnection water mark. */ ASSERT (host->connections[i] == NULL); } if ((lAbsMaxCxns != MAXCONLIMIT(host->params->absMaxConnections)) || (host->connections == NULL)) { /* we need to change the size of the connection array */ if (host->connections == NULL) { /* not yet allocated */ host->connections = CALLOC (Connection, lAbsMaxCxns + 1) ; ASSERT (host->connections != NULL) ; ASSERT (host->cxnActive == NULL); host->cxnActive = CALLOC (bool, lAbsMaxCxns) ; ASSERT (host->cxnActive != NULL) ; ASSERT (host->cxnSleeping == NULL) ; host->cxnSleeping = CALLOC (bool, lAbsMaxCxns) ; ASSERT (host->cxnSleeping != NULL) ; for (i = 0 ; i < lAbsMaxCxns ; i++) { host->connections [i] = NULL ; host->cxnActive[i] = false ; host->cxnSleeping[i] = false ; } host->connections[lAbsMaxCxns] = NULL; } else { host->connections = REALLOC (host->connections, Connection, lAbsMaxCxns + 1); ASSERT (host->connections != NULL) ; host->cxnActive = REALLOC (host->cxnActive, bool, lAbsMaxCxns) ; ASSERT (host->cxnActive != NULL) ; host->cxnSleeping = REALLOC (host->cxnSleeping, bool, lAbsMaxCxns) ; ASSERT (host->cxnSleeping != NULL) ; if (lAbsMaxCxns > MAXCONLIMIT(host->params->absMaxConnections)) { for (i = MAXCONLIMIT(host->params->absMaxConnections) ; i < lAbsMaxCxns ; i++) { host->connections[i+1] = NULL; /* array always 1 larger */ host->cxnActive[i] = false ; host->cxnSleeping[i] = false ; } } } host->params->absMaxConnections = absMaxCxns; } /* if maximum was raised, establish the new connexions (but don't start using them). */ if ( maxCxns > host->maxConnections) { i = host->maxConnections ; /* need to set host->maxConnections before cxnWait() */ host->maxConnections = maxCxns; while ( i < maxCxns ) { host->cxnActive [i] = false ; host->cxnSleeping [i] = false ; /* create a new connection */ host->connections [i] = newConnection (host, i, host->params->ipName, host->params->articleTimeout, host->params->portNum, host->params->responseTimeout, host->params->closePeriod, host->params->lowPassLow, host->params->lowPassHigh, host->params->lowPassFilter) ; /* connect if low enough numbered, or we were forced to */ if ((i < host->params->initialConnections) || makeConnect) cxnConnect (host->connections [i]) ; else cxnWait (host->connections [i]) ; i++ ; } } } /* * Find a host on the blocked host list */ static HostHolder FindBlockedHost(const char *name) { HostHolder hh = blockedHosts; while (hh != NULL) if ((hh->params) && (hh->params->peerName) && (strcmp(name,hh->params->peerName) == 0)) return hh; else hh=hh->next; return NULL; } static void addBlockedHost(HostParams params) { HostHolder hh; hh = ALLOC (struct host_holder_s,1) ; /* Use this set of params */ hh->params = params; hh->next = blockedHosts ; blockedHosts = hh ; } /* * We iterate through the blocked host list and try and reconnect ones * where we couldn't get a lock */ static void tryBlockedHosts(TimeoutId tid /*unused*/ , void *data /*unused*/ ) { HostHolder hh,hi; HostParams params; hh = blockedHosts; /* Get start of our queue */ blockedHosts = NULL ; /* remove them all from the queue of hosts */ while (hh != NULL) { params = hh->params; hi= hh->next; FREE(hh); hh = hi; if (params && params->peerName) { if (findHostByName(params->peerName)!=NULL) { /* Wierd, someone's managed to start it when it's on * the blocked list. Just silently discard. */ freeHostParams(params); } else { Host nHost; nHost = newHost (mainListener, params); if (nHost == NULL) { addBlockedHost(params); syslog (LOG_ERR,NO_HOST,params->peerName) ; } else { dprintf(1,"Unblocked host %s\n",params->peerName); if (params->initialConnections == 0 && listenerIsDummy(mainListener) /*talk to self*/) syslog (LOG_NOTICE,BATCH_AND_NO_CXNS,params->peerName) ; if ( !listenerAddPeer (mainListener,nHost) ) die ("failed to add a new peer\n") ; } } } } tryBlockedHostsId = prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL); } /* * Create a new Host object with default parameters. Called by the * InnListener. */ Host newDefaultHost (InnListener listener, const char *name) { HostParams p; Host h = NULL; if (FindBlockedHost(name)==NULL) { p=newHostParams(defaultParams); ASSERT(p!=NULL); /* relies on fact listener and names are null in default*/ p->peerName=strdup(name); p->ipName=strdup(name); h=newHost (listener,p); h->isDynamic = true; h->removeOnReload = true; if (h==NULL) { /* Couldn't get a lock - add to list of blocked peers */ addBlockedHost(p); syslog (LOG_ERR,NO_HOST,p->peerName) ; return NULL; } syslog (LOG_NOTICE,DYNAMIC_PEER,p->peerName) ; } return h; } /* * Create a new host and attach the supplied param structure */ static bool inited = false ; Host newHost (InnListener listener, HostParams p) { Host nh ; ASSERT (p->maxChecks > 0) ; if (!inited) { inited = true ; atexit (hostCleanup) ; } /* * Once only, init the first blocked host check */ if (tryBlockedHostsId==0) tryBlockedHostsId = prepareSleep(tryBlockedHosts, TRYBLOCKEDHOSTPERIOD, NULL); nh = CALLOC (struct host_s, 1) ; ASSERT (nh != NULL) ; nh->params = p; nh->listener = listener; nh->connections = NULL; /* We'll get these allocated later */ nh->cxnActive = NULL; nh->cxnSleeping = NULL; nh->activeCxns = 0 ; nh->sleepingCxns = 0 ; nh->blockedCxn = NULL ; nh->notThisCxn = NULL ; nh->queued = NULL ; nh->queuedTail = NULL ; nh->processed = NULL ; nh->processedTail = NULL ; nh->statsId = 0 ; nh->ChkCxnsId = 0 ; nh->myTape = newTape (nh->params->peerName, listenerIsDummy (nh->listener)) ; if (nh->myTape == NULL) { /* tape couldn't be locked, probably */ FREE (nh->connections) ; FREE (nh->cxnActive) ; FREE (nh->cxnSleeping) ; FREE (nh) ; return NULL ; /* note we don't free up p */ } nh->backedUp = false ; nh->backlog = 0 ; nh->loggedBacklog = false ; nh->loggedModeOn = false ; nh->loggedModeOff = false ; nh->notifiedChangedRemBlckd = false ; nh->removeOnReload = false ; /* ready for config file reload */ nh->isDynamic = false ; nh->artsOffered = 0 ; nh->artsAccepted = 0 ; nh->artsNotWanted = 0 ; nh->artsRejected = 0 ; nh->artsDeferred = 0 ; nh->artsMissing = 0 ; nh->artsToTape = 0 ; nh->artsQueueOverflow = 0 ; nh->artsCxnDrop = 0 ; nh->artsHostSleep = 0 ; nh->artsHostClose = 0 ; nh->artsFromTape = 0 ; nh->artsProcLastPeriod = 0; nh->secsInLastPeriod = 0; nh->lastCheckPoint = 0; nh->lastSentCheckPoint = 0; nh->lastTotalCheckPoint = 0; nh->maxCxnChk = true; nh->lastMaxCxnTime = time(0); nh->lastChkTime = time(0); nh->nextCxnTimeChk = 30; nh->backlogFilter = ((nh->params->dynBacklogLowWaterMark + nh->params->dynBacklogHighWaterMark) /200.0 /(1.0-nh->params->dynBacklogFilter)); nh->gArtsOffered = 0 ; nh->gArtsAccepted = 0 ; nh->gArtsNotWanted = 0 ; nh->gArtsRejected = 0 ; nh->gArtsDeferred = 0 ; nh->gArtsMissing = 0 ; nh->gArtsToTape = 0 ; nh->gArtsQueueOverflow = 0 ; nh->gArtsCxnDrop = 0 ; nh->gArtsHostSleep = 0 ; nh->gArtsHostClose = 0 ; nh->gArtsFromTape = 0 ; nh->firstConnectTime = 0 ; nh->connectTime = 0 ; nh->spoolTime = 0 ; nh->blNone = 0 ; nh->blFull = 0 ; nh->blQuartile[0] = nh->blQuartile[1] = nh->blQuartile[2] = nh->blQuartile[3] = 0 ; nh->blAccum = 0; nh->blCount = 0; nh->maxConnections = 0; /* we currently have no connections allocated */ /* Note that the following will override the initialCxns specified as maxCxns if we are on non-dyamic feed */ hostAlterMaxConnections(nh, nh->params->absMaxConnections, nh->params->initialConnections, false); nh->next = gHostList ; gHostList = nh ; gHostCount++ ; if (maxIpNameLen == 0) { start = theTime() ; strcpy (startTime,ctime (&start)) ; myPid = getpid() ; } if (strlen (nh->params->ipName) > maxIpNameLen) maxIpNameLen = strlen (nh->params->ipName) ; if (strlen (nh->params->peerName) > maxPeerNameLen) maxPeerNameLen = strlen (nh->params->peerName) ; return nh ; } struct in_addr *hostIpAddr (Host host) { int i ; char *p ; char **newIpAddrs = NULL; struct in_addr ipAddr, *returnAddr ; struct hostent *hostEnt ; ASSERT(host->params != NULL); /* check to see if need to look up the host name */ if (host->nextIpLookup <= theTime()) { /* see if the ipName we're given is a dotted quad */ if ( !inet_aton (host->params->ipName,&ipAddr) ) { if ((hostEnt = gethostbyname (host->params->ipName)) == NULL) syslog (LOG_ERR, HOST_RESOLV_ERROR, host->params->peerName, host->params->ipName, host_err_str ()) ; else { /* figure number of pointers that need space */ for (i = 0 ; hostEnt->h_addr_list[i] ; i++) ; i++; newIpAddrs = (char **) MALLOC ( (i * sizeof(char *)) + ( (i - 1) * hostEnt->h_length) ) ; ASSERT (newIpAddrs != NULL) ; /* copy the addresses from gethostbyname() static space */ p = (char *)&newIpAddrs [ i ] ; i = 0; for (i = 0 ; hostEnt->h_addr_list[i] ; i++) { newIpAddrs[i] = p; memcpy (p, hostEnt->h_addr_list[i], hostEnt->h_length) ; p += hostEnt->h_length ; } newIpAddrs[i] = NULL ; } } else { newIpAddrs = (char **) MALLOC (2 * sizeof(char *) + sizeof(ipAddr)) ; ASSERT (newIpAddrs != NULL) ; p = (char *)&newIpAddrs [ 2 ]; newIpAddrs[0] = p; memcpy (p, (char *)&ipAddr, sizeof(ipAddr)) ; newIpAddrs[1] = NULL; } if (newIpAddrs) { if (host->ipAddrs) FREE (host->ipAddrs) ; host->ipAddrs = newIpAddrs ; host->nextIpAddr = host->ipAddrs ; host->nextIpLookup = theTime () + dnsExpPeriod ; } else { /* failed to setup new addresses */ host->nextIpLookup = theTime () + dnsRetPeriod ; } } if (host->ipAddrs) { returnAddr = (struct in_addr *)(host->nextIpAddr[0]) ; if (*(++host->nextIpAddr) == NULL) host->nextIpAddr = host->ipAddrs ; } else returnAddr = NULL ; return returnAddr ; } void gPrintHostInfo (FILE *fp, u_int indentAmt) { Host h ; char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Host list : (count %d) {\n",indent,gHostCount) ; for (h = gHostList ; h != NULL ; h = h->next) printHostInfo (h,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printHostInfo (Host host, FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; ProcQElem qe ; double cnt = (host->blCount) ? (host->blCount) : 1.0; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sHost : %p {\n",indent,host) ; if (host == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } fprintf (fp,"%s peer-name : %s\n",indent,host->params->peerName) ; fprintf (fp,"%s ip-name : %s\n",indent,host->params->ipName) ; fprintf (fp,"%s abs-max-connections : %d\n",indent, host->params->absMaxConnections) ; fprintf (fp,"%s active-connections : %d\n",indent,host->activeCxns) ; fprintf (fp,"%s sleeping-connections : %d\n",indent,host->sleepingCxns) ; fprintf (fp,"%s initial-connections : %d\n",indent, host->params->initialConnections) ; fprintf (fp,"%s want-streaming : %s\n",indent, boolToString (host->params->wantStreaming)) ; fprintf (fp,"%s remote-streams : %s\n",indent, boolToString (host->remoteStreams)) ; fprintf (fp,"%s max-checks : %d\n",indent,host->params->maxChecks) ; fprintf (fp,"%s article-timeout : %d\n",indent, host->params->articleTimeout) ; fprintf (fp,"%s response-timeout : %d\n",indent, host->params->responseTimeout) ; fprintf (fp,"%s close-period : %d\n",indent, host->params->closePeriod) ; fprintf (fp,"%s port : %d\n",indent,host->params->portNum) ; fprintf (fp,"%s dynamic-method : %d\n",indent, host->params->dynamicMethod) ; fprintf (fp,"%s dynamic-backlog-filter : %2.1f\n",indent, host->params->dynBacklogFilter) ; fprintf (fp,"%s dynamic-backlog-lwm : %2.1f\n",indent, host->params->dynBacklogLowWaterMark) ; fprintf (fp,"%s dynamic-backlog-hwm : %2.1f\n",indent, host->params->dynBacklogHighWaterMark) ; fprintf (fp,"%s no-check on : %2.1f\n",indent, host->params->lowPassHigh) ; fprintf (fp,"%s no-check off : %2.1f\n",indent, host->params->lowPassLow) ; fprintf (fp,"%s no-check filter : %2.1f\n",indent, host->params->lowPassFilter) ; fprintf (fp,"%s backlog-limit : %d\n",indent, host->params->backlogLimit) ; fprintf (fp,"%s backlog-limit-high : %d\n",indent, host->params->backlogLimitHigh) ; fprintf (fp,"%s backlog-factor : %2.1f\n",indent, host->params->backlogFactor) ; fprintf (fp,"%s max-connections : %d\n",indent, host->maxConnections) ; fprintf (fp,"%s statistics-id : %d\n",indent,host->statsId) ; fprintf (fp,"%s ChkCxns-id : %d\n",indent,host->ChkCxnsId) ; fprintf (fp,"%s backed-up : %s\n",indent,boolToString (host->backedUp)); fprintf (fp,"%s backlog : %d\n",indent,host->backlog) ; fprintf (fp,"%s loggedModeOn : %s\n",indent, boolToString (host->loggedModeOn)) ; fprintf (fp,"%s loggedModeOff : %s\n",indent, boolToString (host->loggedModeOff)) ; fprintf (fp,"%s logged-backlog : %s\n",indent, boolToString (host->loggedBacklog)) ; fprintf (fp,"%s streaming-type changed : %s\n",indent, boolToString (host->notifiedChangedRemBlckd)) ; fprintf (fp,"%s articles offered : %d\n",indent,host->artsOffered) ; fprintf (fp,"%s articles accepted : %d\n",indent,host->artsAccepted) ; fprintf (fp,"%s articles not wanted : %d\n",indent, host->artsNotWanted) ; fprintf (fp,"%s articles rejected : %d\n",indent,host->artsRejected); fprintf (fp,"%s articles deferred : %d\n",indent,host->artsDeferred) ; fprintf (fp,"%s articles missing : %d\n",indent,host->artsMissing) ; fprintf (fp,"%s articles spooled : %d\n",indent,host->artsToTape) ; fprintf (fp,"%s because of queue overflow : %d\n",indent, host->artsQueueOverflow) ; fprintf (fp,"%s when the we closed the host : %d\n",indent, host->artsHostClose) ; fprintf (fp,"%s because the host was asleep : %d\n",indent, host->artsHostSleep) ; fprintf (fp,"%s articles unspooled : %d\n",indent,host->artsFromTape) ; fprintf (fp,"%s articles requeued from dropped connections : %d\n",indent, host->artsCxnDrop) ; fprintf (fp,"%s process articles offered : %d\n",indent, host->gArtsOffered) ; fprintf (fp,"%s process articles accepted : %d\n",indent, host->gArtsAccepted) ; fprintf (fp,"%s process articles not wanted : %d\n",indent, host->gArtsNotWanted) ; fprintf (fp,"%s process articles rejected : %d\n",indent, host->gArtsRejected); fprintf (fp,"%s process articles deferred : %d\n",indent, host->gArtsDeferred) ; fprintf (fp,"%s process articles missing : %d\n",indent, host->gArtsMissing) ; fprintf (fp,"%s process articles spooled : %d\n",indent, host->gArtsToTape) ; fprintf (fp,"%s because of queue overflow : %d\n",indent, host->gArtsQueueOverflow) ; fprintf (fp,"%s when the we closed the host : %d\n",indent, host->gArtsHostClose) ; fprintf (fp,"%s because the host was asleep : %d\n",indent, host->gArtsHostSleep) ; fprintf (fp,"%s process articles unspooled : %d\n",indent, host->gArtsFromTape) ; fprintf (fp,"%s process articles requeued from dropped connections : %d\n", indent, host->gArtsCxnDrop) ; fprintf (fp,"%s average (mean) queue length : %.1f\n", indent, (double) host->blAccum / cnt) ; fprintf (fp,"%s percentage of the time empty : %.1f\n", indent, 100.0 * host->blNone / cnt) ; fprintf (fp,"%s percentage of the time >0%%-25%% : %.1f\n", indent, 100.0 * host->blQuartile[0] / cnt) ; fprintf (fp,"%s percentage of the time 25%%-50%% : %.1f\n", indent, 100.0 * host->blQuartile[1] / cnt) ; fprintf (fp,"%s percentage of the time 50%%-75%% : %.1f\n", indent, 100.0 * host->blQuartile[2] / cnt) ; fprintf (fp,"%s percentage of the time 75%%-<100%% : %.1f\n", indent, 100.0 * host->blQuartile[3] / cnt) ; fprintf (fp,"%s percentage of the time full : %.1f\n", indent, 100.0 * host->blFull / cnt) ; fprintf (fp,"%s number of samples : %u\n", indent, host->blCount) ; fprintf (fp,"%s firstConnectTime : %s",indent, ctime (&host->firstConnectTime)); fprintf (fp,"%s connectTime : %s",indent,ctime (&host->connectTime)); fprintf (fp,"%s spoolTime : %s",indent,ctime (&host->spoolTime)) ; fprintf (fp,"%s last-spool-time : %s",indent, ctime (&host->lastSpoolTime)) ; #if 0 fprintf (fp,"%s tape {\n",indent) ; printTapeInfo (host->myTape,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; #else fprintf (fp,"%s tape : %p\n",indent,host->myTape) ; #endif fprintf (fp,"%s QUEUED articles {\n",indent) ; for (qe = host->queued ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s IN PROCESS articles {\n",indent) ; for (qe = host->processed ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s Connections {\n",indent) ; for (i = 0 ; i < host->maxConnections ; i++) { #if 0 if (host->connections[i] != NULL) printCxnInfo (*cxn,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,host->connections[i]) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s Active Connections {\n%s ",indent,indent) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->cxnActive[i]) fprintf (fp," [%d:%p]",i,host->connections[i]) ; fprintf (fp,"\n%s }\n",indent) ; fprintf (fp,"%s Sleeping Connections {\n%s ",indent,indent) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->cxnSleeping[i]) fprintf (fp," [%d:%p]",i,host->connections[i]) ; fprintf (fp,"\n%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* close down all the connections of the Host. All articles that are in * processes are still pushed out and then a QUIT is issued. The Host will * also spool all inprocess articles to tape incase the process is about to * be killed (they'll be refused next time around). When all Connections * report that they're gone, then the Host will delete itself. */ void hostClose (Host host) { u_int i ; u_int cxnCount ; dprintf (1,"Closing host %s\n",host->params->peerName) ; queuesToTape (host) ; delTape (host->myTape) ; host->myTape = NULL ; hostLogStats (host,true) ; clearTimer (host->statsId) ; clearTimer (host->ChkCxnsId) ; host->connectTime = 0 ; /* when we call cxnTerminate() on the last Connection, the Host objects will end up getting deleted out from under us (via hostCxnGone()). If we are running with a malloc that scribbles over memory after freeing it, then we'd fail in the second for loop test. Trying to access host->maxConnections. */ for (i = 0, cxnCount = 0 ; i < host->maxConnections ; i++) cxnCount += (host->connections [i] != NULL ? 1 : 0) ; for (i = 0 ; i < cxnCount ; i++) if (host->connections[i] != NULL) cxnTerminate (host->connections [i]) ; } /* * check if host should get more connections opened, or some closed... */ void hostChkCxns(TimeoutId tid /*unused*/, void *data) { Host host = (Host) data; u_int currArticles, currSentArticles, currTotalArticles, newMaxCxns ; double lastAPS, currAPS, percentTaken, ratio ; double backlogRatio, backlogMult; if(!host->maxCxnChk) return; ASSERT(host->params != NULL); if(host->secsInLastPeriod > 0) lastAPS = host->artsProcLastPeriod / (host->secsInLastPeriod * 1.0); else lastAPS = host->artsProcLastPeriod * 1.0; newMaxCxns = host->maxConnections; currArticles = (host->gArtsAccepted + host->gArtsRejected + (host->gArtsNotWanted / 4)) - host->lastCheckPoint ; host->lastCheckPoint = (host->gArtsAccepted + host->gArtsRejected + (host->gArtsNotWanted / 4)); currSentArticles = host->gArtsAccepted + host->gArtsRejected - host->lastSentCheckPoint ; host->lastSentCheckPoint = host->gArtsAccepted + host->gArtsRejected; currTotalArticles = host->gArtsAccepted + host->gArtsRejected + host->gArtsRejected + host->gArtsQueueOverflow - host->lastTotalCheckPoint ; host->lastTotalCheckPoint = host->gArtsAccepted + host->gArtsRejected + host->gArtsRejected + host->gArtsQueueOverflow ; currAPS = currArticles / (host->nextCxnTimeChk * 1.0) ; percentTaken = currSentArticles * 1.0 / ((currTotalArticles==0)?1:currTotalArticles); /* Get how full the queue is currently */ backlogRatio = (host->backlog * 1.0 / hostHighwater); backlogMult = 1.0/(1.0-host->params->dynBacklogFilter); dprintf(1,"%s hostChkCxns - entry filter=%3.3f blmult=%3.3f blratio=%3.3f",host->params->peerName,host->backlogFilter, backlogMult, backlogRatio); ratio = 0.0; /* ignore APS by default */ switch (host->params->dynamicMethod) { case METHOD_COMBINED: /* When a high % of articles is being taken, take notice of the * APS values. However for smaller %s, quickly start to ignore this * and concentrate on queue sizes */ ratio = percentTaken * percentTaken; /* nobreak; */ case METHOD_QUEUE: /* backlogFilter is an IIR filtered version of the backlogRatio. */ host->backlogFilter *= host->params->dynBacklogFilter; /* Penalise anything over the backlog HWM twice as severely * (otherwise we end up feeding some sites constantly * just below the HWM. This way random noise makes * such sites jump to one more connection * * Use factor (1-ratio) so if ratio is near 1 we ignore this */ if (backlogRatio>host->params->dynBacklogLowWaterMark/100.0) host->backlogFilter += (backlogRatio+1.0)/2.0 * (1.0-ratio); else host->backlogFilter += backlogRatio * (1.0-ratio); /* * Now bump it around for APS too */ if ((currAPS - lastAPS) >= 0.1) host->backlogFilter += ratio*((currAPS - lastAPS) + 1.0); else if ((currAPS - lastAPS) < -.2) host->backlogFilter -= ratio; dprintf(1,"%s hostChkCxns - entry hwm=%3.3f lwm=%3.3f new=%3.3f [%3.3f,%3.3f]", host->params->peerName,host->params->dynBacklogHighWaterMark, host->params->dynBacklogLowWaterMark,host->backlogFilter, (host->params->dynBacklogLowWaterMark * backlogMult / 100.0), (host->params->dynBacklogHighWaterMark * backlogMult / 100.0)); if (host->backlogFilter < (host->params->dynBacklogLowWaterMark * backlogMult / 100.0)) newMaxCxns--; else if (host->backlogFilter > (host->params->dynBacklogHighWaterMark * backlogMult / 100.0)) newMaxCxns++; break; case METHOD_STATIC: /* well not much to do, just check maxConnection = absMaxConnections */ ASSERT (host->maxConnections == MAXCONLIMIT(host->params->absMaxConnections)); break; case METHOD_APS: if ((currAPS - lastAPS) >= 0.1) newMaxCxns += (int)(currAPS - lastAPS) + 1 ; else if ((currAPS - lastAPS) < -.2) newMaxCxns--; break; } dprintf(1, "hostChkCxns: Chngs %f\n", currAPS - lastAPS); if (newMaxCxns < 1) newMaxCxns=1; if (newMaxCxns > MAXCONLIMIT(host->params->absMaxConnections)) newMaxCxns = MAXCONLIMIT(host->params->absMaxConnections); if (newMaxCxns != host->maxConnections) { syslog(LOG_NOTICE, HOST_MAX_CONNECTIONS, host->params->peerName, host->maxConnections,newMaxCxns); host->backlogFilter= ((host->params->dynBacklogLowWaterMark + host->params->dynBacklogHighWaterMark) /200.0 * backlogMult); host->artsProcLastPeriod = currArticles ; host->secsInLastPeriod = host->nextCxnTimeChk ; /* Alter MaxConnections and in doing so ensure we connect new cxns immediately if we are adding stuff */ hostAlterMaxConnections(host, host->params->absMaxConnections, newMaxCxns, true); } if(host->nextCxnTimeChk <= 240) host->nextCxnTimeChk *= 2; else host->nextCxnTimeChk = 300; dprintf(1, "prepareSleep hostChkCxns, %d\n", host->nextCxnTimeChk); host->ChkCxnsId = prepareSleep(hostChkCxns, host->nextCxnTimeChk, host); } /* * have the Host transmit the Article if possible. */ void hostSendArticle (Host host, Article article) { ASSERT(host->params != NULL); if (host->spoolTime > 0) { /* all connections are asleep */ host->artsHostSleep++ ; host->gArtsHostSleep++ ; host->artsToTape++ ; host->gArtsToTape++ ; tapeTakeArticle (host->myTape, article) ; return ; } /* at least one connection is feeding or waiting and there's no backlog */ if (host->queued == NULL) { u_int idx ; Article extraRef ; Connection cxn ; extraRef = artTakeRef (article) ; /* the referrence we give away */ /* stick on the queue of articles we've handed off--we're hopeful. */ queueArticle (article,&host->processed,&host->processedTail) ; /* first we try to give it to one of our active connections. We simply start at the bottom and work our way up. This way connections near the end of the list will get closed sooner from idleness. */ for (idx = 0 ; idx < host->maxConnections ; idx++) { if (host->cxnActive [idx] && (cxn = host->connections[idx]) != host->notThisCxn && cxnTakeArticle (cxn, extraRef)) return ; } /* Wasn't taken so try to give it to one of the waiting connections. */ for (idx = 0 ; idx < host->maxConnections ; idx++) if (!host->cxnActive [idx] && !host->cxnSleeping [idx] && (cxn = host->connections[idx]) != host->notThisCxn) { if (cxnTakeArticle (cxn, extraRef)) return ; else dprintf (1,"%s Inactive connection %d refused an article\n", host->params->peerName,idx) ; } /* this'll happen if all connections are feeding and all their queues are full, or if those not feeding are asleep. */ dprintf (1, "Couldn't give the article to a connection\n") ; delArticle (extraRef) ; remArticle (article,&host->processed,&host->processedTail) ; if (!cxnCheckstate (cxn)) { host->artsToTape++ ; host->gArtsToTape++ ; tapeTakeArticle (host->myTape,article) ; return ; } } /* either all the per connection queues were full or we already had a backlog, so there was no sense in checking. */ queueArticle (article,&host->queued,&host->queuedTail) ; host->backlog++ ; backlogToTape (host) ; } /* * called by the Host's connection when the remote is refusing postings * from us becasue we're not allowed (banner code 400). */ void hostCxnBlocked (Host host, Connection cxn, char *reason) { ASSERT(host->params != NULL); #ifndef NDEBUG { u_int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) ASSERT (host->cxnActive [i] == false) ; } #endif if (host->blockedReason == NULL) host->blockedReason = strdup (reason) ; if (host->activeCxns == 0 && host->spoolTime == 0) { host->blockedCxn = cxn ; /* to limit log notices */ syslog (LOG_NOTICE,REMOTE_BLOCKED, host->params->peerName, reason) ; } else if (host->activeCxns > 0 && !host->notifiedChangedRemBlckd) { syslog (LOG_NOTICE,CHANGED_REMOTE_BLOCKED, host->params->peerName,reason) ; host->notifiedChangedRemBlckd = true ; } else if (host->spoolTime != 0 && host->blockedCxn == cxn) { syslog (LOG_NOTICE,REMOTE_STILL_BLOCKED, host->params->peerName, reason) ; } } /* * Called by the Connection when it gets a response back to the MODE * STREAM command. It's now that we consider the connection usable. */ void hostRemoteStreams (Host host, Connection cxn, bool doesStreaming) { u_int i ; host->blockedCxn = NULL ; if (host->blockedReason != NULL) FREE (host->blockedReason) ; host->blockedReason = NULL ; /* we may have told the connection to quit while it was in the middle of connecting */ if (amClosing (host)) return ; if (host->connectTime == 0) /* first connection for this cycle. */ { if (doesStreaming && host->params->wantStreaming) syslog (LOG_NOTICE, REMOTE_DOES_STREAMING, host->params->peerName); else if (doesStreaming) syslog (LOG_NOTICE, REMOTE_STREAMING_OFF, host->params->peerName); else syslog (LOG_NOTICE, REMOTE_NO_STREAMING, host->params->peerName); if (host->spoolTime > 0) hostStopSpooling (host) ; /* set up the callback for statistics logging. */ if (host->statsId != 0) clearTimer (host->statsId) ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; if (host->ChkCxnsId != 0) clearTimer (host->ChkCxnsId); host->ChkCxnsId = prepareSleep (hostChkCxns, 30, host) ; host->remoteStreams = (host->params->wantStreaming ? doesStreaming : false) ; host->connectTime = theTime() ; if (host->firstConnectTime == 0) host->firstConnectTime = host->connectTime ; } else if (host->remoteStreams != doesStreaming && host->params->wantStreaming) syslog (LOG_NOTICE,STREAMING_CHANGE,host->params->peerName) ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { host->cxnActive [i] = true ; if (host->cxnSleeping [i]) host->sleepingCxns-- ; host->cxnSleeping [i] = false ; break ; } ASSERT (i != host->maxConnections) ; host->activeCxns++ ; hostLogStatus () ; } /* * Called by the connection when it is no longer connected to the * remote. Perhaps due to getting a code 400 to an IHAVE, or due to a * periodic close. */ void hostCxnDead (Host host, Connection cxn) { u_int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (host->cxnActive [i]) /* won't be active if got 400 on banner */ { host->cxnActive [i] = false ; host->activeCxns-- ; if (!amClosing (host) && host->activeCxns == 0) { clearTimer (host->statsId) ; clearTimer (host->ChkCxnsId) ; hostLogStats (host,true) ; host->connectTime = 0 ; } } else if (host->cxnSleeping [i]) /* cxnNuke can be called on sleepers */ { host->cxnSleeping [i] = false ; host->sleepingCxns-- ; } break ; } ASSERT (i < host->maxConnections) ; hostLogStatus () ; } /* * Called by the Connection when it is going to sleep so the Host won't * bother trying to give it Articles */ void hostCxnSleeping (Host host, Connection cxn) { u_int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (!host->cxnSleeping [i]) { host->cxnSleeping [i] = true ; host->sleepingCxns++ ; } if (host->spoolTime == 0 && host->sleepingCxns >= host->maxConnections) hostStartSpooling (host) ; break ; } ASSERT (i < host->maxConnections) ; hostLogStatus () ; } /* * Called by the Connection when it goes into the waiting state. */ void hostCxnWaiting (Host host, Connection cxn) { u_int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (host->cxnSleeping [i]) host->sleepingCxns-- ; host->cxnSleeping [i] = false ; break ; } ASSERT (i < host->maxConnections) ; if (host->spoolTime > 0) hostStopSpooling (host) ; hostLogStatus () ; } /* * Called by the Connection when it is about to delete itself. */ bool hostCxnGone (Host host, Connection cxn) { u_int i; bool oneThere = false ; /* forget about the Connection and see if we are still holding any live connections still. */ for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] == cxn) { if (!amClosing (host)) syslog (LOG_ERR,CONNECTION_DISAPPEARING,host->params->peerName,i) ; host->connections [i] = NULL ; if (host->cxnActive [i]) { host->cxnActive [i] = false ; host->activeCxns-- ; } else if (host->cxnSleeping [i]) { host->cxnSleeping [i] = false ; host->sleepingCxns-- ; } } else if (host->connections [i] != NULL) oneThere = true ; /* remove the host if it has no connexions */ if ( !oneThere ) { time_t now = theTime() ; u_int hostsLeft ; if (host->firstConnectTime > 0) syslog (LOG_NOTICE,REALLY_FINAL_STATS, host->params->peerName, (long) (now - host->firstConnectTime), host->gArtsOffered, host->gArtsAccepted, host->gArtsNotWanted, host->gArtsRejected, host->gArtsMissing) ; hostsLeft = listenerHostGone (host->listener, host) ; delHost (host) ; if (hostsLeft == 0) syslog (LOG_NOTICE,PROCESS_FINAL_STATS, (long) (now - start), procArtsOffered, procArtsAccepted, procArtsNotWanted,procArtsRejected, procArtsMissing) ; /* return true if that was the last host */ return (hostsLeft == 0 ? true : false) ; } /* return false because there is still at least one host (this one) */ return false ; } /* * The connections has offered an article to the remote. */ void hostArticleOffered (Host host, Connection cxn) { (void) cxn ; /* keep lint happy. */ host->artsOffered++ ; host->gArtsOffered++ ; procArtsOffered++ ; } /* * Article was succesfully transferred. */ void hostArticleAccepted (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; dprintf (5,"Article %s (%s) was transferred\n", msgid, filename) ; host->artsAccepted++ ; host->gArtsAccepted++ ; procArtsAccepted++ ; /* host has two references to the article here... the parameter `article' and the queue */ delArticle (article) ; /* drop the parameter reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* * remote said no thanks to an article. */ void hostArticleNotWanted (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; dprintf (5,"Article %s (%s) was not wanted\n", msgid, filename) ; host->artsNotWanted++ ; host->gArtsNotWanted++ ; procArtsNotWanted++ ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* * remote rejected the article after it was was transferred */ void hostArticleRejected (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; dprintf (5,"Article %s (%s) was rejected\n", msgid, filename) ; host->artsRejected++ ; host->gArtsRejected++ ; procArtsRejected++ ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; } /* * The remote wants us to retry the article later. */ void hostArticleDeferred (Host host, Connection cxn, Article article) { host->artsDeferred++ ; host->gArtsDeferred++ ; procArtsDeferred++ ; if (!amClosing (host)) { Article extraRef ; extraRef = artTakeRef (article) ; /* hold a reference until requeued */ articleGone (host,cxn,article) ; /* drop from the queue */ hostSendArticle (host, article) ; /* requeue it */ delArticle (extraRef) ; } else delArticle(article); /*drop parameter reference if not sent to tape*/ } /* * The Connection is giving the article back to the Host, but it doesn't * want a new one in return. */ void hostTakeBackArticle (Host host, Connection cxn, Article article) { (void) cxn ; /* keep lint happy */ if (!amClosing (host)) { Article extraRef ; host->artsCxnDrop++ ; host->gArtsCxnDrop++ ; extraRef = artTakeRef (article) ; /* hold a reference until requeued */ articleGone (host,NULL,article) ; /* drop from the queue */ host->notThisCxn = cxn; hostSendArticle (host, article) ; /* requeue it */ host->notThisCxn = NULL; delArticle (extraRef) ; } else delArticle(article); /*drop parameter reference if not sent to tape*/ } /* * The disk file for the article is no longer valid */ void hostArticleIsMissing (Host host, Connection cxn, Article article) { const char *filename = artFileName (article) ; const char *msgid = artMsgId (article) ; dprintf (5, "%s article is missing %s %s\n", host->params->peerName, msgid, filename) ; host->artsMissing++ ; host->gArtsMissing++ ; procArtsMissing++ ; /* host has two references to the article here... `article' and the queue */ delArticle (article) ; /* drop the `article' reference */ if (!amClosing (host)) articleGone (host,cxn,article) ; /* and the one in the queue */ } /* The Connection wants something to do. This is called by the Connection * after it has transferred an article. This is what keeps the pipes full * of data off the tapes if the input from inn is idle. */ bool hostGimmeArticle (Host host, Connection cxn) { Article article = NULL ; bool gaveSomething = false ; size_t amtToGive = cxnQueueSpace (cxn) ; /* may be more than one */ if (amClosing (host)) { dprintf (5,"%s no article to give due to closing\n",host->params->peerName) ; return false ; } if (amtToGive == 0) dprintf (5,"%s Queue space is zero....\n",host->params->peerName) ; while (amtToGive > 0) { bool tookIt ; if ((article = remHead (&host->queued,&host->queuedTail)) != NULL) { host->backlog-- ; tookIt = cxnQueueArticle (cxn,artTakeRef (article)) ; ASSERT (tookIt == true) ; queueArticle (article,&host->processed,&host->processedTail) ; amtToGive-- ; gaveSomething = true ; } else if ((article = getArticle (host->myTape)) != NULL) { /* go to the tapes */ tookIt = cxnQueueArticle (cxn,artTakeRef (article)) ; ASSERT (tookIt == true) ; host->artsFromTape++ ; host->gArtsFromTape++ ; queueArticle (article,&host->processed,&host->processedTail) ; amtToGive-- ; gaveSomething = true ; } else { /* we had nothing left to give... */ if (host->processed == NULL) /* and if nothing outstanding... */ listenerHostIsIdle (host->listener,host) ; /* tell our owner */ amtToGive = 0 ; } } return gaveSomething ; } /* * get the name that INN uses for this host */ const char *hostPeerName (Host host) { ASSERT (host != NULL) ; return host->params->peerName ; } /* return true if the Connections for this host should attempt to do streaming. */ bool hostWantsStreaming (Host host) { return host->params->wantStreaming ; } u_int hostMaxChecks (Host host) { return host->params->maxChecks ; } /**********************************************************************/ /** CLASS FUNCTIONS **/ /**********************************************************************/ /* * Set the state of whether each Connection is told to log its stats when * its controlling Host logs its stats. */ void hostLogConnectionStats (bool val) { logConnectionStats = val ; } bool hostLogConnectionStatsP (void) { return logConnectionStats ; } /* * Called by one of the Host's Connection's when it (the Connection) * switches into or out of no-CHECK mode. */ void hostLogNoCheckMode (Host host, bool on, double low, double cur, double high) { if (on && host->loggedModeOn == false) { syslog (LOG_NOTICE, STREAMING_MODE_SWITCH, host->params->peerName, low, cur, high) ; host->loggedModeOn = true ; } else if (!on && host->loggedModeOff == false) { syslog (LOG_NOTICE, STREAMING_MODE_UNDO, host->params->peerName, low, cur, high) ; host->loggedModeOff = true ; } } void hostSetStatusFile (const char *filename) { FILE *fp ; if (filename == NULL) die ("Can't set status file name with a NULL filename\n") ; else if (*filename == '\0') die ("Can't set status file name with a empty string\n") ; if (*filename == '/') statusFile = strdup (filename) ; else { const char *tapeDir = getTapeDirectory() ; statusFile = malloc (strlen (tapeDir) + strlen (filename) + 2) ; sprintf (statusFile,"%s/%s",tapeDir,filename) ; } if ((fp = fopen (statusFile,"w")) == NULL) { syslog (LOG_ERR,"Status file is not a valid pathname: %s", statusFile) ; FREE (statusFile) ; statusFile = NULL ; } else fclose (fp) ; } /**********************************************************************/ /** PRIVATE FUNCTIONS **/ /**********************************************************************/ #define INHERIT 1 #define NO_INHERIT 0 static HostParams hostDetails (scope *s, char *name, bool isDefault, FILE *fp) { long iv ; int bv, vival, inherit ; HostParams p; char * q; double rv, l, h ; value * v; p=newHostParams(isDefault?NULL:defaultParams); if (isDefault) { ASSERT (name==NULL); } else { if (name) { p->peerName=strdup(name); } if (s != NULL) if (getString (s,IP_NAME,&q,NO_INHERIT)) p->ipName = q ; else p->ipName = strdup (name) ; } /* check required global defaults are there and have good values */ #define GETINT(sc,f,n,min,max,req,val,inh) \ vival = validateInteger(f,n,min,max,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ val = 0; \ } while(0); \ iv = 0 ; \ (void) getInteger (sc,n,&iv,inh) ; \ val = (u_int) iv ; \ #define GETREAL(sc,f,n,min,max,req,val,inh) \ vival = validateReal(f,n,min,max,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ rv = 0; \ } while(0); \ rv = 0 ; \ (void) getReal (sc,n,&rv,inh) ; \ val = rv ; \ #define GETBOOL(sc,f,n,req,val,inh) \ vival = validateBool(f,n,req,val,sc,inh); \ if (isDefault) do{ \ if(vival==VALUE_WRONG_TYPE) \ { \ logOrPrint(LOG_CRIT,fp,"cannot continue"); \ exit(1); \ } \ else if(vival != VALUE_OK) \ bv = 0; \ } while(0); \ bv = 0 ; \ (void) getBool (sc,n,&bv,inh) ; \ val = (bv ? true : false); \ inherit = isDefault?NO_INHERIT:INHERIT; GETINT(s,fp,"article-timeout",0,LONG_MAX,REQ,p->articleTimeout, inherit); GETINT(s,fp,"response-timeout",0,LONG_MAX,REQ,p->responseTimeout, inherit); GETINT(s,fp,"close-period",0,LONG_MAX,REQ,p->closePeriod, inherit); GETINT(s,fp,"initial-connections",0,LONG_MAX,REQ,p->initialConnections, inherit); GETINT(s,fp,"max-connections",0,LONG_MAX,REQ,p->absMaxConnections, inherit); GETINT(s,fp,"max-queue-size",1,LONG_MAX,REQ,p->maxChecks, inherit); GETBOOL(s,fp,"streaming",REQ,p->wantStreaming, inherit); GETREAL(s,fp,"no-check-high",0.0,100.0,REQ,p->lowPassHigh, inherit); GETREAL(s,fp,"no-check-low",0.0,100.0,REQ,p->lowPassLow, inherit); GETREAL(s,fp,"no-check-filter",0.1,DBL_MAX,REQ,p->lowPassFilter, inherit); GETINT(s,fp,"port-number",0,LONG_MAX,REQ,p->portNum, inherit); GETINT(s,fp,"backlog-limit",0,LONG_MAX,REQ,p->backlogLimit, inherit); if (findValue (s,"backlog-factor",inherit) == NULL && findValue (s,"backlog-limit-high",inherit) == NULL) { logOrPrint (LOG_ERR,fp,NOFACTORHIGH,"backlog-factor",LIMIT_FUDGE) ; addReal (s,"backlog-factor",LIMIT_FUDGE) ; rv = 0 ; } GETINT(s,fp,"backlog-limit-high",0,LONG_MAX,NOTREQNOADD,p->backlogLimitHigh, inherit); GETREAL(s,fp,"backlog-factor",1.0,DBL_MAX,NOTREQNOADD,p->backlogFactor, inherit); GETINT(s,fp,"dynamic-method",0,3,REQ,p->dynamicMethod, inherit); GETREAL(s,fp,"dynamic-backlog-filter",0.0,DBL_MAX,REQ,p->dynBacklogFilter, inherit); GETREAL(s,fp,"dynamic-backlog-low",0.0,100.0,REQ,p->dynBacklogLowWaterMark, inherit); GETREAL(s,fp,"dynamic-backlog-high",0.0,100.0,REQ,p->dynBacklogHighWaterMark, inherit); l=p->lowPassLow; h=p->lowPassHigh; if (l > h) { logOrPrint (LOG_ERR,fp,NOCK_LOWVSHIGH,l,h,NOCHECKLOW,NOCHECKHIGH) ; rv = 0 ; v = findValue (s,"no-check-low",NO_INHERIT) ; v->v.real_val = p->lowPassLow = NOCHECKLOW ; v = findValue (s,"no-check-high",NO_INHERIT) ; v->v.real_val = p->lowPassHigh = NOCHECKHIGH ; } else if (h - l < 5.0) logOrPrint (LOG_WARNING,fp,NOCK_LOWHIGHCLOSE,l,h) ; return p; } static HostParams getHostInfo (void) { static int idx = 0 ; value *v ; scope *s ; HostParams p=NULL; bool isGood = false ; if (topScope == NULL) return false ; while ((v = getNextPeer (&idx)) != NULL) { if (!ISPEER (v)) continue ; s = v->v.scope_val ; p=hostDetails(s,v->name,false,NULL); isGood = true ; break ; } if (v == NULL) idx = 0 ; /* start over next time around */ return p; } /* * fully delete and clean up the Host object. */ void delHost (Host host) { Host h,q ; for (h = gHostList, q = NULL ; h != NULL ; q = h, h = h->next) if (h == host) { if (gHostList == h) gHostList = gHostList->next ; else q->next = h->next ; break ; } ASSERT (h != NULL) ; delTape (host->myTape) ; FREE (host->connections) ; FREE (host->cxnActive) ; FREE (host->cxnSleeping) ; FREE (host->params->peerName) ; FREE (host->params->ipName) ; if (host->ipAddrs) FREE (host->ipAddrs) ; FREE (host) ; gHostCount-- ; } static Host findHostByName (char *name) { Host h; for (h = gHostList; h != NULL; h = h->next) if ( strcmp(h->params->peerName, name) == 0 ) return h; return NULL; } /* * the article can be dropped from the process queue and the connection can * take a new article if there are any to be had. */ static void articleGone (Host host, Connection cxn, Article article) { if ( !remArticle (article,&host->processed,&host->processedTail) ) die ("remArticle in articleGone failed") ; delArticle (article) ; if (cxn != NULL) hostGimmeArticle (host,cxn) ; /* may not give anything over */ } /* * One of the Connections for this Host has reestablished itself, so stop * spooling article info to disk. */ static void hostStopSpooling (Host host) { ASSERT (host->spoolTime != 0) ; clearTimer (host->statsId) ; hostLogStats (host,true) ; host->spoolTime = 0 ; } /* * No connections are active and we're getting response 201 or 400 (or some * such) so that we need to start spooling article info to disk. */ static void hostStartSpooling (Host host) { ASSERT (host->spoolTime == 0) ; queuesToTape (host) ; hostLogStats (host,true) ; host->spoolTime = theTime() ; if (host->firstConnectTime == 0) host->firstConnectTime = host->spoolTime ; /* don't want to log too frequently */ if (SPOOL_LOG_PERIOD > 0 && (host->spoolTime - host->lastSpoolTime) > SPOOL_LOG_PERIOD) { syslog (LOG_NOTICE,SPOOLING,host->params->peerName) ; host->lastSpoolTime = host->spoolTime ; } host->connectTime = 0 ; host->notifiedChangedRemBlckd = false ; clearTimer (host->statsId) ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; } /* * Time to log the statistics for the Host. If FINAL is true then the * counters will be reset. */ static void hostLogStats (Host host, bool final) { time_t now = theTime() ; time_t *startPeriod ; double cnt = (host->blCount) ? (host->blCount) : 1.0; if (host->spoolTime == 0 && host->connectTime == 0) return ; /* host has never connected and never started spooling*/ startPeriod = (host->spoolTime != 0 ? &host->spoolTime : &host->connectTime); if (now - *startPeriod >= statsResetPeriod) final = true ; if (host->spoolTime != 0) syslog (LOG_NOTICE, HOST_SPOOL_STATS, host->params->peerName, (final ? "final" : "checkpoint"), (long) (now - host->spoolTime), host->artsToTape, host->artsHostClose, host->artsHostSleep) ; else syslog (LOG_NOTICE, HOST_STATS_MSG, host->params->peerName, (final ? "final" : "checkpoint"), (long) (now - host->connectTime), host->artsOffered, host->artsAccepted, host->artsNotWanted, host->artsRejected, host->artsMissing, host->artsToTape, host->artsHostClose, host->artsFromTape, host->artsDeferred, host->artsCxnDrop, (double)host->blAccum/cnt, hostHighwater, (100.0*host->blNone)/cnt, (100.0*host->blQuartile[0])/cnt, (100.0*host->blQuartile[1])/cnt, (100.0*host->blQuartile[2])/cnt, (100.0*host->blQuartile[3])/cnt, (100.0*host->blFull)/cnt) ; if (logConnectionStats) { u_int i ; for (i = 0 ; i < host->maxConnections ; i++) if (host->connections [i] != NULL && host->cxnActive [i]) cxnLogStats (host->connections [i],final) ; } /* one 'spooling backlog' message per stats logging period */ host->loggedBacklog = false ; host->loggedModeOn = host->loggedModeOff = false ; if (final) { host->artsOffered = 0 ; host->artsAccepted = 0 ; host->artsNotWanted = 0 ; host->artsRejected = 0 ; host->artsDeferred = 0 ; host->artsMissing = 0 ; host->artsToTape = 0 ; host->artsQueueOverflow = 0 ; host->artsCxnDrop = 0 ; host->artsHostSleep = 0 ; host->artsHostClose = 0 ; host->artsFromTape = 0 ; *startPeriod = theTime () ; /* in of case STATS_RESET_PERIOD */ } /* reset these each log period */ host->blNone = 0 ; host->blFull = 0 ; host->blQuartile[0] = host->blQuartile[1] = host->blQuartile[2] = host->blQuartile[3] = 0; host->blAccum = 0; host->blCount = 0; #if 0 /* XXX turn this section on to get a snapshot at each log period. */ if (gPrintInfo != NULL) gPrintInfo () ; #endif } /* * Log the status of the Hosts. */ extern char *versionInfo ; static void hostLogStatus (void) { FILE *fp = NULL ; Host h ; bool anyToLog = false ; u_int peerNum = 0, actConn = 0, slpConn = 0, maxcon = 0 ; static bool logged = false ; static bool flogged = false ; if (statusFile == NULL && !logged) { syslog (LOG_ERR,"No status file to write to") ; logged = true ; return ; } logged = false ; for (h = gHostList ; h != NULL ; h = h->next) if (h->myTape != NULL) /* the host deletes its tape when it's closing */ { anyToLog = true ; peerNum++ ; actConn += h->activeCxns ; slpConn += h->sleepingCxns ; maxcon += h->maxConnections ; } if (!anyToLog) return ; lastStatusLog = theTime() ; if ((fp = fopen (statusFile,"w")) == NULL) { if ( !flogged ) syslog (LOG_ERR,NO_STATUS,statusFile) ; flogged = true ; } else { char timeString [30] ; time_t now ; flogged = false ; now = time (NULL) ; strcpy (timeString,ctime (&now)) ; if (genHtml) fprintf (fp,"
\n\n") ;

      fprintf (fp,"%s\npid %d started %s\nUpdated: %s",
               versionInfo,(int) myPid,startTime,timeString) ;
      fprintf (fp,"(peers: %d active-cxns: %d sleeping-cxns: %d idle-cxns: %d)\n\n",
               peerNum, actConn, slpConn,(maxcon - (actConn + slpConn))) ;

      fprintf (fp,"Configuration file: %s\n\n",configFile) ;

      mainLogStatus (fp) ;
      listenerLogStatus (fp) ;

/*
Default peer configuration parameters:
    article timeout: 600       initial connections: 1
   response timeout: 300           max connections: 5
       close period: 6000               max checks: 25
     want streaming: true           dynamic method: 1
        no-check on: 95.0%     dynamic backlog low: 25%
       no-check off: 90.0%    dynamic backlog high: 50%
    no-check filter: 50.0   dynamic backlog filter: 0.7
  backlog low limit: 1024                 port num: 119
 backlog high limit: 1280
     backlog factor: 1.1
*/
      
      fprintf(fp,"Default peer configuration parameters:\n") ;
      fprintf(fp,"    article timeout: %-5d     initial connections: %d\n",
	    defaultParams->articleTimeout,
	    defaultParams->initialConnections) ;
      fprintf(fp,"   response timeout: %-5d         max connections: %d\n",
	    defaultParams->responseTimeout,
	    defaultParams->absMaxConnections) ;
      fprintf(fp,"       close period: %-5d              max checks: %d\n",
	    defaultParams->closePeriod,
	    defaultParams->maxChecks) ;
      fprintf(fp,"     want streaming: %-5s          dynamic method: %d\n",
	    defaultParams->wantStreaming ? "true " : "false",
	    defaultParams->dynamicMethod) ;
      fprintf(fp,"        no-check on: %-2.1f%%     dynamic backlog low: %-2.1f%%\n",
	    defaultParams->lowPassHigh,
	    defaultParams->dynBacklogLowWaterMark) ;
      fprintf(fp,"       no-check off: %-2.1f%%    dynamic backlog high: %-2.1f%%\n",
	    defaultParams->lowPassLow,
	    defaultParams->dynBacklogHighWaterMark) ;
      fprintf(fp,"    no-check filter: %-2.1f   dynamic backlog filter: %-2.1f\n",
	    defaultParams->lowPassFilter,
	    defaultParams->dynBacklogFilter) ;
      fprintf(fp,"  backlog limit low: %-5d\n",
	    defaultParams->backlogLimit);
      fprintf(fp," backlog limit high: %-5d\n",
	    defaultParams->backlogLimitHigh);
      fprintf(fp,"     backlog factor: %1.1f\n\n",
	    defaultParams->backlogFactor);

      tapeLogGlobalStatus (fp) ;

      fprintf (fp,"\n") ;
      fprintf (fp,"global (process)\n") ;
      
      fprintf (fp, "   seconds: %-7ld\n", (long) (now - start)) ;
      fprintf (fp, "   offered: %-7ld\n", procArtsOffered) ;
      fprintf (fp, "  accepted: %-7ld\n", procArtsAccepted) ;
      fprintf (fp, "   refused: %-7ld\n", procArtsNotWanted) ;
      fprintf (fp, "  rejected: %-7ld\n", procArtsRejected) ;
      fprintf (fp, "   missing: %-7ld\n", procArtsMissing) ;
      fprintf (fp, "  deferred: %-7ld\n", procArtsDeferred) ;
      fprintf (fp, "\n");
      
      for (h = gHostList ; h != NULL ; h = h->next)
        hostPrintStatus (h,fp) ;

      if (genHtml) 
        fprintf (fp,"
\n") ; fclose (fp) ; } } /* * This prints status information for each host. An example of the * format of the output is: * * sitename * seconds: 351 art. timeout: 400 ip name: foo.bar * offered: 1194 resp. timeout: 240 port: 119 * accepted: 178 want streaming: yes active cxns: 6 * refused: 948 is streaming: yes sleeping cxns: 0 * rejected: 31 max checks: 25 initial cxns: 5 * missing: 0 no-check on: 95.0% idle cxns: 4 * deferred: 0 no-check off: 95.0% max cxns: 8/10 * requeued: 0 no-check fltr: 50.0 queue length: 0 * spooled: 0 empty: 100.0% *[overflow]: 0 dyn b'log low: 25% >0%-25%: 0.0% *[on_close]: 0 dyn b'log high: 50% 25%-50%: 0.0% *[sleeping]: 0 dyn b'log stat: 37% 50%-75%: 0.0% * unspooled: 0 dyn b'log fltr: 0.7 75%-<100%: 0.0% * full: 0.0% * backlog low limit: 1000000 * backlog high limit: 2000000 (factor 2.0) * backlog shrinkage: 0 bytes (from current file) * */ static void hostPrintStatus (Host host, FILE *fp) { time_t now = theTime() ; double cnt = (host->blCount) ? (host->blCount) : 1.0; ASSERT (host != NULL) ; ASSERT (fp != NULL) ; fprintf (fp,"%s",host->params->peerName); if (host->blockedReason != NULL) fprintf (fp," (remote status: ``%s'')",host->blockedReason) ; fputc ('\n',fp) ; fprintf (fp, " seconds: %-7ld art. timeout: %-5d ip name: %s\n", host->firstConnectTime > 0 ? (long)(now - host->firstConnectTime) : 0, host->params->articleTimeout, host->params->ipName) ; fprintf (fp, " offered: %-7ld resp. timeout: %-5d port: %d\n", (long) host->gArtsOffered, host->params->responseTimeout, host->params->portNum); fprintf (fp, " accepted: %-7ld want streaming: %s active cxns: %d\n", (long) host->gArtsAccepted, (host->params->wantStreaming ? "yes" : "no "), host->activeCxns) ; fprintf (fp, " refused: %-7ld is streaming: %s sleeping cxns: %d\n", (long) host->gArtsNotWanted, (host->remoteStreams ? "yes" : "no "), host->sleepingCxns) ; fprintf (fp, " rejected: %-7ld max checks: %-5d initial cxns: %d\n", (long) host->gArtsRejected, host->params->maxChecks, host->params->initialConnections) ; fprintf (fp, " missing: %-7ld no-check on: %-3.1f%% idle cxns: %d\n", (long) host->gArtsMissing, host->params->lowPassHigh, host->maxConnections - (host->activeCxns + host->sleepingCxns)) ; fprintf (fp, " deferred: %-7ld no-check off: %-3.1f%% max cxns: %d/%d\n", (long) host->gArtsDeferred, host->params->lowPassLow, host->maxConnections, host->params->absMaxConnections) ; fprintf (fp, " requeued: %-7ld no-check fltr: %-3.1f queue length: %-3.1f\n", (long) host->gArtsCxnDrop, host->params->lowPassFilter, (double)host->blAccum / cnt) ; fprintf (fp, " spooled: %-7ld dynamic method: %-5d empty: %-3.1f%%\n", (long) host->gArtsToTape, host->params->dynamicMethod, 100.0 * host->blNone / cnt) ; fprintf (fp, "[overflow]: %-7ld dyn b'log low: %-3.1f%% >0%%-25%%: %-3.1f%%\n", (long) host->gArtsQueueOverflow, host->params->dynBacklogLowWaterMark, 100.0 * host->blQuartile[0] / cnt) ; fprintf (fp, "[on_close]: %-7ld dyn b'log high: %-3.1f%% 25%%-50%%: %-3.1f%%\n", (long) host->gArtsHostClose, host->params->dynBacklogHighWaterMark, 100.0 * host->blQuartile[1] / cnt) ; fprintf (fp, "[sleeping]: %-7ld dyn b'log stat: %-3.1f%% 50%%-75%%: %-3.1f%%\n", (long) host->gArtsHostSleep, host->backlogFilter*100.0*(1.0-host->params->dynBacklogFilter), 100.0 * host->blQuartile[2] / cnt) ; fprintf (fp, " unspooled: %-7ld dyn b'log fltr: %-3.1f 75%%-<100%%: %-3.1f%%\n", (long) host->gArtsFromTape, host->params->dynBacklogLowWaterMark, 100.0 * host->blQuartile[3] / cnt) ; fprintf (fp, " full: %-3.1f%%\n", 100.0 * host->blFull / cnt) ; tapeLogStatus (host->myTape,fp) ; #ifdef XXX_STATSHACK { time_t now = time(NULL), sec = (long) (now - host->connectTime); float or, ar, rr, jr; if (sec != 0) { or = (float) host->artsOffered / (float) sec; ar = (float) host->artsAccepted / (float) sec; rr = (float) host->artsNotWanted / (float) sec; jr = (float) host->artsRejected / (float) sec; fprintf(fp, "\t\tor %02.2f ar %02.2f rr %02.2f jr %02.2f\n", or, ar, rr, jr); } fprintf(fp, "\tmissing %d spooled %d\n", host->artsMissing,host->backlogSpooled); } #endif /* XXX_STATSHACK */ fprintf (fp, "\n\n"); } /* * The callback function for the statistics timer to call. */ static void hostStatsTimeoutCbk (TimeoutId tid, void *data) { Host host = (Host) data ; time_t now = theTime () ; (void) tid ; /* keep lint happy */ ASSERT (tid == host->statsId) ; if (!amClosing (host)) hostLogStats (host, false) ; if (now - lastStatusLog >= statsPeriod) hostLogStatus () ; host->statsId = prepareSleep (hostStatsTimeoutCbk, statsPeriod, host) ; } /* if the host has too many unprocessed articles so we send some to the tape. */ static void backlogToTape (Host host) { Article article ; while (host->backlog > hostHighwater) { if (!host->loggedBacklog) { #if 0 /* this message is pretty useless and confuses people */ syslog (LOG_NOTICE,BACKLOG_TO_TAPE,host->params->peerName /* ,host->backlog */) ; #endif host->loggedBacklog = true ; } article = remHead (&host->queued,&host->queuedTail) ; ASSERT(article != NULL); host->artsQueueOverflow++ ; host->gArtsQueueOverflow++ ; host->artsToTape++ ; host->gArtsToTape++ ; host->backlog--; tapeTakeArticle (host->myTape,article) ; } } /* * Returns true of the Host is in the middle of closing down. */ static bool amClosing (Host host) { return (host->myTape == NULL ? true : false) ; } /* * flush all queued articles all the way out to disk. */ static void queuesToTape (Host host) { Article art ; while ((art = remHead (&host->processed,&host->processedTail)) != NULL) { host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } while ((art = remHead (&host->queued,&host->queuedTail)) != NULL) { host->backlog-- ; host->artsHostClose++ ; host->gArtsHostClose++ ; host->artsToTape++ ; host->gArtsToTape++ ; tapeTakeArticle (host->myTape,art) ; } } #define QUEUE_ELEM_POOL_SIZE ((4096 - 2 * (sizeof (void *))) / (sizeof (struct proc_q_elem))) static ProcQElem queueElemPool ; /* * Add an article to the given queue. */ static void queueArticle (Article article, ProcQElem *head, ProcQElem *tail) { ProcQElem elem ; if (queueElemPool == NULL) { int i ; queueElemPool = ALLOC (struct proc_q_elem, QUEUE_ELEM_POOL_SIZE) ; ASSERT (queueElemPool != NULL) ; for (i = 0; i < QUEUE_ELEM_POOL_SIZE - 1; i++) queueElemPool[i] . next = &(queueElemPool [i + 1]) ; queueElemPool [QUEUE_ELEM_POOL_SIZE-1] . next = NULL ; } elem = queueElemPool ; ASSERT (elem != NULL) ; queueElemPool = queueElemPool->next ; elem->article = article ; elem->next = NULL ; elem->prev = *tail ; if (*tail != NULL) (*tail)->next = elem ; else *head = elem ; *tail = elem ; } /* * remove the article from the queue */ static bool remArticle (Article article, ProcQElem *head, ProcQElem *tail) { ProcQElem elem ; ASSERT (head != NULL) ; ASSERT (tail != NULL) ; /* we go backwards down the list--probably faster */ elem = *tail ; while (elem != NULL && elem->article != article) elem = elem->prev ; if (elem != NULL) { if (elem->prev != NULL) elem->prev->next = elem->next ; if (elem->next != NULL) elem->next->prev = elem->prev ; if (*head == elem) *head = elem->next ; if (*tail == elem) *tail = elem->prev ; elem->next = queueElemPool ; queueElemPool = elem ; return true ; } else return false ; } /* * remove the article that's at the head of the queue and return * it. Returns NULL if the queue is empty. */ static Article remHead (ProcQElem *head, ProcQElem *tail) { ProcQElem elem ; Article art ; ASSERT (head != NULL) ; ASSERT (tail != NULL) ; ASSERT ((*head == NULL && *tail == NULL) || (*head != NULL && *tail != NULL)) ; if (*head == NULL) return NULL ; elem = *head ; art = elem->article ; *head = elem->next ; if (elem->next != NULL) elem->next->prev = NULL ; if (*tail == elem) *tail = NULL ; elem->next = queueElemPool ; queueElemPool = elem ; return art ; } static int validateInteger (FILE *fp, const char *name, long low, long high, int required, long setval, scope * sc, u_int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addInteger (s,p ? p + 1 : name,setval) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp,NODEFN,name) ; } else if (required) logOrPrint (LOG_INFO,fp,ADDMISSINGINT,name,setval) ; } else if (v != NULL && v->type != intval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp,NOTINT,name) ; } else if (v != NULL && low != LONG_MIN && v->v.int_val < low) { rval = VALUE_TOO_LOW ; logOrPrint (LOG_ERR,fp,INT_TO_LOW,name,v->v.int_val, "global scope",low,low) ; v->v.int_val = low ; } else if (v != NULL && high != LONG_MAX && v->v.int_val > high) { rval = VALUE_TOO_HIGH ; logOrPrint(LOG_ERR,fp,INT_TO_HIGH,name,v->v.int_val, "global scope",high,high); v->v.int_val = high ; } return rval ; } static int validateReal (FILE *fp, const char *name, double low, double high, int required, double setval, scope * sc, u_int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addReal (s,p ? p + 1 : name,setval) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp,NODEFN,name) ; } else logOrPrint (LOG_INFO,fp,ADDMISSINGREAL,name,setval) ; } else if (v != NULL && v->type != realval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp,NOTREAL,name) ; } else if (v != NULL && low != -DBL_MAX && v->v.real_val < low) { logOrPrint (LOG_ERR,fp,REALTOLOW,name,v->v.real_val,low) ; /* bleh */ v->v.real_val = setval ; } else if (high != DBL_MAX && v->v.real_val > high) { logOrPrint (LOG_ERR,fp,REALTOHIGH,name,v->v.real_val,high) ; v->v.real_val = setval ; } return rval ; } static int validateBool (FILE *fp, const char *name, int required, bool setval, scope * sc, u_int inh) { int rval = VALUE_OK ; value *v ; scope *s ; char *p = strrchr (name,':') ; v = findValue (sc,name,inh) ; if (v == NULL && required != NOTREQNOADD) { s = findScope (sc,name,0) ; addBoolean (s,p ? p + 1 : name, setval ? 1 : 0) ; if (required == REQ) { rval = VALUE_MISSING ; logOrPrint (LOG_ERR,fp,NODEFN,name) ; } else logOrPrint (LOG_INFO,fp,ADDMISSINGBOOL,name,(setval?"true":"false")) ; } else if (v != NULL && v->type != boolval) { rval = VALUE_WRONG_TYPE ; logOrPrint (LOG_ERR,fp,NOTBOOLEAN,name) ; } return rval ; } void gCalcHostBlStat (void) { Host h ; for (h = gHostList ; h != NULL ; h = h->next) { h->blAccum += h->backlog ; if (h->backlog == 0) h->blNone++ ; else if (h->backlog >= hostHighwater) h->blFull++ ; else h->blQuartile[(4*h->backlog) / hostHighwater]++ ; h->blCount++ ; } } static void hostCleanup (void) { if (statusFile != NULL) FREE (statusFile) ; } innfeed-0.10.1.7.orig/host.h0100644000175100001440000001657106364204652013775 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 08:44:20 1995 * Project: INN (innfeed) * File: host.h * RCSId: $Id: host.h,v 1.5 1997/07/19 18:38:34 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The public interface to the Host class. * * The Host class represents the remote news system * that we're feeding. A Host object has possibly * multiple connections to the remote system which it * sends articles down. It is given the articles by * other objects (typically the InnListener), and once * taken it assumes all responsibility for transmission * or temporary storage on network failures etc. * */ #if ! defined ( host_h__ ) #define host_h__ #include #include "misc.h" /* * Functions used by the InnListener */ /* * Create a new Host object. * * NAME is the name that INN uses. * IPNAME is the name the networking code uses (or the ascii dotted quad * IP address). * ARTTIMEOUT is the max amount of time we'll wait for a new article * from INN before considering the connection unused and we'll close * down. * RESPTIMEOUT is the max amount of time we'll wait for any reponse * from a remote. Past this we'll close down the network connection. * INITIALCXNS is the number of Connections to create at Host creation time. * MAXCXNS is the maximum number of parallel connections to the * remote host we can run at any one time. * MAXCHECK is the maximum number of nntp CHECK commands to be outstanding * on a connection before opening up a new connection (or refusing * new articles if we get to MAXCXNS). * PORTNUM is the port number on the remote host we should talk to. * CLOSEPERIOD is the number of seconds after connecting that the * connections should be closed down and reinitialized (due to problems * with old NNTP servers that hold history files open. Value of 0 means * dont close down. * STREAMING is a boolean flag to tell if the Host wants its Connections to * do streaming or not. * LOWPASSHIGH is the high value for the low-pass filter. * LOWPASSLOW is the low value for the low-pass filter. */ void configHosts (bool talkSelf) ; /* print some debugging info. */ void gPrintHostInfo (FILE *fp, u_int indentAmt) ; void printHostInfo (Host host, FILE *fp, u_int indentAmt) ; /* Delete the host object. Drops all the active connections immediately (i.e. no QUIT) . */ void delHost (Host host) ; /* Get a new default host object */ Host newDefaultHost (InnListener listener, const char *name); /* gently close down all the host's connections (issue QUITs). */ void hostClose (Host host) ; /* gently close down all active connections (issue QUITs) and recreate them immediately */ void hostFlush (Host host) ; /* have the HOST transmit the ARTICLE, or, failing that, store article information for later attempts. */ void hostSendArticle (Host host, Article article) ; /* return an IP address for the host */ struct in_addr *hostIpAddr (Host host) ; /* * Functions used by the Connection to indicate Connection state. */ /* called by the Host's connection when the remote is refusing postings. Code 400 in the banner */ void hostCxnBlocked (Host host, Connection cxn, char *reason) ; /* called by the Connection when it has determined if the remote supports the streaming extension or not. */ void hostRemoteStreams (Host host, Connection cxn, bool doesStream) ; /* Called by the connection when it is no longer connected to the remote. Perhaps due to getting a code 400 to an IHAVE. */ void hostCxnDead (Host host, Connection cxn) ; /* Called when the Connection deletes itself */ bool hostCxnGone (Host host, Connection cxn) ; /* Called when the Connection goes to sleep. */ void hostCxnSleeping (Host host, Connection cxn) ; /* Called when the Connection starts waiting for articles. */ void hostCxnWaiting (Host host, Connection cxn) ; /* Called when the connection has sent an IHAVE or a CHECK, or a TAKETHIS when in no-check mode.*/ void hostArticleOffered (Host host, Connection cxn) ; /* called by the Connection when the article was transferred. */ void hostArticleAccepted (Host host, Connection cxn, Article article) ; /* Called by the connection when the remote answered 435 or 438 */ void hostArticleNotWanted (Host host, Connection cxn, Article article) ; /* Called by the connection when the remote answered 437 or 439 */ void hostArticleRejected (Host host, Connection cxn, Article article) ; /* Called when the connection when the remote answered 400 or 431 or 436 */ void hostArticleDeferred (Host host, Connection cxn, Article article) ; /* Called by the connection if it discovers the file is gone. */ void hostArticleIsMissing (Host host, Connection cxn, Article article) ; /* Called by the connection when it wants to defer articles, but it doesn't want the Host to queue any news on it. */ void hostTakeBackArticle (Host host, Connection cxn, Article article) ; /* called by the Connection when it is idle and wants to get things moving. Returns true if there was something to do and the Host called cxnQueueArticle() . */ bool hostGimmeArticle (Host host, Connection cxn) ; /* get the name that INN uses for this host */ const char *hostPeerName (Host host) ; /* if VAL is true then each time the host logs its stats all its connections will too. */ void hostLogConnectionStats (bool val) ; bool hostLogConnectionStatsP (void) ; #if 0 /* Set the frequency (in seconds) with which we log statistics */ void hostSetStatsPeriod (u_int period) ; #endif /* return whether or not the Connections should attempt to stream. */ bool hostWantsStreaming (Host host) ; /* return maxChecks */ u_int hostmaxChecks (Host host); /* return the maximum number of CHECKs that can be outstanding */ u_int hostMaxChecks (Host host) ; /* Called by the Host's connections when they go into (true) or out of (false) no-CHECK mode. */ void hostLogNoCheckMode (Host host, bool on, double low, double cur, double high) ; /* calculate host backlog statistics */ void gCalcHostBlStat (void) ; /* set the pathname of the file to use instead of innfeed.status */ void hostSetStatusFile (const char *filename) ; /* function called when config file is loaded. */ int hostConfigLoadCbk (void *data) ; #endif /* host_h__ */ void hostChkCxns(TimeoutId tid, void *data); innfeed-0.10.1.7.orig/inet_addr.c0100644000175100001440000001315406331417055014733 0ustar mdusers/* * ++Copyright++ 1983, 1990, 1993 * - * Copyright (c) 1983, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * - * --Copyright-- */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; static char rcsid[] = "$Id: inet_addr.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #if 0 #include "../conf/portability.h" #endif /* these are compatibility routines, not needed on recent BSD releases */ #if 0 /* not needed for innfeed. */ /* * Ascii internet address interpretation routine. * The value returned is in network order. */ u_long inet_addr(cp) register const char *cp; { struct in_addr val; if (inet_aton(cp, &val)) return (val.s_addr); return (INADDR_NONE); } #endif /* * Check whether "cp" is a valid ascii representation * of an Internet address and convert to a binary address. * Returns 1 if the address is valid, 0 if not. * This replaces inet_addr, the return value from which * cannot distinguish between failure and a local broadcast address. */ int inet_aton(cp, addr) register const char *cp; struct in_addr *addr; { register u_long val; register int base, n; register char c; u_int parts[4]; register u_int *pp = parts; c = *cp; for (;;) { /* * Collect number up to ``.''. * Values are specified as for C: * 0x=hex, 0=octal, isdigit=decimal. */ if (!isdigit(c)) return (0); val = 0; base = 10; if (c == '0') { c = *++cp; if (c == 'x' || c == 'X') base = 16, c = *++cp; else base = 8; } for (;;) { if (isascii(c) && isdigit(c)) { val = (val * base) + (c - '0'); c = *++cp; } else if (base == 16 && isascii(c) && isxdigit(c)) { val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A')); c = *++cp; } else break; } if (c == '.') { /* * Internet format: * a.b.c.d * a.b.c (with c treated as 16 bits) * a.b (with b treated as 24 bits) */ if (pp >= parts + 3) return (0); *pp++ = val; c = *++cp; } else break; } /* * Check for trailing characters. */ if (c != '\0' && (!isascii(c) || !isspace(c))) return (0); /* * Concoct the address according to * the number of parts specified. */ n = pp - parts + 1; switch (n) { case 0: return (0); /* initial nondigit */ case 1: /* a -- 32 bits */ break; case 2: /* a.b -- 8.24 bits */ if (val > 0xffffff) return (0); val |= parts[0] << 24; break; case 3: /* a.b.c -- 8.8.16 bits */ if (val > 0xffff) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: /* a.b.c.d -- 8.8.8.8 bits */ if (val > 0xff) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr) addr->s_addr = htonl(val); return (1); } innfeed-0.10.1.7.orig/innfeed.10100644000175100001440000003745306375220536014344 0ustar mdusers.\" -*- nroff -*- .\" .\" Author: James A. Brister -- berkeley-unix -- .\" Start Date: Sat, 20 Jan 1996 15:50:56 +1100 .\" Project: INN -- innfeed .\" File: innfeed.1 .\" RCSId: $Id: innfeed.1,v 1.5 1997/08/16 03:41:18 scrappy Exp $ .\" Description: Man page for innfeed(1) .\" .de t$ /var/news/spool/innfeed .. .de c$ innfeed.conf .. .de .TH INNFEED 1 .\" .\" .\" .\" .\" .SH NAME innfeed \- multi-host, multi-connection, streaming NNTP feeder. .SH SYNOPSIS .B innfeed [ .B \-a spool-dir ] [ .BI \-b " directory" ] [ .B \-C ] [ .BI \-c " filename" ] [ .BI \-d " num" ] [ .BI \-e " bytes" ] [ .B \-h ] [ .BI \-l " filename" ] [ .B \-m ] [ .B \-M ] [ .B \-o bytes ] [ .B \-p file ] [ .B \-S file ] [ .B \-x ] [ .B \-y ] [ .B \-z ] [ .B \-v ] [ file ] .\" .\" .\" .\" .\" .SH DESCRIPTION .PP This man page describes version 0.10 (beta) of innfeed. .PP .I Innfeed implements the NNTP protocol for transferring news between computers. It handles both the standard IHAVE protocol as well as the CHECK/TAKETHIS streaming extension. Innfeed can feed any number of remote hosts at once and will open multiple connections to each host if configured to do so. The only limitations are the process limits for open file descriptors and memory. .\" .\" .\" .\" .\" .SH MODES .PP .I Innfeed has three modes of operation: channel, funnel-file and batch. .PP Channel mode is used when no filename is given on the command line, the ``input-file'' keyword is \fInot\fP given in the config file, \fIand\fP the ``\fI\-x\fP'' option is \fInot\fP given. In channel mode innfeed runs with stdin connected via a pipe to innd. Whenever innd closes this pipe (and it has several reasons during normal processing to do so), innfeed will exit. It first will try to finish sending all articles it was in the middle of transmitting, before issuing a QUIT command. This means innfeed may take a while to exit depending on how slow your peers are. It never (well, almost never) just drops the connection. .PP Funnel-file mode is used when a filename is given as an argument or the ``input-file'' keyword is given in the config file. In funnel file mode it reads the specified file for the same formatted information as innd would give in channel mode. It is expected that innd is continually writing to this file, so when innfeed reaches the end of the file it will check periodically for new information. To prevent the funnel file from growing without bounds, you will need to periodically move the file to the side (or simply remove it) and have innd flush the file. Then, after the file is flushed by innd, you can send innfeed a SIGALRM, and it too will close the file and open the new file created by innd. Something like: .PP .RS .nf innfeed -p /var/tmp/innfeed.pid my-funnel-file & while true; do sleep 43200 rm -f my-funnel-file ctlinnd flush funnel-file-site kill -ALRM `cat /var/tmp/innfeed.pid` done .fi .RE .PP Batch mode is used when the ``\fI\-x\fP'' flag is used. In batch mode innfeed will ignore stdin, and will simply process any backlog created by a previously running innfeed. This mode is not normally needed as innfeed will take care of backlog processing. .\" .\" .\" .\" .\" .SH CONFIGURATION Innfeed expects a couple of things to be able to run correctly: a directory where it can store backlog files and a configuration file to describe which peers it should handle. .PP The configuration file is described in innfeed.conf(5). The ``\fI\-c\fP'' option can be used to specify a different file. .PP For each peer (say, ``\fIfoo\fP''), innfeed manages up to 4 files in the backlog directory: a ``\fIfoo.lock\fP'' file, which prevents other instances of innfeed from interfering with this one; a ``\fIfoo.input\fP'' file which has old article information innfeed is reading for re-processing; a ``\fPfoo.output\fP'' file where innfeed is writing information on articles that couldn't be processed (normally due to a slow or blocked peer); and a ``\fIfoo\fP'' file. .PP This last file (``\fIfoo\fP'') is never created by innfeed, but if innfeed notices it, it will rename it to ``\fIfoo.input\fP'' at the next opportunity and will start reading from it. This lets you create a batch file and put it in a place where innfeed will find it. You should never alter the .input or .output files of a running innfeed. .PP The format of these last three files is: .PP .RS .nf /path/to/article .fi .RE .PP This is the same as the first two fields of the lines innd feeds to innfeed, and the same as the first two fields of the lines of the batch file innd will write if innfeed is unavailable for some reason. When innfeed processes its own batch files it ignores everything after the first two whitespace separated fields, so moving the innd-created batch file to the appropriate spot will work, even though the lines are longer. .PP Innfeed writes its current status to the file ``\fIinnfeed.status\fP'' (or the file given by the ``\fI-S\fP'' option). This file contains details on the process as a whole, and on each peer this instance of innfeed is managing. .PP If innfeed is told to send an article to a host it is not managing, then the article information will be put into a file matching the pattern ``\fIinnfeed-dropped.*\fP'', with part of the file name matching the pid of the innfeed process that is writing to it. Innfeed will not process this file except to write to it. If nothing is written to the file then it will be removed if innfeed exits normally. .\" .\" .\" .\" .\" .SH SIGNALS .PP Upon receipt of a SIGALRM innfeed will close the funnel-file specified on the command line, and will reopen it (see funnel file description above). .PP Innfeed with catch SIGINT and will write a large debugging snapshot of the state of the running system. .PP Innfeed will catch SIGHUP and will reload the config file. See innfeed.conf(5) for more details. .PP Innfeed will catch SIGEMT and will close and reopen all backlog files. .PP Innfeed will catch SIGTERM and will do an orderly shutdown. .PP Upon receipt of a SIGUSR1 innfeed will increment the debugging level by one, receipt of a SIGUSR2 will decrement it by one. The debugging level starts at zero (unless the ``-d'' option it used), and no debugging information is emitted. A larger value for the level means more debugging information. Numbers up to 5 are currently useful. .\" .\" .\" .\" .\" .SH SYSLOG ENTRIES .PP There are 3 different categories of syslog entries for statistics. Host, Connection and Global. .PP The Host statistics are generated for a given peer at regular intervals after the first connection is made (or, if the remote is unreachable, after spooling starts). The Host statistics give totals over all Connections that have been active during the given time frame. For example (broken here to fit the page, with ``vixie'' being the peer): .PP .nf May 23 12:49:08 data innfeed[16015]: vixie checkpoint seconds 1381 offered 2744 accepted 1286 refused 1021 rejected 437 missing 0 spooled 990 on_close 0 unspooled 240 deferred 10 requeued 25 queue 42.1/100:14,35,13,4,24,10 .fi .PP These meanings of these fields are: .nr XX \w'unspooled ' .TP \n(XXu seconds The time since innfeed connected to the host or since the statistics were reset by a ``final'' log entry. .TP offered The number of IHAVE commands sent to the host if it is not in streaming mode. The sum of the number of TAKETHIS commands sent when no-CHECK mode is in effect plus the number CHECK commands sent in streaming mode (when no-CHECK mode is not in effect). .TP accepted The number of articles which were sent to the remote host and accepted by it. .TP refused The number of articles offered to the host that it it indicated it didn't want because it had already seen the Message-ID. The remote host indicates this by sending a 435 response to an IHAVE command or a 438 response to a CHECK command. .TP rejected The number of articles transferred to the host that it did not accept because it determined either that it already had the article or it did not want it because of the article's Newsgroups: or Distribution: headers, etc. The remote host indicates that it is rejecting the article by sending a 437 or 439 response after innfeed sent the entire article. .TP missing The number of articles which innfeed was told to offer to the host but which were not present in the article spool. These articles were probably cancelled or expired before innfeed was able to offer them to the host. .TP spooled The number of article entries that were written to the .output backlog file because the articles could not either be sent to the host or be refused by it. Articles are generally spooled either because new articles are arriving more quickly than they can be offered to the host, or because innfeed closed all the connections to the host and pushed all the articles currently in progress to the .output backlog file. .TP on_close The number of articles that were spooled when innfeed closed all the connections to the host. .TP unspooled The number of article entries that were read from the .input backlog file. .TP deferred The number of articles that the host told innfeed to retry later by sending a 431 or 436 response. Innfeed immediately puts these articles back on the tail of the queue. .TP requeued The number of articles that were in progress on connections when innfeed dropped those connections and put the articles back on the queue. These connections may have been broken by a network problem or became unresponsive causing innfeed to time them out. .TP queue The first number is the average (mean) queue size during the previous logging interval. The second number is the maximum allowable queue size. The third number is the percentage of the time that the queue was empty. The fourth through seventh numbers are the percentages of the time that the queue was >0% to 25% full, 25% to 50% full, 50% to 75% full, and 75% to <100% full. The last number is the percentage of the time that the queue was totally full. .PP If the ``\fI\-z\fP'' option is used (see below), then when the peer stats are generated, each Connection will log its stats too. For example, for connection number zero (from a set of five): .PP .nf May 23 12:49:08 data innfeed[16015]: vixie:0 checkpoint seconds 1381 offered 596 accepted 274 refused 225 rejected 97 .fi .PP If you only open a maximum of one Connection to a remote, then there will be a close correlation between Connection numbers and Host numbers, but in general you can't tie the two sets of number together in any easy or very meaningful way. When a Connection closes it will always log its stats. .PP If all Connections for a Host get closed together, then the Host logs its stats as ``final'' and resets its counters. If the feed is so busy that there's always at least one Connection open and running, then after some amount of time (set via the config file), the Host stats are logged as final and reset. This is to make generating higher level stats from log files, by other programs, easier. .PP There is one log entry that is emitted for a Host just after its last Connection closes and innfeed is preparing to exit. This entry contains counts over the entire life of the process. The ``seconds'' field is from the first time a Connection was successfully built, or the first time spooling started. If a Host has been completely idle, it will have no such log entry. .PP .nf May 23 12:49:08 data innfeed[16015]: decwrl global seconds 1381 offered 34 accepted 22 refused 3 rejected 7 missing 0 .fi .PP The final log entry is emitted immediately before exiting. It contains a summary of the statistics over the entire life of the process. .PP .nf Feb 13 14:43:41 data innfeed-0.9.4[22344]: ME global seconds 15742 offered 273441 accepted 45750 refused 222008 rejected 3334 missing 217 .fi .PP .\" .\" .\" .\" .\" .SH OPTIONS .TP .B \-a The ``\fI\-a\fP'' flag is used to specify the top of the article spool tree. Innfeed does a chdir(2) to this directory, so it should probably be an absolute path. .TP .B \-b The ``\fI\-b\fP'' flag may be used to specify a different directory for backlog file storage and retrieval. The default is normally .t$ .TP .B \-c The ``\fI\-c\fP'' flag may be used to specify a different config file from the default value. If the path is relative then it is relative to the backlog directory. The default is .c$ .TP .B \-C The ``\fI\-C\fP'' flag is used to have innfeed simply check the config file, report on any errors and then exit. .TP .B \-d The ``\fI\-d\fP'' flag may be used to specify the initial logging level. All debugging messages to to stderr (see the ``\fI\-l\fP'' flag below. .TP .B \-e The ``\fI\-e\fP'' flag may be used to specify the size limit (in bytes) for the \fI.output\fP backlog files innfeed creates. If the output file gets bigger than 10% more than the given number, innfeed will replace the output file with the tail of the original version. The default value is 0, which means there is no limit. .TP .B \-h Use the ``\fI\-h\fP'' flag to print the usage message. .TP .B \-l The ``\fI\-l\fP'' flag may be used to specify a different log file from stderr. As innd starts innfeed with stderr attached to /dev/null using this option can be useful in catching any abnormal error messages, or andy debugging messages (all ``normal'' errors messages go to syslog). .TP .B \-M If innfeed has been built with mmap support, then the ``\fI\-M\fP'' flag turns OFF the use of mmap(), otherwise it has no effect. .TP .B \-m The ``\fI\-m\fP'' flag is used to turn on logging of all missing articles. Normally if an article is missing, innfeed keeps a count, but logs no further information. When this flag is used, details about message-id and expected pathname are logged. .TP .B \-o The ``\fI\-o\fP'' flag sets a value of the maximum number of bytes of article data innfeed is supposed to keep in memory. This doesn't work properly yet. .TP .B \-p The ``\fI\-p\fP'' flag is used to specify the filename to write the pid of the process into. A relative path is relative to the backlog directory. The default is ``\fIinnfeed.pid\fP''. .TP .B \-S The ``\fI\-S\fP'' flag specifies the name of the file to write the periodic staus to. If the path is relative it is considered relative to the backlog directory. The default is ``\fIinnfeed.status\fP''. .TP .B \-v When the ``\fI\-v\fP'' flag is given, version information is printed to stderr and then innfeed exits. .TP .B \-x The ``\fI\-x\fP'' flag is used to tell innfeed not to expect any article information from innd but just to process any backlog files that exist and then exit. .TP .B \-y The ``\fI\-y\fP'' flag is used to allow dynamic peer binding. If this flag is used and article information is received from innd that specifies an unknown peer, then the peer name is taken to be the IP name too, and an association with it is created. Using this it is possible to only have the global defaults in the innfeed.conf(5) file, provided the peername as used by innd is the same as the ip name. .TP .B \-z The ``\fI\-z\fP'' flag is used to cause each connection, in a parallel feed configuration, to report statistics when the controller for the connections prints its statistics. .TP .\" .\" .\" .\" .\" .SH BUGS .PP When using the ``-x'' option, the config file entry's ``initial-connections'' field will be the total number of connections created and used--no matter how many big the batch file, and no matter how big the ``max-connectiond'' field specifies. Thus a value of 0 for ``initial-connections'', means nothing will happen in ``-x'' mode. .PP Innfeed does not automatically grab the file out of out.going--this needs to be prepared for it by external means. .PP Probably too many other bugs to count. .\" .\" .\" .\" .\" .SH FILES .c$ config file. .br .t$ directory for backlog files. .\" .\" .\" .\" .\" .SH HISTORY Written by James Brister for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: innfeed.1,v 1.5 1997/08/16 03:41:18 scrappy Exp $ .SH SEE ALSO .IR innfeed.conf(5) innfeed-0.10.1.7.orig/innfeed.conf0100644000175100001440000000573106365762373015134 0ustar mdusers# $Revision: 1.2 $ # # Sample innfeed config file. See the comment block at the # end for a fuller description of the format, and innfeed.conf(5) for a # description of the entries. # ## ## Global values. Not specific to any peer. These ## are optional, but if used will override the ## compiled in values. ## news-spool: /var/news/spool/articles pid-file: innfeed.pid debug-level: 0 use-mmap: false log-file: innfeed.log stdio-fdmax: 0 ## Uncomment the next line to include the contents ## of ``testfile'' at this point. #$INCLUDE testfile backlog-directory: /var/news/spool/innfeed backlog-rotate-period: 60 backlog-ckpt-period: 30 backlog-newfile-period: 600 dns-retry: 900 dns-expire: 86400 close-period: 3600 gen-html: false status-file: innfeed.status connection-stats: false host-queue-highwater: 200 stats-period: 600 stats-reset: 43200 max-reconnect-time: 3600 initial-reconnect-time: 30 ## ## Defaults for all peers. These must all exist at ## global scope. Any of them can be redefined ## inside a peer or group definition. ## article-timeout: 600 response-timeout: 300 initial-connections: 1 max-connections: 5 max-queue-size: 25 streaming: true no-check-high: 95.0 no-check-low: 90.0 no-check-filter: 50.0 port-number: 119 backlog-limit: 0 backlog-factor: 1.10 backlog-limit-highwater: 0 dynamic-method: 3 dynamic-backlog-filter: 0.7 dynamic-backlog-low: 25.0 dynamic-backlog-high: 50.0 no-backlog: false ## ## Peers. ## peer decwrl { ip-name: news1.pa.dec.com } peer uunet { ip-name: news.uunet.uu.net max-connections: 10 } ## ## Group peers together to give second level defaults. ## group fast-sites { max-connections: 7 peer data.ramona.vix.com { # ip-name defaults to data.ramona.vix.com streaming: false } peer bb.home.vix.com { ip-name: 192.5.5.33 } } # Blank lines are ignored. Exerything after a '#' # is ignored too. # # Format is: # key : value # # See innfeed.conf(5) for a description of # necessary & useful keys. Unknown keys and their # values are ignored. # # Values may be a integer, floating-point, c-style # single-quoted characters, boolean, and strings. # # If a string value contains whitespace, or # embedded quotes, or the comment character # (``#''), then the whole string must be quoted # with double quotes. Inside the quotes, you may # use the standard c-escape sequence # (\t,\n,\r,\f,\v,\",\'). # # Examples: # eg-string: "New\tConfig\tfile\n" # eg-long-string: "A long string that goes # over multiple lines. The # newline is kept in the # string except when quoted # with a backslash \ # as here." # eg-simple-string: A-no-quote-string # eg-integer: 10 # eg-boolean: true # eg-char: 'a' # eg-ctrl-g: '\007' innfeed-0.10.1.7.orig/innfeed.conf.50100644000175100001440000006135206365762354015277 0ustar mdusers.\" -*- nroff -*- .\" .\" Author: James A. Brister -- berkeley-unix -- .\" Start Date: Sun, 21 Jan 1996 00:47:37 +1100 .\" Project: INN -- innfeed .\" File: innfeed.conf.5 .\" RCSId: $Id: innfeed.conf.5,v 1.4 1997/07/24 23:27:40 scrappy Exp $ .\" Description: Man page for innfeed.conf(5) .\" .TH innfeed.conf 5 .SH NAME innfeed.conf \- configuration file for innfeed .SH DESCRIPTION .PP This man page describes the configuration file for version 0.10 (beta) of innfeed. This format has changed dramatically since version 0.9.3. .PP The file .B innfeed.conf is used to control the innfeed(1) program. It is a fairly free-format file that consists of three types of entries: \fIkey/value\fP, \fIpeer\fP and \fIgroup\fP. Comments are taken from the hash character ``#'' to the end of the line. .PP \fIKey/value\fP entries are a keyword and a value separated by a colon (which can itself be surrounded by whitespace). For example: .PP .RS .nf max-connections: 10 .fi .RE .PP A legal key starts with a letter and contains only letters, numbers and ``_'', ``-''. .LP There are 5 different type of values: integers, floating-point numbers, characters, booleans, and strings. Integer and floating point numbers are as to be expected except that exponents in floating point numbers are not supported. A boolean value is either ``true'' or ``false'' (case is not significant). A character value is a single-quoted character as defined by the C-language. A string value is any other sequence of characters. If the string needs to contain whitespace, then it must be quoted with double quotes, and uses the same format for embedding non-printing characters as normal C-language string. .PP Peer entries look like: .PP .RS .nf peer { # body ... } .fi .RE .PP The word ``peer'' is required. The ``'' is the same as the site name in INN's newsfeeds file. The body of a peer entry contains some number (possibly zero) of key/value entries. .PP Group entries look like: .PP .RS .nf group { # body } .fi .RE .PP The word ``group'' is required. The ``'' is any string valid as a key. The body of a group entry contains any number of the three types of entries. So key/value pairs can be defined inside a group, and peers can be nested inside a group, and other groups can be nested inside a group. .PP Key/value entries that are defined outside of all peer and group entries are said to be at ``global scope''. There are global key/value entries that apply to the process as a whole (for example the location of the backlog file directory), and there are global key/value entries that act as defaults for peers. When innfeed looks for a specific value in a peer entry (for example, the maximum number of connections to set up), if the value is not defined in the peer entry, then the enclosing groups are examined for the entry (starting at the closest enclosing group). If there are no enclosing groups, or the enclosing groups don't define the key/value, then the value at global scope is used. .PP A small example could be: .PP .RS .nf # Global value for the process news-spool: /var/news/spool/articles # Global value applied to all peers that have # no value of their own. max-connections: 5 # A peer definition. ``uunet'' is the name used by innd in # the newsfeeds file. peer uunet { ip-name: usenet1.uu.net } peer vixie { ip-name: gw.home.vix.com max-connections: 10 # override global value. } # A group of two peers who can handle more connections # than normal group fast-sites { max-connections: 15 # Another peer. The ``max-connections'' value from the # ``fast-sites'' group scope is used. The ``ip-name'' value # defaults to the peer's name. peer data.ramona.vix.com { } peer bb.home.vix.com { max-connections: 20 # he can really cook. } } .fi .RE .PP Given the above configuration file, the defined peers would have the following values for the ``max-connections'' key. .PP .RS .nf uunet 5 vixie 10 data.ramona.vix.com 15 bb.home.vix.com 20 .fi .RE .PP Innfeed ignores key/value pairs it is not interested in. Any config file value that can be set via a command line option, is not used if the command-line option is given. .PP Config files can be included in other config files via the syntax: .nf .RS $INCLUDE filename .RE .FI There is a maximum nesting depth of 10. .PP For a fuller example config file, see the supplied \fIinnfeed.conf\fP. .SH "GLOBAL VALUES" .PP The following listing show all the keys that apply to the process as whole. These are not required (compiled-in defaults are used where needed). .TP .B news-spool This key requires a pathname value. It specifies where the top of the article spool is. This corresponds to the ``\fI\-a\fP'' command-line option. .TP .B input-file This key requires a pathname value. It specifies the pathname (relative to the \fBbacklog-directory\fP) that should be read in funnel-file mode. This corresponds to giving a filename as an argument on the command-line (i.e. its presence also implies that funnel-file mode should be used). .TP .B pid-file This key requires a pathname value. It specifies the pathname (relative to the \fBbacklog-directory\fP) where the pid of the innfeed process should be stored. This corresponds to the ``\fI\-p\fP'' command-line option. .TP .B debug-level This key defines the debug level for the process. A non-zero number generates a lot of messages to stderr, or to the config-defined ``log-file''. This corresponsds to the ``\fI\-d\fP'' command-line option. .TP .B use-mmap This key requires a boolean value. It specifies whether mmap'ing should be used if innfeed has been built with mmap support. If article data on disk is not in NNTP-ready format (CR/LF at the end of each line), then after mmap'ing the article is read into memory and fixed up, so mmap'ing has no positive effect (and possibly some negative effect depending on your system), and so in such a case this value should be \fIfalse\fP. This corresponds to the ``\fI\-M\fP'' command-line option. .TP .B log-file This key requires a pathname value. It specifies where any logging messages that couldn't be sent via syslog(3) should go (such as those generated when a positive value for ``\fBdebug-value\fP'', is used). This corresponds to the ``\fI\-l\fP'' command-line option. A relative pathname is relative to the ``\fBbacklog-directory\fP'' value. .\" .TP .\" .B initial-sleep .\" This key requires a postive integer value. It specifies how many seconds .\" innfeed should sleep at startup before attempting to take out its locks. On .\" fast machines and with innfeed handling many connections, it can take too .\" long for innfeed to recognise that its input has been closed, and that it .\" should release any locks it holds. .\".................................................. .TP .B backlog-directory This key requires a pathname value. It specifies where the current innfeed process should store backlog files. This corresponds to the ``\fI\-b\fP'' command-line option. .TP .B backlog-highwater This key requires a positive integer value. It specifies how many articles should be kept on the backlog file queue before starting to write new entries to disk. .TP .B backlog-ckpt-period This key requires a positive integer value. It specifies how many seconds between checkpoints of the input backlog file. To small a number will mean frequent disk accesses, to large a number will mean after a crash innfeed will re-offer more already-processed articles than necessary. .TP .B backlog-newfile-period This key requires a positive integer value. It specifies how many seconds between checks for externally generated backlog files that are to be picked up and processed. .\".................................................. .TP .B dns-retry The key requires a positive integer value. It defines the number of seconds between attempts to re-lookup host information that previous failed to be resolved. .TP .B dns-expire The key requires a positive integer value. It defines the number of seconds between refreshes of name to address DNS translation. This is so long running processes don't get stuck with stale data, should peer ip addresses change.. .TP .B close-period The key requires a positive integer value. It is the maximum number of seconds a connection should be kept open. Some NNTP servers don't deal well with connections being held open for long periods. .TP .B gen-html This key requires a boolean value. It specifies whether the \fBstatus-file\fP should be HTML-ified. .TP .B status-file This key requires a pathname value. It specifies the pathname (relative to the \fBbacklog-directory\fP) where the periodic status of the innfeed process should be stored. This corresponds to the ``\fI\-S\fP'' command-line option. .TP .B connection-stats This key requires a boolean value. If the value is true, then whenever the transmission statistics for a peer are logged, then each active connection logs its own statistics. This corresponds to the ``\fI\-z\fP'' command-line option. .TP .B host-queue-highwater This key requires a postive integer value. It defines how many articles will be held internally for a peer before new arrivals cause article information to be spooled to the backlog file. .TP .B stats-period This key requires a positive integer value. It defines how many seconds innfeed waits between generating statistics on transfer rates. .TP .B stats-reset This key requires a positive integer value. It defines how many seconds innfeed waits before resetting all internal transfer counters back to zero (after logging one final time). This is so a innfeed-process running more than a day will generate ``final'' stats that will be picked up by logfile processing scripts. .\".................................................. .TP .B initial-reconnect-time This key requires a postive integer value. It defines how many seconds to first wait before retrying to reconnect after a connection failure. If the next attempt fails too, then the reconnect time is approximately doubled until the connection succeeds, or \fBmax-reconnection-time\fP is reached. .TP .B max-reconnect-time This key requires an integer value. It defines the maximum number of seconds to wait between attempt to reconnect to a peer. The initial value for reconnection attempts is defined by \fBinitial-reconnect-time\fP, and it is doubled after each failure, up to this value. .TP .B stdio-fdmax This key requires a non-negative integer value. If the value is greater than zero, then whenever a network socket file dcescriptor is created and it has a value \fIless than\fP this, the file descriptor will be dup'ed to bring the value up greater than this. This is to leave lower numbered file descriptors free for stdio. Certain systems, Sun's in particular, require this. SunOS 4.1.x usually requires a value of 128 and Solaris requires a value of 256. The default if this is not specified, is 0. .\".................................................. .SH "GLOBAL PEER DEFAULTS" .PP All the key/value pairs mentioned in this section must be specified at global scope. They may also be specified inside a group or peer definition. Note that when peers are added dynamically (i.e. when innfeed receives an article for an unspecified peer), it will add the peer site using the parameters specified at global scope. .TP .B article-timeout This key requires a non-negative integer value. If no articles need to be sent to the peer for this many seconds, then the peer is considered idle and all its active connections are torn down. .TP .B response-timeout This key requires a non-negative integer value. It defines the maximum amount of time to wait for a response from the peer after issuing a command. .TP .B initial-connections This key requires a non-negative integer value. It defines the number of connections to be opened immediately when setting up a peer binding. A value of 0 means no connections will be created until an article needs to be sent. .TP .B max-connections This key requires positive integer value. It defines the maximum number of connections to run in parallel to the peer. A value of zero specifies an unlimited number of maximum connections. In general use of an unlimited number of maximum connections is not recommended. Do not ever set \fBmax-connections\fP to zero with \fBdynamic-method\fP 0 set, as this will saturate peer hosts with connections. [ Note that in previous versions of innfeed, a value of 1 had a special meaning. This is no longer the case, 1 means a maximum of 1 connection ]. .TP .B dynamic-method This key requires a value between 0 and 3. It controls how connections (up to max-connections) are opened up to the maximum specified by \fBmax-connections\fP. In general (and specifically, with \fBdynamic-method\fP 0) a new connection is opened when the current number of connections is below \fBmax-connections\fP, and an article is to be sent whilst no current connections are idle. Without further restraint (i.e. using \fBdynamic-method\fP 0), in practice this means that \fBmax-connections\fP connections are established whilst articles are being sent. Use of other \fBdynamic-method\fP settings imposes a further limit on the amount of connections opened below that specified by \fBmax-connections\fP. This limit is calculated in different ways, depending of the value of \fBdynamic-method\fP. Users should note that adding additional connections is not always productive - just because opening twice as many connections results in a small percentage increase of articles accepted by the remote peer, this may be at considerable resource cost both locally and at the remote site, whereas the remote site might well have received the extra articles sent from another peer a fraction of a second later. Opening large numbers of connections is considered antisocial. The meanings of the various settings are: .RS .TP .B 0 no method Increase of connections up to \fBmax-connections\fP is unrestrained. .TP .B 1 maximize articles per second Connections are increased (up to \fBmax-connections\fP) and decreased so as to maximize the number of articles per second sent, whilst using the fewest connections to do this. .TP .B 2 set target queue length Connections are increased (up to \fBmax-connections\fP) and decreased so as to keep the queue of articles to be sent within the bounds set by \fBdynamic-backlog-low\fP and \fBdynamic-backlog-high\fP, whilst using the minimum resource possible. As the queue will tend to fill if the site is not keeping up, this method ensures that the maximum number of articles are offered to the peer whilst using the minimum number of connections to achieve this. .TP .B 3 combination This method uses a combination of methods 1 and 2 above. For sites accepting a large percentage of articles, method 2 will be used to ensure these sites are offered as complete a feed as possible. For sites accepting a small percentage of articles, method 1 is used, to minimize remote resource usage. For intermediate sites, an appropriate combination is used. .RE .TP .B dynamic-backlog-low This key takes a value between 0 and 100 and represents (as a percentage) the low water mark for the host queue. When the host queue falls below this level, when using \fBdynamic-method\fP 2 or 3, if 2 or more connections are open, innfeed will attempt to drop connections to the host. An IIR filter is applied to the value to prevent connection flap (see \fBdynamic-filter\fP). A value of 25.0 is recommended. This value must be smaller than \fBdynamic-backlog-high\fP. .TP .B dynamic-backlog-high This key takes a value between 0 and 100 and represents (as a percentage) the high water mark for the host queue. When the host queue rises above this level, when using \fBdynamic-method\fP 2 or 3, if less than \fBmax-connections\fP are open to the host, innfeed will attempt to open further connections to the host. An IIR filter is applied to the value to prevent connection flap (see \fBdynamic-filter\fP). A value of 50.0 is recommended. This value must be larger than \fBdynamic-backlog-low\fP. .TP .B dynamic-backlog-filter This key takes a floating-point value between 0 and 1 which represents the filter coefficient used by the IIR filter used to implement \fBdynamic-method\fP 2 and 3. The recommended value of this filter is 0.7, giving a time constant of 1/(1-0.7) articles. Higher values will result in slower response to queue fullness changes, lower values with faster response. .TP .B max-queue-size This key requires a positive integer value. It defines the maximum number of articles to process at one time when using streaming to transmit to a peer. Larger numbers mean more memory consumed as articles usually get pulled into memory (see the description of \fBuse-mmap\fP). .TP .B streaming This key requires a boolean value. It defines whether streaming commands are used to transmit articles to the peers. .TP .B no-check-high This key requires a floating-point number which must be in the range [0.0, 100.0]. When running transmitting with the streaming commands, innfeed attempts an optimization called ``no-CHECK'' mode. This involves \fInot\fP asking the peer if it wants the article, but just sending it. This optimization occurs when the percentage of the articles the peer has accepted gets larger than this number. If this value is set to 100.0, then this effectivly turns off no-CHECK mode, as the percentage can never get above 100.0. If this value is too small, then the number of articles the peer rejects will get bigger (and your bandwidth will be wasted). A value of 95.0 is usually pretty good. NOTE: In innfeed 0.9.3 and earlier this value was in the range [0.0, 9.0]. .TP .B no-check-low: This key requires a floating-point number which must be in the range [0.0, 100.0), and it must be smaller that the value for \fBno-check-high\fP. When running in no-CHECK mode, as described above, if the percentage of articles the remote accepts drops below this number, then the no-CHECK optimization is turned off until the percentage gets above the \fBno-check-high\fP value again. If there is small difference between this and the \fBno-check-high\fP value (less than about 5.0), then innfeed may frequently go in and out of no-CHECK mode. If the difference is too big, then it will make it harder to get out of no-CHECK mode when necessary (wasting bandwidth). Keeping this to between 5.0 and 10.0 less than \fBno-check-high\fP is usually pretty good. .TP .B no-check-filter This is a floating point value representing the time constant, in articles, over which the CHECK / no-CHECK calculations are done. The recommended value is 50.0 which will implement an IIR filter of time constant 50. This roughly equates to making a decision about the mode over the previous 50 articles. A higher number will result in a slower response to changing percentages of articles accepted; a lower number will result in a faster response. .TP .B port-number This key requires a postive integer value. It defines the tcp/ip port number to use when connecting to the remote. .TP .B no-backlog This key requires a boolean value. It specifies whether spooling should be enabled (false, the default) or disabled (true). Note that when no-backlog is set, articles reported as "spooled" are actually silently discarded. .TP .B backlog-limit This key requires a non-negative integer value. If the number is 0 then backlog files are allowed to grown without bound when the peer is unable to keep up with the article flow. If this number is greater than 0 then it specifies the size (in bytes) the backlog file should get truncated to when the backlog file reaches a certain limit. The limit depends on whether \fBbacklog-factor\fP or \fBbacklog-limit-high\fP is used. .TP .B backlog-factor This key requires a floating point value, which must be larger than 1.0. It is used in conjunction with the peer key \fBbacklog-limit\fP. If \fBbacklog-limit\fP has a value greater than zero, then when the backlog file gets larger than the value \fBbacklog-limit * backlog-factor\fP, then the backlog file will be truncated to the size \fBbacklog-limit\fP. For example if \fBbacklog-limit\fP has a value of 1000000, and \fBbacklog-factor\fP has a value of 2.0, then when the backlogfile gets to be larger than 2000000 bytes in size, it will be truncated to 1000000 bytes. The front portion of the file is removed, and the triming happens on line boundaries, so the final size may be a bit less than this number. If \fBbacklog-limit-highwater\fP is defined too, then \fBbacklog-factor\fP takes precedence. .TP .B backlog-limit-highwater This key requires a positive integer value that must be larger than the value for \fBbacklog-limit\fP. If the size of the backlog file gets larger than this value (in bytes), then the backlog file will be dhrunk down to the size of \fBbacklog-limit\fP. If both \fBbacklog-factor\fP and \fBbacklog-limit-highwater\fP are defined, then the value of \fBbacklog-factor\fP is used. .\".................................................. .SH "PEER VALUES" As previously explained, the peer definitions can contain redefinitions of any of the key/value pairs described in the \fBGLOBAL PEER DEFAULTS\fP section above. There is one key/value pair that is specific to a peer definition. .TP .B ip-name This key requires a word value. The word is the host's FQDN, or the dotted quad ip-address. If this value is not specified then the name of the peer is taken to also be its ip-name. See the entry for data.ramona.vix.com in the example below. .\".................................................. .SH RELOADING .PP If innfeed gets a SIGHUP signal, then it will reread the config file. All values at global scope except for ``\fBbacklog-directory\fP'' can be changed. Any new peers are added and any missing peers have their connections closed. .\".................................................. .SH EXAMPLE .PP Below is the sample innfeed.conf file. .RS .nf # # innfeed.conf file. See the comment block at the # end for a fuller description. # ## ## Global values. Not specific to any peer. These ## are optional, but if used will override the ## compiled in values. Command-line options used ## will override these values. ## news-spool: /var/news/spool/articles pid-file: innfeed.pid debug-level: 0 use-mmap: false log-file: innfeed.log stdio-fdmax: 0 backlog-directory: /var/news/spool/innfeed backlog-rotate-period: 60 backlog-ckpt-period: 30 backlog-newfile-period: 600 dns-retry: 900 dns-expire: 86400 close-period: 3600 gen-html: false status-file: innfeed.status connection-stats: false host-queue-highwater: 200 stats-period: 600 stats-reset: 43200 max-reconnect-time: 3600 initial-reconnect-time: 30 ## ## Defaults for all peers. These must all exist at ## global scope. Any of them can be redefined ## inside a peer or group definition. ## article-timeout: 600 response-timeout: 300 initial-connections: 1 max-connections: 5 max-queue-size: 25 streaming: true no-check-high: 95.0 no-check-low: 90.0 no-check-filter: 50.0 port-number: 119 backlog-limit: 0 backlog-factor: 1.10 backlog-limit-highwater:0 dynamic-method: 3 dynamic-backlog-filter: 0.7 dynamic-backlog-low: 25.0 dynamic-backlog-high: 50.0 no-backlog: false ## ## Peers. ## peer decwrl { ip-name: news1.pa.dec.com } peer uunet { ip-name: news.uunet.uu.net max-connections: 10 } peer data.ramona.vix.com { # ip-name defaults to data.ramona.vix.com streaming: false } peer bb.home.vix.com { ip-name: 192.5.5.33 } # Blank lines are ignored. Exerything after a '#' # is ignored too. # # Format is: # key : value # # See innfeed.conf(5) for a description of # necessary & useful keys. Unknown keys and their # values are ignored. # # Values may be a integer, floating-point, c-style # single-quoted characters, boolean, and strings. # # If a string value contains whitespace, or # embedded quotes, or the comment character # (``#''), then the whole string must be quoted # with double quotes. Inside the quotes, you may # use the standard c-escape sequence # (\t,\n,\r,\f,\v,\",\'). # # Examples: # eg-string: "New\tConfig\tfile\n" # eg-long-string: "A long string that goes # over multiple lines. The # newline is kept in the # string except when quoted # with a backslash \ # as here." # eg-simple-string: A-no-quote-string # eg-integer: 10 # eg-boolean: true # eg-char: 'a' # eg-ctrl-g: '\007' .fi .RE .SH HISTORY Written by James Brister for InterNetNews. .de R$ This is revision \\$3, dated \\$4. .. .R$ $Id: innfeed.conf.5,v 1.4 1997/07/24 23:27:40 scrappy Exp $ .SH SEE ALSO innfeed(1), newsfeeds(5) innfeed-0.10.1.7.orig/innlistener.c0100644000175100001440000004615606364204657015354 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Thu Dec 28 13:15:04 1995 * Project: INN (innfeed) * File: innlistener.c * RCSId: $Id: innlistener.c,v 1.4 1997/07/19 18:38:39 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Implementation of the InnListener class. * */ #if ! defined (lint) static const char *rcsid = "$Id: innlistener.c,v 1.4 1997/07/19 18:38:39 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #include #include #include #include #include #include #include #include "innlistener.h" #include "endpoint.h" #include "host.h" #include "buffer.h" #include "article.h" #include "msgs.h" #include "tape.h" #include "configfile.h" #define LISTENER_INPUT_BUFFER (1024 * 8) /* byte size of the input buffer */ #define EOF_SLEEP_TIME 1 /* seconds to sleep when EOF on InputFile */ struct innlistener_s { EndPoint myep ; Host *myHosts ; size_t hostLen ; Buffer inputBuffer ; bool dummyListener ; bool dynamicPeers ; TimeoutId inputEOFSleepId ; InnListener next ; }; static u_int listenerCount = 0 ; static InnListener listenerList = NULL ; InnListener mainListener ; static FILE *droppedFp = NULL ; static long droppedCount = 0 ; static int droppedFileCount = 0 ; static char *dropArtFile = NULL ; static bool fastExit = false ; extern const char *pidFile ; extern const char *InputFile ; extern bool RollInputFile ; static void giveArticleToPeer (InnListener lis, Article article, const char *peerName) ; static void newArticleCommand (EndPoint ep, IoStatus i, Buffer *buffs, void *data) ; static void wakeUp (TimeoutId id, void *data) ; static void writeCheckPoint (int offsetAdjust) ; static void dropArticle (const char *peer, Article article) ; static void listenerCleanup (void) ; static bool inited = false ; void listenerLogStatus (FILE *fp) { fprintf (fp,"Listener Status:\n") ; fprintf (fp," Dropped article file: %s\n",dropArtFile) ; fprintf (fp," Dropped article count: %ld\n",(long) droppedCount) ; fprintf (fp,"\n") ; } InnListener newListener (EndPoint endp, bool isDummy, bool dynamicPeers) { InnListener l = CALLOC (struct innlistener_s, 1) ; Buffer *readArray ; if (!inited) { inited = true ; atexit (listenerCleanup) ; } ASSERT (l != NULL) ; l->myep = endp ; l->hostLen = MAX_HOSTS ; l->myHosts = CALLOC (Host, l->hostLen) ; ASSERT (l->myHosts != NULL) ; l->inputBuffer = newBuffer (LISTENER_INPUT_BUFFER) ; l->dummyListener = isDummy ; l->dynamicPeers = dynamicPeers ; addPointerFreedOnExit ((char *)bufferBase(l->inputBuffer)) ; addPointerFreedOnExit ((char *)l->myHosts) ; addPointerFreedOnExit ((char *)l) ; readArray = makeBufferArray (bufferTakeRef (l->inputBuffer), NULL) ; prepareRead (endp,readArray,newArticleCommand,l,1) ; l->next = listenerList ; listenerList = l ; listenerCount++ ; return l ; } void gPrintListenerInfo (FILE *fp, u_int indentAmt) { InnListener p ; char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal InnListener list : %p (count %d) {\n", indent,listenerList,listenerCount) ; for (p = listenerList ; p != NULL ; p = p->next) printListenerInfo (p,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void printListenerInfo (InnListener listener, FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sInnListener : %p {\n",indent,listener) ; fprintf (fp,"%s endpoint : %p\n", indent, listener->myep) ; fprintf (fp,"%s dummy-listener : %s\n",indent, boolToString (listener->dummyListener)) ; fprintf (fp,"%s dynamicPeers : %s\n",indent, boolToString (listener->dynamicPeers)) ; fprintf (fp,"%s input-buffer {\n",indent) ; printBufferInfo (listener->inputBuffer,fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s hosts {\n",indent) ; for (i = 0 ; i < listener->hostLen ; i++) { #if 0 if (listener->myHosts [i] != NULL) printHostInfo (listener->myHosts [i],fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,listener->myHosts[i]) ; #endif } fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s}\n",indent) ; } /* Close down all hosts on this listener. When they're all gone the Listener will be deleted. */ void shutDown (InnListener l) { u_int i ; u_int count ; dprintf (1,"Shutting down the listener\n") ; closeDroppedArticleFile () ; unlink (pidFile) ; if (l->myep != NULL) { if (l->inputEOFSleepId != 0) removeTimeout (l->inputEOFSleepId) ; l->inputEOFSleepId = 0 ; delEndPoint (l->myep) ; } l->myep = NULL ; for (i = 0, count = 0 ; i < l->hostLen ; i++) if (l->myHosts [i] != NULL) { hostClose (l->myHosts[i]) ; count++ ; } if (count == 0 || fastExit) { time_t now = theTime () ; char dateString [30] ; strcpy (dateString,ctime (&now)) ; dateString [24] = '\0' ; if (fastExit) syslog (LOG_NOTICE,FAST_EXIT_PROGRAM,dateString) ; else syslog (LOG_NOTICE,STOPPING_PROGRAM,dateString) ; exit (0) ; } } bool listenerAddPeer (InnListener listener, Host hostObj) { u_int i ; dprintf (1,"Adding peer: %s\n", hostPeerName (hostObj)) ; for (i = 0 ; i < listener->hostLen ; i++) { if (listener->myHosts [i] == NULL) { listener->myHosts [i] = hostObj ; return true ; } } return false ; } /* return true if this listener doesn't ever generate articles. */ bool listenerIsDummy (InnListener listener) { return listener->dummyListener ; } /* Called by the Host when it (the Host) is about to delete itself. */ u_int listenerHostGone (InnListener listener, Host host) { u_int i ; u_int someThere = 0 ; dprintf (1,"Host is gone: %s\n", hostPeerName (host)) ; for (i = 0 ; i < listener->hostLen ; i++) if (listener->myHosts [i] == host) listener->myHosts [i] = NULL ; else if (listener->myHosts [i] != NULL) someThere++ ; return someThere ; } /* called by the Host when it has nothing to do. */ void listenerHostIsIdle (InnListener listener, Host host) { ASSERT (listener != NULL) ; ASSERT (host != NULL) ; dprintf (1,"Host is idle: %s\n", hostPeerName (host)) ; if (!listener->dummyListener) return ; /* if this listener is a dummy (i.e. not generating articles cause we're just dealing with backlog files) then forget about the host and when last one is gone we exit. */ hostClose (host) ; } void openInputFile (void) { int fd, i, mainFd ; off_t offset ; char buf [32], *p ; ASSERT (InputFile && *InputFile) ; fd = open(InputFile, O_RDWR) ; if (fd < 0) die ("open %s: %s\n", InputFile, strerror(errno)) ; mainFd = getMainEndPointFd() ; if (fd != mainFd) { if (dup2(fd, mainFd) < 0) die ("dup2 %d %d: %s\n", fd, mainFd, strerror(errno)) ; (void) close (fd); } i = read(mainFd, buf, sizeof (buf)) ; if (i < 0) die ("read %s: %s\n", InputFile, strerror(errno)) ; else if (i > 0) { p = buf; buf [ sizeof(buf) - 1 ] = '\0'; offset = (off_t) strtol (p, &p, 10) ; if (offset > 0 && *p == '\n') lseek (mainFd, offset, SEEK_SET) ; else lseek (mainFd, 0, SEEK_SET) ; } syslog(LOG_NOTICE, "ME opened %s", InputFile); } int listenerConfigLoadCbk (void *data) { int bval ; (void) data ; if (getBool (topScope,"fast-exit",&bval,NO_INHERIT)) fastExit = (bval ? true : false) ; return 1 ; } /**********************************************************************/ /** STATIC PRIVATE FUNCTIONS **/ /**********************************************************************/ /* EndPoint callback function for when the InnListener's fd is ready for reading. */ static void newArticleCommand (EndPoint ep, IoStatus i, Buffer *buffs, void *data) { InnListener lis = (InnListener) data ; char *msgid, *msgidEnd ; char *fileName, *fileNameEnd ; char *peer, *peerEnd ; char *cmd, *endc ; char *bbase = bufferBase (buffs [0]) ; size_t blen = bufferDataSize (buffs [0]) ; Buffer *readArray ; static int checkPointCounter ; ASSERT (ep = lis->myep) ; bufferAddNullByte (buffs [0]) ; if (i == IoEOF) { if ( lis == mainListener && InputFile != NULL ) { if ( RollInputFile ) { syslog(LOG_NOTICE, "ME reached EOF in %s", InputFile); openInputFile () ; RollInputFile = false ; readArray = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; prepareRead (ep, readArray, newArticleCommand, data, 1) ; } else { lis->inputEOFSleepId = prepareSleep (wakeUp, EOF_SLEEP_TIME, data) ; } } else { dprintf (1,"Got EOF on listener\n") ; syslog (LOG_NOTICE,INN_GONE) ; shutDown (lis) ; } } else if (i == IoFailed) { errno = endPointErrno (ep) ; syslog (LOG_ERR,INN_IO_ERROR) ; dprintf (1,"Got IO Error on listener\n") ; shutDown (lis) ; } else if (strchr (bbase, '\n') == NULL) /* partial read */ { expandBuffer (buffs [0], BUFFER_EXPAND_AMOUNT) ; readArray = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; prepareRead (ep, readArray, newArticleCommand, data, 1) ; } else { /* now iterate over each full command we got on the input. */ cmd = bbase ; while ((cmd < (bbase + blen)) && ((endc = strchr (cmd,'\n')) != NULL)) { Article article ; char *next = endc + 1; if (*next == '\r') next++ ; endc-- ; if (*endc != '\r') endc++ ; *endc = '\0' ; dprintf (2,"INN Command: %s\n", cmd) ; /* pick out the leading string (the filename) */ if ((fileName = findNonBlankString (cmd,&fileNameEnd)) == NULL) { syslog (LOG_ERR,INN_BAD_CMD,cmd) ; shutDown (lis) ; return ; } *fileNameEnd = '\0' ; /* for the benefit of newArticle() */ /* now pick out the next string (the message id) */ if ((msgid = findNonBlankString (fileNameEnd + 1,&msgidEnd)) == NULL) { *fileNameEnd = ' ' ; /* to make syslog work properly */ syslog (LOG_ERR,INN_BAD_CMD,cmd) ; shutDown (lis) ; return ; } *msgidEnd = '\0' ; /* for the benefit of newArticle() */ /* now create an article object and give it all the peers on the rest of the command line. Will return null if file is missing. */ article = newArticle (fileName, msgid) ; *fileNameEnd = ' ' ; *msgidEnd = ' ' ; /* now get all the peernames off the rest of the command lines */ peerEnd = msgidEnd ; do { *peerEnd = ' ' ; /* pick out the next peer name */ if ((peer = findNonBlankString (peerEnd + 1,&peerEnd))==NULL) break ; /* even no peer names is OK. */ /* XXX REALLY? */ *peerEnd = '\0' ; if (article != NULL) giveArticleToPeer (lis,article,peer) ; } while (peerEnd < endc) ; delArticle (article) ; cmd = next ; /* write a checkpoint marker if we've done another large chunk */ if (InputFile && *InputFile && ++checkPointCounter == 1000) { /* adjust the seek pointer value by the current location within the input buffer */ writeCheckPoint (blen - (cmd - bbase)) ; checkPointCounter = 0 ; } } if (*cmd != '\0') /* partial command left in buffer */ { Buffer *bArr ; u_int leftAmt = blen - (cmd - bbase) ; /* first we shift whats left in the buffer down to the bottom */ if (cmd != bbase) { memcpy (bbase,cmd,leftAmt) ; bufferSetDataSize (buffs [0],leftAmt) ; } else if ( !expandBuffer (buffs[0],BUFFER_EXPAND_AMOUNT) ) { syslog (LOG_ERR,L_BUFFER_EXPAND_ERROR); shutDown (lis) ; return ; } bArr = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; if ( !prepareRead (lis->myep, bArr, newArticleCommand, lis, 1) ) { syslog (LOG_ERR,L_PREPARE_READ_FAILED) ; freeBufferArray (bArr) ; shutDown (lis) ; return ; } } else if ( !readIsPending (lis->myep) ) { /* XXX read should never be pending here */ Buffer *bArr = makeBufferArray (bufferTakeRef (buffs [0]),NULL) ; bufferSetDataSize (buffs [0],0) ; if ( !prepareRead (lis->myep, bArr, newArticleCommand, lis, 1) ) { syslog (LOG_ERR,L_PREPARE_READ_FAILED) ; shutDown (lis) ; return ; } } } freeBufferArray (buffs) ; } /* EndPoint callback function for when the sleep due to having reached EOF on InputFile is done. */ static void wakeUp (TimeoutId id, void *data) { InnListener lis = (InnListener) data ; Buffer *readArray ; ASSERT (id = lis->inputEOFSleepId) ; lis->inputEOFSleepId = 0 ; readArray = makeBufferArray (bufferTakeRef (lis->inputBuffer), NULL) ; prepareRead (lis->myep,readArray,newArticleCommand,lis,1) ; } /* Find the Host object for the peer and hand off a reference to the article for it to transmit. */ static void giveArticleToPeer (InnListener lis, Article article, const char *peerName) { u_int i ; for (i = 0 ; i < lis->hostLen ; i++) if (lis->myHosts[i] != NULL) if (strcmp (peerName,hostPeerName (lis->myHosts [i])) == 0) { dprintf (1,"Giving article to peer: %s\n", peerName) ; hostSendArticle (lis->myHosts [i],artTakeRef (article)) ; break ; } if (i == lis->hostLen) { dprintf (1,"Failed to give article to peer: -%s-\n", peerName) ; if (lis->dynamicPeers) { Host newHostObj; dprintf (1, "Adding peer dynamically\n") ; newHostObj = newDefaultHost (lis, peerName); if (newHostObj == NULL) { /* Most likely we couldn't get the lock, i.e. the peer is blocked. */ dropArticle (peerName,article) ; } else if ( !listenerAddPeer (lis, newHostObj) ) { /* XXX need to remember we've gone over the limit and not try to add any more. */ syslog (LOG_ERR, TOO_MANY_HOSTS, lis->hostLen) ; dropArticle (peerName,article) ; } else syslog (LOG_NOTICE,DYNAMIC_PEER,peerName) ; } else { dropArticle (peerName,article) ; } } } static void writeCheckPoint (int offsetAdjust) { char offsetString[16], *writePointer ; off_t offset ; int writeBytes, writeReturn, mainFd ; mainFd = getMainEndPointFd() ; offset = lseek (mainFd, 0L, SEEK_CUR) ; if (offset < 0) syslog (LOG_ERR, "ME tell(mainFd): %m") ; else { (void) sprintf (offsetString, "%ld\n", (long)(offset - offsetAdjust) ) ; if ( lseek (mainFd, 0L, SEEK_SET) != 0 ) syslog (LOG_ERR, "ME seek(mainFd, 0, 0): %m") ; else { writeBytes = strlen (offsetString) ; writePointer = offsetString ; do { writeReturn = write (mainFd, writePointer, writeBytes) ; if (writeReturn < 0) { syslog (LOG_ERR,"ME write input checkpoint: %m") ; break ; } writePointer += writeReturn ; writeBytes -= writeReturn ; } while (writeBytes) ; if ( lseek (mainFd, offset, SEEK_SET) != offset ) die ("ME seek(mainFd, %ld, SEEK_SET): %s\n", (long)offset, strerror(errno) ) ; } } } void openDroppedArticleFile (void) { pid_t myPid = getpid () ; const char *tapeDir = getTapeDirectory() ; if (dropArtFile != NULL) FREE (dropArtFile) ; dropArtFile = malloc (pathMax(tapeDir) + 1) ; sprintf (dropArtFile,"%s/innfeed-dropped.%c%06d", tapeDir, droppedFileCount + 'A', (int) myPid) ; if ((droppedFp = fopen (dropArtFile,"w")) == NULL) { syslog (LOG_ERR,NO_DROPPED_FILE,dropArtFile) ; FREE (dropArtFile) ; dropArtFile = NULL ; if ((droppedFp = fopen ("/dev/null","w")) == NULL) { syslog (LOG_ERR,NO_NULL_FILE) ; die ("Error opening /dev/null") ; } } } void closeDroppedArticleFile (void) { long pos ; if (droppedFp == NULL) return ; fflush (droppedFp) ; pos = ftell (droppedFp) ; fclose (droppedFp) ; droppedFp = NULL ; if (pos == 0 && dropArtFile != NULL) unlink (dropArtFile) ; else if (pos != 0 && dropArtFile == NULL) syslog (LOG_WARNING,LOST_ARTICLE_COUNT,droppedCount) ; else if (pos != 0) syslog (LOG_NOTICE,DROPPED_ARTICLE_COUNT,droppedCount) ; droppedFileCount = (droppedFileCount + 1) % 26 ; droppedCount = 0 ; } static void dropArticle (const char *peerName, Article article) { static bool logged = false ; if (!logged) { syslog (LOG_WARNING,DROPPED_LOCATION,dropArtFile) ; logged = true ; } droppedCount++ ; fprintf (droppedFp,"%s %s %s\n",artFileName (article), artMsgId (article), peerName) ; } static void listenerCleanup (void) { FREE (dropArtFile) ; } innfeed-0.10.1.7.orig/innlistener.h0100644000175100001440000000651506331417054015343 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 08:25:26 1995 * Project: INN (innfeed) * File: innlistener.h * RCSId: $Id: innlistener.h,v 1.1.1.1 1997/04/29 16:13:32 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The public interface of the things that listens to * commands from INN. It receives lines of the form: * * filename msgid peer1 peer2 peer3 * * and turns them into Article objects and hands * those Articles off to the Host objects. * */ #if ! defined ( innlistener_h__ ) #define innlistener_h__ #include #include "misc.h" extern InnListener mainListener ; /* Initialization of the InnListener object. If it fails then returns NULL. ENDPOINT is the endpoint where the article info will come from. A dummy listener exists when processing backlog files and is there just to help drive the process. */ InnListener newListener (EndPoint endp, bool isDummy, bool dynamicPeers) ; /* print some useful debugging information about the Listener and all its * Hosts and all their Connecitons/Article/Buffers etc. to the given FILE. */ void gPrintListenerInfo (FILE *fp, u_int indentAmt) ; void printListenerInfo (InnListener listener, FILE *fp, u_int indentAmt) ; /* Called by the Host when it is about to delete itself */ u_int listenerHostGone (InnListener listener, Host host) ; /* Called to hook up the given Host to the Listener. */ bool listenerAddPeer (InnListener listener, Host hostObj) ; /* true if the listener is a dummy. */ bool listenerIsDummy (InnListener listener) ; /* * This gets called to stop accepting new articles from innd. Typically * called by the signal handler, or when the listener gets EOF on its input * (in channel mode) */ void shutDown (InnListener cxn) ; /* Callback fired after config file is loaded */ int listenerConfigLoadCbk (void *data) ; /* stop a specific host. */ void shutDownHost (InnListener cxn, const char *peerName) ; /* Called by the Host when it has nothing to do (so it can be shut down if necessary). */ void listenerHostIsIdle (InnListener listener, Host host) ; void openInputFile (void) ; void openDroppedArticleFile (void) ; void closeDroppedArticleFile (void) ; void listenerLogStatus (FILE *fp) ; #endif /* innlistener_h__ */ innfeed-0.10.1.7.orig/main.c0100644000175100001440000006134106400214365013723 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Mon, 15 Jan 1996 17:31:58 +1100 * Project: INN -- innfeed * File: main.c * RCSId: $Id: main.c,v 1.5 1997/08/25 05:32:37 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Main routines for the innfeed program. * */ #if ! defined (lint) static const char *rcsid = "$Id: main.c,v 1.5 1997/08/25 05:32:37 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" /* system specific configuration */ #include #include #include #include #include #include #include #include #include #include #include #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #include #include #include #include #include #include #include "misc.h" #include "tape.h" #include "article.h" #include "msgs.h" #include "buffer.h" #include "connection.h" #include "configfile.h" #if defined(DO_HAVE_UNIX_DOMAIN) #include #endif /* defined(DO_HAVE_UNIX_DOMAIN) */ #include "endpoint.h" #include "host.h" #include "innlistener.h" #ifdef XXX_RAWHACK #include #include "raw.h" char *LogName = "innfeed"; #endif /* XXX_RAWHACK */ #define INHERIT 1 #define NO_INHERIT 0 /* exports */ bool talkToSelf ; extern int debugWrites ; bool sigFlag = false ; const char *InputFile ; const char *configFile = NULL ; bool RollInputFile = false ; const char *pidFile = NULL ; bool useMMap = false ; void (*gPrintInfo) (void) ; /* imports */ extern char *versionInfo ; #if defined (sun) extern char *optarg ; /* needed for Solaris */ extern int optind; #endif extern void openInputFile (void); /* privates */ static char *logFile ; static char *newsspool ; static void sigemt (int sig) ; static void sigalrm (int sig) ; static void sigchld (int sig) ; static void sigint (int sig) ; static void sigquit (int sig) ; static void sighup (int sig) ; static void sigterm (int sig) ; static void sigusr (int sig) ; static void usage (int) ; static void gprintinfo (void) ; static void openLogFile (void) ; static void writePidFile (void) ; static int mainOptionsProcess (void *data) ; static int mainConfigLoadCbk (void *data) ; static void mainCleanup (void) ; static char *bopt = NULL ; static char *aopt = NULL ; static char *popt = NULL ; static bool Mopt = false ; static bool Zopt = false ; static bool Dopt = false ; static int debugLevel = 0 ; static u_int initialSleep = 2 ; static char *sopt = NULL ; static char *lopt = NULL ; static bool eopt = false ; static int elimit = 0 ; int main (int argc, char **argv) { EndPoint ep ; InnListener listener ; int optVal, fd, rval ; const char *subProgram = NULL ; bool seenV = false ; bool dynamicPeers = false ; time_t now = theTime() ; char dateString [30] ; char *copt = NULL ; bool checkConfig = false ; struct rlimit rl; strcpy (dateString,ctime(&now)) ; dateString [24] = '\0' ; if ((program = strrchr (argv [0],'/')) == NULL) program = argv [0] ; else program++ ; gPrintInfo = gprintinfo ; #if defined (HAVE_MMAP) useMMap = true ; #else useMMap = false ; #endif #ifdef XXX_RAWHACK_CRLFSTORAGE artBitFiddleContents (false) ; #else /* XXX_RAWHACK_CRLFSTORAGE */ /* always turn on copying into memory unless mmapping and the article data being in NNTP format. */ artBitFiddleContents (true) ; #endif /* XXX_RAWHACK_CRLFSTORAGE */ #define OPT_STRING "a:b:c:Cd:e:hl:mMo:p:S:s:vxyz" while ((optVal = getopt (argc,argv,OPT_STRING)) != EOF) { switch (optVal) { case 'a': aopt = optarg ; break ; case 'b': if ( !isDirectory (optarg) ) logAndExit (1,"Not a directory: %s\n",optarg) ; bopt = optarg ; break ; case 'C': checkConfig = true ; break ; case 'c': copt = optarg ; break ; case 'd': loggingLevel = atoi (optarg) ; debugLevel = loggingLevel ; Dopt = true ; break ; case 'e': eopt = true ; elimit = atoi (optarg) ; if (elimit <= 0) { fprintf (stderr,"Illegal value for -e option\n") ; usage (1) ; } break ; case 'h': usage (0) ; case 'l': lopt = optarg ; break ; case 'M': Mopt = true ; useMMap = false ; break ; case 'm': artLogMissingArticles (true) ; break ; case 'o': artSetMaxBytesInUse (atoi (optarg)) ; break ; case 'p': popt = optarg ; break ; case 's': subProgram = optarg ; break ; case 'S': sopt = optarg ; break ; case 'v': seenV = true ; break ; case 'x': talkToSelf = true ; break ; case 'y': dynamicPeers = true ; break ; case 'z': Zopt = true ; break ; default: usage (1) ; } } argc -= optind; argv += optind; if (argc > 1) usage (1) ; else if (argc == 1) InputFile = *argv; if (seenV) { warn ("%s version: %s\n",program, versionInfo) ; exit (0) ; } /* make sure we have valid fds 0, 1 & 2 so it is not taken by something else, probably openlog(). fd 0 will be freopen()ed on the inputFile, the subProgram, or ourself. fd 1 and fd 2 will be freopen()ed on the log file (or will stay pointed at /dev/null). without doing this, if the descriptors were closed then the freopen calls on some systems (like BSDI 2.1) will really close whatever has aquired the stdio descriptors, such as the socket to syslogd. XXX possible problems: what if fd 0 is closed but no inputFile, XXX subProgram or talkToSelf is true? it will not be freopen()ed, so XXX innfeed won't have any fresh data (besides, fd 0 is only writable XXX here). perhaps a warning should be issued. */ do { fd = open("/dev/null", O_WRONLY); switch (fd) { case -1: logAndExit (1,"open(\"/dev/null\", O_WRONLY): %s", strerror (errno)); break; case 0: case 1: case 2: /* good, we saved an fd from being trounced */ break; default: close(fd); } } while (fd < 2); if ( !checkConfig ) { openlog (program,(int)(L_OPENLOG_FLAGS|LOG_PID),LOG_NEWS) ; syslog (LOG_NOTICE,STARTING_PROGRAM,versionInfo,dateString) ; } #ifdef XXX_RAWHACK syslog(LOG_NOTICE, "Calling RAWread_config()"); RAWread_config(); #endif /* XXX_RAWHACK */ if (subProgram == NULL && talkToSelf == false) { struct stat buf ; if (fstat (0,&buf) < 0) logAndExit (1,FSTAT_FAILURE,"stdin") ; else if (S_ISREG (buf.st_mode)) InputFile = ""; } /* * set up the config file name and then read the file in. Order is important. */ configAddLoadCallback (mainOptionsProcess,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (tapeConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (endpointConfigLoadCbk,(checkConfig ? stderr : NULL)); configAddLoadCallback (hostConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (cxnConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (mainConfigLoadCbk,(checkConfig ? stderr : NULL)) ; configAddLoadCallback (listenerConfigLoadCbk,(checkConfig ? stderr : NULL)); if (copt != NULL && *copt == '\0') { logOrPrint (LOG_CRIT,(checkConfig ? stderr : NULL), "Empty pathname for ``-c'' option") ; exit (1) ; } else if (copt != NULL && bopt != NULL) configFile = buildFilename (bopt,copt) ; else if (bopt != NULL) configFile = buildFilename (bopt,CONFIG_FILE) ; else if (copt != NULL) configFile = buildFilename (TAPE_DIRECTORY,copt) ; else configFile = buildFilename (TAPE_DIRECTORY,CONFIG_FILE) ; rval = readConfig (configFile,(checkConfig ? stderr : NULL), checkConfig,loggingLevel > 0); if (subProgram != NULL && (talkToSelf == true || InputFile)) { dprintf (0,"Cannot specify '-s' with '-x' or an input file\n") ; syslog (LOG_ERR,"Incorrect arguments: '-s' with '-x' or an input file\n"); usage (1) ; } if (checkConfig) { if (!rval) fprintf (stderr,"config loading failed.\n") ; else fprintf (stderr,"config loading succeeded.\n") ; exit (1) ; } else if (!rval) exit (1) ; if (loggingLevel == 0 && fileExistsP (DEBUG_FILE)) loggingLevel = 1 ; if (logFile == NULL && ! isatty (fileno (stderr))) logFile = strdup (LOG_FILE) ; if (logFile) openLogFile () ; openfds = 4 ; /* stdin, stdout, stderr and syslog */ writePidFile (); if (subProgram != NULL) { int fds [2] ; int pid ; if (pipe (fds) < 0) { syslog (LOG_CRIT,PIPE_FAILURE) ; exit (1) ; } if ((pid = fork ()) < 0) { syslog (LOG_CRIT,FORK_FAILURE) ; exit (1) ; } else if (pid == 0) { /* child */ close (fds[0]) ; close (0) ; close (1) ; close (2) ; dup2 (fds[1],1) ; dup2 (fds[1],2) ; execlp ("sh","sh", "-c", subProgram, NULL) ; perror ("execlp") ; exit (1) ; } else { /* parent */ close (0) ; dup2 (fds[0],0) ; close (fds[1]) ; signal(SIGCHLD,sigchld) ; openfds++ ; } } else if (talkToSelf) { /* We're not really getting information from innd or a subprogram, but are just processing backlog files. We set up a pipe to ourself that we never write to, to simulate an idle innd. */ int pipefds [2] ; if (pipe (pipefds) != 0) { syslog (LOG_ERR,PIPE_FAILURE) ; exit (1) ; } close (0) ; dup2 (pipefds [0], 0) ; openfds++ ; openfds++ ; } if (chdir (newsspool) != 0) { syslog (LOG_ERR,CD_FAILED,newsspool) ; exit (1) ; } /* hook up the endpoint to the source of new article information (usually innd). */ ep = newEndPoint (0) ; /* fd 0, i.e. stdin */ /* now arrange for this endpoint to always be the first one checked for possible activity. */ setMainEndPoint (ep) ; listener = newListener (ep, talkToSelf,dynamicPeers) ; mainListener = listener ; sleep (initialSleep) ; /* now lower maximum open file limit to match what select(2) can handle. */ if (getrlimit(RLIMIT_NOFILE,&rl) != 0) syslog (LOG_ERR,GETRLIM_FAILED) ; else { #if defined (FD_SETSIZE) u_int fd_max = FD_SETSIZE ; #else u_int fd_max = sizeof (fd_set) * CHAR_BIT ; #endif if (rl.rlim_max > fd_max) { rl.rlim_max = rl.rlim_cur = fd_max ; if (setrlimit (RLIMIT_NOFILE,&rl) != 0) syslog (LOG_ERR,SETRLIM_FAILED,(long)fd_max); } } configHosts (talkToSelf) ; if (InputFile && *InputFile) { openInputFile () ; } /* handle signal to shutdown */ setSigHandler (SIGTERM,sigterm) ; setSigHandler (SIGQUIT,sigquit) ; /* handle signal to reload config */ setSigHandler (SIGHUP,sighup) ; /* handle signal to print snapshot. */ setSigHandler (SIGINT,sigint) ; /* handle signal to roll input file */ setSigHandler (SIGALRM,sigalrm) ; /* handle signal to flush all the backlog files */ setSigHandler (SIGEMT,sigemt) ; /* we can increment and decrement logging levels by sending SIGUSR{1,2} */ setSigHandler (SIGUSR1,sigusr) ; setSigHandler (SIGUSR2,sigusr) ; atexit (mainCleanup) ; Run () ; exit (0) ; } static void usage (int val) { fprintf (stderr,"usage: %s [ options ] [ file ]\n\n", program) ; fprintf (stderr,"Version: %s\n\n",versionInfo) ; fprintf (stderr,"Config file: %s\n",CONFIG_FILE) ; fprintf (stderr,"Backlog directory: %s\n",TAPE_DIRECTORY) ; fprintf (stderr,"\nLegal options are:\n") ; fprintf (stderr,"\t-a dir Use the given directory as the top of the article spool\n") ; fprintf (stderr,"\t-b dir Use the given directory as the the storage\n"); fprintf (stderr,"\t place for backlog files and lock files.\n"); fprintf (stderr,"\t-c file Use the given file as the config file instead of the\n"); fprintf (stderr,"\t default of %s\n",CONFIG_FILE); fprintf (stderr,"\t-C Check the config file and then exit.\n") ; fprintf (stderr,"\t-d num set the logging level to num (an integer).\n"); fprintf (stderr,"\t Larger value means more logging. 0 means no\n"); fprintf (stderr,"\t logging. The default is 0\n"); fprintf (stderr,"\t-e bytes Keep the output backlog files to no bigger\n"); fprintf (stderr,"\t than %.2f times this number\n",LIMIT_FUDGE); fprintf (stderr,"\t-h print this message\n"); fprintf (stderr,"\t-l file redirect stderr and stdout to the given file.\n"); fprintf (stderr,"\t When run under INN they normally are redirected to\n"); fprintf (stderr,"\t /dev/null. This is needed if using '-d'.\n"); fprintf (stderr,"\t-m Log information on all missing articles\n"); fprintf (stderr,"\t-M Turn *off* use of mmap\n") ; #if ! defined (HAVE_MMAP) fprintf (stderr,"\t (a no-op as this excutable has been built without mmap support\n") ; #endif fprintf (stderr,"\t-p file Write the process id to the given file\n") ; fprintf (stderr,"\t instead of the default of %s\n",PID_FILE); fprintf (stderr,"\t A relative path is relative to the backlog directory\n") ; fprintf (stderr,"\t-s command run the given command in a subprocess and use\n"); fprintf (stderr,"\t its output as article information instead of\n"); fprintf (stderr,"\t running under innd\n"); fprintf (stderr,"\t-S file Use the give filename instead of innfeed.status\n") ; fprintf (stderr,"\t relative pathnames start from the backlog directory\n") ; fprintf (stderr,"\t-v print version information\n"); fprintf (stderr,"\t-x Do not read any article information off stdin,\n"); fprintf (stderr,"\t but simply process backlog files and then exit\n"); fprintf (stderr,"\t when done\n"); fprintf (stderr,"\t-y Add peers dynamically. If an unrecognized peername\n"); fprintf (stderr,"\t is received from innd, then it is presumed to also\n"); fprintf (stderr,"\t be the ip name and a new peer binding is set up\n"); fprintf (stderr,"\t-z have each of the connections issue their own stats\n"); fprintf (stderr,"\t whenever they close, or whenever their controller\n"); fprintf (stderr,"\t issues its own stats\n"); exit (val) ; } static void sigterm (int sig) { (void) sig ; syslog(LOG_NOTICE, SHUTDOWN_SIGNAL); shutDown (mainListener) ; } static void sigquit (int sig) { (void) sig ; sigterm (0) ; } static void sigint (int sig) { (void) sig ; gprintinfo () ; } static void sighup (int sig) { (void) sig ; syslog(LOG_NOTICE, CONFIG_RELOAD, configFile); if (!readConfig (configFile,NULL,false,loggingLevel > 0)) { syslog (LOG_ERR,PARSE_FAILURE) ; exit (1) ; } configHosts (talkToSelf) ; } static void sigemt (int sig) { (void) sig ; gFlushTapes () ; } static void sigalrm (int sig) { (void) sig ; if (InputFile == NULL) syslog (LOG_ERR,IGNORE_SIGALRM) ; else { RollInputFile = true; syslog(LOG_NOTICE, "ME preparing to roll %s", InputFile); } } static void sigchld (int sig) { (void) sig ; /* keep lint happy */ #if 0 wait (&status) ; /* we don't care */ #endif signal (sig,sigchld) ; } /* SIGUSR1 increments logging level. SIGUSR2 decrements. */ static void sigusr (int sig) { if (sig == SIGUSR1) { syslog(LOG_NOTICE, INCR_LOGLEVEL, loggingLevel); loggingLevel++ ; } else if (sig == SIGUSR2 && loggingLevel > 0) { syslog(LOG_NOTICE, DECR_LOGLEVEL, loggingLevel); loggingLevel-- ; } } static void openLogFile (void) { FILE *fpr ; if (logFile) { fpr = freopen (logFile,"a",stdout) ; if (fpr != stdout) logAndExit (1,"freopen (%s, \"a\", stdout): %s", logFile, strerror (errno)) ; fpr = freopen (logFile,"a",stderr) ; if (fpr != stderr) logAndExit (1,"freopen (%s, \"a\", stderr): %s", logFile, strerror (errno)) ; #if defined (DO_HAVE_SETBUFFER) setbuffer (stdout, NULL, 0) ; setbuffer (stderr, NULL, 0) ; #else setbuf (stdout, NULL) ; setbuf (stderr, NULL) ; #endif } } static void writePidFile (void) { FILE *F; int pid; if (pidFile == NULL) logAndExit (1,"NULL pidFile\n") ; /* Record our PID. */ pid = getpid(); if ((F = fopen(pidFile, "w")) == NULL) { syslog(LOG_ERR, "ME cant fopen %s %m", pidFile); } else { if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F)) { syslog(LOG_ERR, "ME cant fprintf %s %m", pidFile); } if (fclose(F) == EOF) { syslog(LOG_ERR, "ME cant fclose %s %m", pidFile); } if (chmod(pidFile, 0664) < 0) { syslog(LOG_ERR, "ME cant chmod %s %m", pidFile); } } } static void gprintinfo (void) { FILE *fp = fopen (SNAPSHOT_FILE,"a") ; time_t now = theTime() ; if (fp == NULL) { syslog (LOG_ERR,FOPEN_FAILURE,SNAPSHOT_FILE) ; return ; } #if defined (DO_HAVE_SETBUFFER) setbuffer (fp, NULL, 0) ; #else setbuf (fp, NULL) ; #endif fprintf (fp,"----------------------------System snaphot taken at: %s\n", ctime (&now)) ; gPrintListenerInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintHostInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintCxnInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintArticleInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; gPrintBufferInfo (fp,0) ; fprintf (fp,"\n\n\n\n") ; fclose (fp) ; } /* called after the config file is loaded and after the config data has been updated with command line options. */ static int mainConfigLoadCbk (void *data) { FILE *fp = (FILE *) data ; char *p ; long ival ; int bval ; if (getString (topScope,"news-spool", &p,NO_INHERIT)) { if ( !isDirectory (p) && isDirectory (NEWSSPOOL) ) { logOrPrint (LOG_WARNING,fp,BADSPOOL_CHANGE,p,NEWSSPOOL) ; p = strdup (NEWSSPOOL) ; } else if (!isDirectory (p)) logAndExit (1,"Bad spool directories: %s, %s\n",p,NEWSSPOOL) ; } else if (!isDirectory (NEWSSPOOL)) logAndExit (1,SPOOL_NODEF,NEWSSPOOL); else p = strdup (NEWSSPOOL) ; newsspool = p ; /***************************************************/ if (getString (topScope,"input-file",&p,NO_INHERIT)) { if (*p != '\0') InputFile = buildFilename (getTapeDirectory(),p) ; else InputFile = "" ; free (p) ; } if (getString (topScope,"pid-file",&p,NO_INHERIT)) { pidFile = buildFilename (getTapeDirectory(),p) ; free (p) ; } else pidFile = buildFilename (getTapeDirectory(),PID_FILE) ; if (getInteger (topScope,"debug-level",&ival,NO_INHERIT)) loggingLevel = (u_int) ival ; if (getInteger (topScope,"initial-sleep",&ival,NO_INHERIT)) initialSleep = (u_int) ival ; if (getBool (topScope,"use-mmap",&bval,NO_INHERIT)) useMMap = (bval ? true : false) ; if (getString (topScope,"log-file",&p,NO_INHERIT)) { logFile = buildFilename (getTapeDirectory(),p) ; FREE (p) ; } return 1 ; } /* * called after config file is loaded but before other callbacks, so we * can adjust config file values from options. They will be validated in the * second callback. */ static int mainOptionsProcess (void *data) { value *v ; (void) data ; if (bopt != NULL) { if ((v = findValue (topScope,"backlog-directory",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (bopt) ; } else addString (topScope,"backlog-directory",strdup (bopt)) ; } if (aopt != NULL) { if ((v = findValue (topScope,"news-spool",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (aopt) ; } else addString (topScope,"news-spool",strdup (aopt)) ; } if (sopt != NULL) { if ((v = findValue (topScope,"status-file",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (sopt) ; } else addString (topScope,"status-file",strdup (sopt)) ; } if (Dopt) { if ((v = findValue (topScope,"debug-level",NO_INHERIT)) != NULL) v->v.int_val = debugLevel ; else addInteger (topScope,"debug-level",debugLevel) ; } if (eopt || talkToSelf) { if (talkToSelf) elimit = 0 ; if ((v = findValue (topScope,"backlog-limit",NO_INHERIT)) != NULL) v->v.int_val = elimit ; else addInteger (topScope,"backlog-limit",elimit) ; } if (Mopt) { if ((v = findValue (topScope,"use-mmap",NO_INHERIT)) != NULL) v->v.bool_val = 0 ; else addBoolean (topScope,"use-mmap",0) ; } if (popt != NULL) { if ((v = findValue (topScope,"pid-file",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (popt) ; } else addString (topScope,"pid-file",strdup (popt)) ; } if (Zopt) { if ((v = findValue (topScope,"connection-stats",NO_INHERIT)) != NULL) v->v.bool_val = 1 ; else addBoolean (topScope,"connection-stats",1) ; } if (lopt != NULL) { if ((v = findValue (topScope,"log-file",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (lopt) ; } else addString (topScope,"log-file",strdup (lopt)) ; } if (InputFile != NULL) { if ((v = findValue (topScope,"input-file",NO_INHERIT)) != NULL) { FREE (v->v.charp_val) ; v->v.charp_val = strdup (InputFile) ; } else addString (topScope,"input-file",strdup (InputFile)) ; } return 1 ; } static void mainCleanup (void) { FREE (configFile) ; FREE (pidFile) ; FREE (logFile) ; FREE (newsspool) ; } void mainLogStatus (FILE *fp) { fprintf (fp,"Global configuration parameters:\n") ; fprintf (fp," Mode: ") ; if (InputFile != NULL) fprintf (fp,"Funnel file") ; else if (talkToSelf) fprintf (fp,"Batch") ; else fprintf (fp,"Channel") ; if (InputFile != NULL) fprintf (fp," (%s)",(*InputFile == '\0' ? "stdin" : InputFile)) ; fprintf (fp,"\n") ; fprintf (fp," News spool: %s\n",newsspool) ; fprintf (fp," Pid file: %s\n",pidFile) ; fprintf (fp," Log file: %s\n",(logFile == NULL ? "(none)" : logFile)); fprintf (fp," Debug level: %2ld Mmap: %s\n", (long)loggingLevel,boolToString(useMMap)) ; fprintf (fp,"\n") ; } innfeed-0.10.1.7.orig/makedepend.sh0100644000175100001440000001033606331417056015267 0ustar mdusers#!/bin/sh # # Author: James A. Brister -- berkeley-unix -- # Start Date: Sat, 17 Feb 1996 22:05:07 +1100 # Project: INN -- innfeed # File: makedepend.sh # RCSId: $Id: makedepend.sh,v 1.1.1.1 1997/04/29 16:13:34 scrappy Exp $ # Description: A replacement for 'gcc -MM'. # if [ "X$CPP" = X ]; then CPP="cc -E" fi MAKEFILE=Makefile OBJSUFFIX=o OBJPREFIX='' # The field number (as awk numbers field) in a CPP-output line # that has the file name. FILE_FIELD=3 IGNORE_BAD=false USAGE="`basename $0` [ -c cpp-cmd -x field-num -s -a -f file -o obj-suffix -p obj-prefix -Ddefine -Iinclude -- ] files -c cpp-cmd specifies the command to run the source files over to preprocess the files. Defaults to 'cc -E'. -x field-num specifies the field number of the preprocessor tagged lines (lines with a leading '#') that has the file name. Field numbers start at 1. Default is 3. -s Specifies to ignore system include files (actually any include file that CPP reports with an absolute pathname -a specifies to append the info to the Makefile instread of replacing what is already there. -f file Specifies the file to edit instead of Makefile. -o obj-suffix Specifies the suffix to give to the object files instead of '.o' -p obj-prefix Specifies the prefix to give to the object file names instead of the empty string (for adding directories to the name usually). -- Specifies that any unrecognised arguments after this are silently ignored. -Ddefine A normal C or C++ compiler -D option. -Iinclude A normal C or C++ compiler -I include file path option." while [ $# != 0 ]; do case "$1" in -c*) CPP=`expr "$1" : '-c\(.*\)'` if [ "X$CPP" = X ]; then CPP=$2 shift fi ;; -D*) DEFINES="$DEFINES $1" ;; -I*) INCLUDES="$INCLUDES $1" ;; -a) APPEND=1 ;; -f*) MAKEFILE=`expr "$1" : '-f\(.*\)'` if [ "X$MAKEFILE" = X ]; then MAKEFILE=$2 shift fi ;; -o*) OBJSUFFIX=`expr "$1" : '-o\(.*\)'` if [ "X$OBJSUFFIX" = X ]; then OBJSUFFIX=$2 shift fi ;; -p*) OBJPREFIX=`expr "$1" : '-p\(.*\)'` if [ "X$OBJPREFIX" = X ]; then OBJPREFIX=$2 shift fi ;; -w*) ;; # ignored -m*) ;; # ignored -x*) FILE_FIELD=`expr "$1" : '-x\(.*\)'` if [ "X$FILE_FIELD" = X ]; then FILE_FIELD=$2 shift fi if [ `expr "$FILE_FIELD" : '^[0-9][0-9]*$'` = 0 ]; then echo 'Must give a field number (starting at 1) to "-x"' DIE=1 elif [ 0 -ge $FILE_FIELD ]; then echo 'Must give a field number (starting at 1) to "-x"' DIE=1 fi ;; -s) NO_SYSTEM=1 ;; --) if $IGNORE_BAD; then IGNORE_BAD=false else IGNORE_BAD=true fi ;; -*) if $IGNORE_BAD; then true else echo "Unknown option $1" DIE=1 fi ;; *) FILES="$FILES $1" ;; esac shift done if [ -n "$DIE" ]; then echo "$USAGE" exit 1 fi ARGS="$DEFINES $INCLUDES" WKFILE=/tmp/makedepend.$$ TAILFILE=/tmp/makedepend.tail.$$ if [ ! -f $MAKEFILE ]; then echo "No such file: $MAKEFILE" exit 1 fi SYSTEM_FILES=cat if [ -n "$NO_SYSTEM" ]; then SYSTEM_FILES="grep -v ^/" fi for file in $FILES; do OBJ=`expr "$file" : '\(.*\)\..*'` OBJ="${OBJPREFIX}$OBJ.$OBJSUFFIX" $CPP $ARGS $file 2> /dev/null | awk '/^#/ && NF >= '$FILE_FIELD'{print $'$FILE_FIELD'}' | sed -e 's/"//g' | sort | uniq | $SYSTEM_FILES | tr '\012' ' ' > $WKFILE if [ -s $WKFILE ]; then (echo -n "${OBJ}: " ; cat $WKFILE ; echo "") | fmt | sed -e 's/$/ \\/' \ -e '2,$s/^/ /' \ -e '$s/ \\$//' >> $TAILFILE else echo "Hmmmmm.... Not good. No dependencies for $file." fi rm -f $WKFILE done LINE="# DO NOT DELETE THIS LINE -- make depend depends on it." if [ -s $TAILFILE ]; then mv $MAKEFILE $MAKEFILE.BAK || { echo "Can't move $MAKEFILE to $MAKEFILE.BAK" exit } if [ "X$APPEND" = X ]; then sed -n -e '1,/^'"$LINE"'/p' $MAKEFILE.BAK > $MAKEFILE if grep -q '^'"$LINE" $MAKEFILE > /dev/null 2>&1; then true else (echo "" ; echo "$LINE") >> $MAKEFILE fi else cat $MAKEFILE.BAK > $MAKEFILE fi ( echo "" ; cat $TAILFILE ) >> $MAKEFILE fi rm -f $TAILFILE innfeed-0.10.1.7.orig/malloc.c0100644000175100001440000002511506340727547014263 0ustar mdusers/* $Revision: 1.2 $ * * Revision 4.0.1.1 91/04/11 17:48:31 lwall * patch1: Configure now figures out malloc ptr type * * Revision 4.0 91/03/20 01:28:52 lwall * 4.0 baseline. * * Slightly hacked by Matthias Urlichs (urlichs@smurf.sub.org) * to fit into INND; 91/08/28. */ #define RCHECK #ifndef lint static char sccsid[] = "@(#)malloc.c 4.3 (Berkeley) 9/16/83"; /* * malloc.c (Caltech) 2/21/82 * Chris Kingsley, kingsley@cit-20. * * This is a very fast storage allocator. It allocates blocks of a small * number of different sizes, and keeps free lists of each size. Blocks that * don't exactly fit are passed up to the next larger size. In this * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. * This is designed for use in a program that uses vast quantities of memory, * but bombs when it runs out. */ #include #include #include "configdata.h" #include "clibrary.h" #include "logging.h" static findbucket(), morecore(); /* I don't much care whether these are defined in sys/types.h--LAW */ #define u_char unsigned char #define u_int unsigned int #define u_short unsigned short /* * The overhead on a block is at least 4 bytes. When free, this space * contains a pointer to the next free block, and the bottom two bits must * be zero. When in use, the first byte is set to MAGIC, and the second * byte is the size index. The remaining bytes are for alignment. * If range checking is enabled and the size of the block fits * in two bytes, then the top two bytes hold the size of the requested block * plus the range checking words, and the header word MINUS ONE. */ union overhead { union overhead *ov_next; /* when free */ #if ALIGNBYTES > 4 double strut; /* alignment problems */ #endif struct { u_char ovu_magic; /* magic number */ u_char ovu_index; /* bucket # */ #ifdef RCHECK u_short ovu_size; /* actual block size */ u_int ovu_rmagic; /* range magic number */ #endif } ovu; #define ov_magic ovu.ovu_magic #define ov_index ovu.ovu_index #define ov_size ovu.ovu_size #define ov_rmagic ovu.ovu_rmagic }; #define MAGIC 0xff /* magic # on accounting info */ #define OLDMAGIC 0x7f /* same after a free() */ #define RMAGIC 0x55555555 /* magic # on range info */ #ifdef RCHECK #define RSLOP sizeof (u_int) #else #define RSLOP 0 #endif /* * nextf[i] is the pointer to the next free block of size 2^(i+3). The * smallest allocatable block is 8 bytes. The overhead information * precedes the data area returned to the user. */ #define NBUCKETS 30 static union overhead *nextf[NBUCKETS]; #if ! defined (NO_SBRK) extern char *sbrk(); #endif #ifdef MSTATS /* * nmalloc[i] is the difference between the number of mallocs and frees * for a given block size. */ static u_int nmalloc[NBUCKETS]; #include #endif #ifdef debug #define ASSERT(p) if (!(p)) botch("p"); else static botch(s) char *s; { printf("assertion botched: %s\n", s); sleep (5) ; abort(); } #else #ifdef __STDC__ #define ASSERT(p) if(!(p)) syslog(L_FATAL, #p); else #else #define ASSERT(p) if(!(p)) syslog(L_FATAL, "p"); else #endif #endif POINTER malloc(nbytes) register SIZE_T nbytes; { register union overhead *p; register int bucket = 0; register unsigned shiftr; /* * Convert amount of memory requested into * closest block size stored in hash buckets * which satisfies request. Account for * space used per block for accounting. */ nbytes += sizeof (union overhead) + RSLOP; nbytes = (nbytes + 3) &~ 3; shiftr = (nbytes - 1) >> 2; /* apart from this loop, this is O(1) */ while (shiftr >>= 1) bucket++; /* * If nothing in hash bucket right now, * request more memory from the system. */ if (nextf[bucket] == NULL) morecore(bucket); if ((p = (union overhead *)nextf[bucket]) == NULL) return (NULL); /* remove from linked list */ #ifdef RCHECK if (*((int*)p) & (sizeof(union overhead) - 1)) #ifndef I286 fprintf(stderr,"Corrupt malloc ptr 0x%x at 0x%x\n",*((int*)p),p); #else fprintf(stderr,"Corrupt malloc ptr 0x%lx at 0x%lx\n",*((int*)p),p); #endif #endif nextf[bucket] = p->ov_next; p->ov_magic = MAGIC; p->ov_index= bucket; #ifdef MSTATS nmalloc[bucket]++; #endif #ifdef RCHECK /* * Record allocated size of block and * bound space with magic numbers. */ if (nbytes <= 0x10000) p->ov_size = nbytes - 1; p->ov_rmagic = RMAGIC; *((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC; #endif return ((char *)(p + 1)); } /* * Allocate more memory to the indicated bucket. */ static morecore(bucket) register int bucket; { register union overhead *op; register int rnu; /* 2^rnu bytes will be requested */ register int nblks; /* become nblks blocks of the desired size */ register int siz; if (nextf[bucket]) return; /* * Insure memory is allocated * on a page boundary. Should * make getpageize call? */ op = (union overhead *)sbrk(0); #ifndef I286 if ((int)op & 0x3ff) (void)sbrk(1024 - ((int)op & 0x3ff)); #else /* The sbrk(0) call on the I286 always returns the next segment */ #endif #ifndef I286 /* take 2k unless the block is bigger than that */ rnu = (bucket <= 8) ? 11 : bucket + 3; #else /* take 16k unless the block is bigger than that (80286s like large segments!) */ rnu = (bucket <= 11) ? 14 : bucket + 3; #endif nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */ if (rnu < bucket) rnu = bucket; op = (union overhead *)sbrk(1 << rnu); /* no more room! */ if ((int)op == -1) return; /* * Round up to minimum allocation size boundary * and deduct from block count to reflect. */ #ifndef I286 if ((int)op & 7) { op = (union overhead *)(((int)op + 8) &~ 7); nblks--; } #else /* Again, this should always be ok on an 80286 */ #endif /* * Add new memory allocated to that on * free list for this hash bucket. */ nextf[bucket] = op; siz = 1 << (bucket + 3); while (--nblks > 0) { op->ov_next = (union overhead *)((caddr_t)op + siz); op = (union overhead *)((caddr_t)op + siz); } } FREEVAL free(cp) POINTER cp; { register int size; register union overhead *op; if (cp == NULL) return; op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); #ifdef debug ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ #else if (op->ov_magic != MAGIC) { syslog(L_ERROR, "%s free() ignored", op->ov_magic == OLDMAGIC ? "Duplicate" : "Bad"); return; /* sanity */ } op->ov_magic = OLDMAGIC; #endif #ifdef RCHECK ASSERT(op->ov_rmagic == RMAGIC); if (op->ov_index <= 13) ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC); #endif ASSERT(op->ov_index < NBUCKETS); size = op->ov_index; op->ov_next = nextf[size]; nextf[size] = op; #ifdef MSTATS nmalloc[size]--; #endif } /* * When a program attempts "storage compaction" as mentioned in the * old malloc man page, it realloc's an already freed block. Usually * this is the last block it freed; occasionally it might be farther * back. We have to search all the free lists for the block in order * to determine its bucket: 1st we make one pass thru the lists * checking only the first block in each; if that fails we search * ``reall_srchlen'' blocks in each list for a match (the variable * is extern so the caller can modify it). If that fails we just copy * however many bytes was given to realloc() and hope it's not huge. */ int reall_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ POINTER realloc(cp, nbytes) POINTER cp; SIZE_T nbytes; { register u_int onb; union overhead *op; char *res; register int i; int was_alloced = 0; if (cp == NULL) return (malloc(nbytes)); op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); if (op->ov_magic == MAGIC) { was_alloced++; i = op->ov_index; } else { /* * Already free, doing "compaction". * * Search for the old block of memory on the * free list. First, check the most common * case (last element free'd), then (this failing) * the last ``reall_srchlen'' items free'd. * If all lookups fail, then assume the size of * the memory block being realloc'd is the * smallest possible. */ if ((i = findbucket(op, 1)) < 0 && (i = findbucket(op, reall_srchlen)) < 0) i = 0; } onb = (1 << (i + 3)) - sizeof (*op) - RSLOP; /* avoid the copy if same size block */ if (was_alloced && nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP) { #ifdef RCHECK /* * Record new allocated size of block and * bound space with magic numbers. */ if (op->ov_index <= 13) { /* * Convert amount of memory requested into * closest block size stored in hash buckets * which satisfies request. Account for * space used per block for accounting. */ nbytes += sizeof (union overhead) + RSLOP; nbytes = (nbytes + 3) &~ 3; op->ov_size = nbytes - 1; *((u_int *)((caddr_t)op + nbytes - RSLOP)) = RMAGIC; } #endif return(cp); } if ((res = malloc(nbytes)) == NULL) return (NULL); if (cp != res) /* common optimization */ (void)bcopy(cp, res, (int)((nbytes < onb) ? nbytes : onb)); if (was_alloced) free(cp); return (res); } /* * Search ``srchlen'' elements of each free list for a block whose * header starts at ``freep''. If srchlen is -1 search the whole list. * Return bucket number, or -1 if not found. */ static findbucket(freep, srchlen) union overhead *freep; int srchlen; { register union overhead *p; register int i, j; for (i = 0; i < NBUCKETS; i++) { j = 0; for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { if (p == freep) return (i); j++; } } return (-1); } #ifdef MSTATS /* * mstats - print out statistics about malloc * * Prints two lines of numbers, one showing the length of the free list * for each size category, the second showing the number of mallocs - * frees for each size category. */ mstats(s) char *s; { register int i, j; register union overhead *p; int totfree = 0, totused = 0; fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); for (i = 0; i < NBUCKETS; i++) { for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) ; fprintf(stderr, " %d", j); totfree += j * (1 << (i + 3)); } fprintf(stderr, "\nused:\t"); for (i = 0; i < NBUCKETS; i++) { fprintf(stderr, " %d", nmalloc[i]); totused += nmalloc[i] * (1 << (i + 3)); } fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", totused, totfree); } #endif POINTER calloc(num, size) SIZE_T num; register SIZE_T size; { register void *p; size *= num; if (p = malloc(size)) memset(p, 0, size); return(p); } #endif /* lint */ innfeed-0.10.1.7.orig/misc.c0100644000175100001440000004642506331417053013742 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 17:10:18 1995 * Project: INN (innfeed) * File: misc.c * RCSId: $Id: misc.c,v 1.1.1.1 1997/04/29 16:13:31 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: * */ #if ! defined (lint) static const char *rcsid = "$Id: misc.c,v 1.1.1.1 1997/04/29 16:13:31 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (DO_HAVE_UNISTD) #include #endif #include #if USE_INNLIB #include "libinn.h" #else #include #include #endif #include "misc.h" #include "msgs.h" #include "endpoint.h" #include "tape.h" u_int openfds ; char *program ; /* this should be set to argv[0] */ int debuggingOutput ; u_int loggingLevel ; char **PointersFreedOnExit ; static void log (int level, const char *fmt, va_list args) ; int maxFds (void) { static int size = 0 ; #if USE_INNLIB size = getfdcount () ; #elif defined (FDCOUNT_GETDTAB) if (size <= 0) { if ((size = getdtablesize()) < 0) return -1; } #elif defined (FDCOUNT_GETRLIMIT) { struct rlimit rl ; if (size <= 0) { if (getrlimit(RLIMIT_NOFILE, &rl) < 0) return -1 ; size = rl.rlim_cur; } } #elif defined (FDCOUNT_SYSCONF) if (size <= 0) { if ((size = sysconf(_SC_OPEN_MAX)) < 0) return -1; } #elif defined (FDCOUNT_ULIMIT) if (size <= 0) { if ((size = ulimit(4, 0L)) < 0) return -1; } #elif defined (FDCOUNT_CONSTANT) #if defined (NOFILE) size = NOFILE ; #else size = 20 ; #endif #endif return size; } void dprintf (u_int level, const char *fmt, ...) { static pid_t myPid ; char timeString [30] ; time_t now ; va_list ap ; if (myPid == 0) myPid = getpid () ; if (loggingLevel < level) return ; now = theTime() ; strcpy (timeString, ctime (&now) + 4) ; /* strip off leading day name */ timeString [15] = '\0' ; /* strip off trailing year and newline */ va_start (ap, fmt) ; fprintf (stderr, "%s %s[%ld]: ",timeString, (program ? program : "UNKNOWN PROGRAM NAME"), (long) myPid) ; vfprintf (stderr, fmt, ap) ; va_end (ap) ; } bool debuggingDump = true ; extern void (*gPrintInfo) (void) ; void (*gCleanUp) (void) = 0 ; static void log (int level, const char *fmt, va_list args) { time_t now = time (NULL) ; char timeString [30] ; char *p = NULL ; int out ; strcpy (timeString,ctime (&now)) ; timeString [24] = '\0' ; fprintf (stderr, "%s %s: ", timeString, (program ? program : "UNKNOWN PROGRAM NAME")) ; out = vfprintf (stderr, fmt, args) ; fprintf (stderr,"\n") ; p = malloc (out + 10) ; vsprintf (p,fmt,args) ; syslog (level,p) ; } void logOrPrint (int level, FILE *fp, const char *fmt, ...) { va_list ap ; va_start (ap,fmt) ; if (fp != NULL) { vfprintf (fp,fmt,ap) ; fputc ('\n',fp) ; } else { char buffer [512] ; /* gag me */ vsprintf (buffer,fmt,ap) ; syslog (level,buffer) ; } va_end (ap) ; } void die (const char *fmt, ...) { va_list ap ; va_start (ap, fmt) ; log (LOG_ERR,fmt,ap) ; va_end (ap) ; #if SNAPSHOT_ON_DIE if (debuggingDump && gPrintInfo != NULL) gPrintInfo () ; #endif if (gCleanUp != NULL) gCleanUp () ; if (CORE_DIRECTORY != NULL) (void) chdir (CORE_DIRECTORY) ; else (void) chdir (getTapeDirectory()) ; sleep (5) ; abort () ; } void warn (const char *fmt, ...) { va_list ap ; va_start (ap, fmt) ; log (LOG_WARNING,fmt,ap) ; va_end (ap) ; } void logAndExit (int exitVal, const char *fmt, ...) { va_list ap ; va_start (ap,fmt) ; log (LOG_CRIT,fmt,ap) ; va_end (ap) ; exit (exitVal) ; } static const char * const pvt_h_errlist[] = { "Resolver Error 0 (no error)", "Unknown host", /* 1 HOST_NOT_FOUND */ "Host name lookup failure", /* 2 TRY_AGAIN */ "Unknown server error", /* 3 NO_RECOVERY */ "No address associated with name", /* 4 NO_ADDRESS */ }; static int pvt_h_nerr = (sizeof pvt_h_errlist / sizeof pvt_h_errlist[0]); /* return a friendly string for the current value of h_errno. Pinched from Stevens */ const char *host_err_str (void) { static char msgstr [200] ; if (h_errno != 0) { if (h_errno > 0 && h_errno < pvt_h_nerr) sprintf (msgstr,"(%s)", pvt_h_errlist[h_errno]) ; else sprintf (msgstr,"(herrno = %d)", h_errno) ; } else msgstr [0] = '\0' ; return msgstr ; } /* return true if the file exists and is a regular file. */ bool fileExistsP (const char *filename) { struct stat buf ; if (stat (filename,&buf) < 0) return false ; return (S_ISREG (buf.st_mode) ? true : false) ; } bool isDirectory (const char *filename) { struct stat buf ; if (stat (filename,&buf) < 0) return false ; return (S_ISDIR (buf.st_mode) ? true : false) ; } bool getNntpResponse (char *p, int *code, char **rest) { bool rval = true ; int cd = 0 ; int digits = 0 ; if (rest) *rest = 0 ; *code = 0 ; if (p == NULL) return false ; while (*p && isspace (*p)) p++ ; while (*p && isdigit (*p)) { digits++ ; cd = (cd * 10) + (*p - '0') ; p++ ; } if (digits != 3) return false ; if (*p == '-') p++ ; while (*p && isspace (*p)) p++ ; if (rest) *rest = p ; *code = cd ; return rval ; } #if 0 /* pinched from INN main source */ int writev(fd, vp, vpcount) int fd; struct iovec *vp; int vpcount; { int count; for (count = 0; --vpcount >= 0; count += vp->iov_len, vp++) if (xwrite(fd, vp->iov_base, vp->iov_len) < 0) return -1; return count; } #endif #if defined (DO_NEED_STRDUP) char *strdup (const char *string) { char *p = NULL ; if (string != NULL) { p = MALLOC (strlen (string) + 1) ; assert (p != NULL) ; strcpy (p,string) ; } return p ; } #endif /* Pull out a message id from a response on to a streaming command */ char *getMsgId (const char *p) { const char *q ; char *rval ; while (*p && isspace (*p)) p++ ; while (*p && !isspace (*p)) p++ ; /* skip response code */ while (*p && isspace (*p)) p++ ; if ( *p == '\0' ) return NULL ; q = p ; while ( *q && !isspace (*q) ) q++ ; rval = MALLOC ((size_t) (q - p + 1)) ; assert (rval != NULL) ; strncpy (rval,p,(size_t) (q - p)) ; rval [q - p] = '\0' ; return rval ; } char *findNonBlankString (char *ptr, char **tail) { char *p, *q ; for (p = ptr ; *p && isspace (*p) ; p++) /* nada */ ; if ( ! *p ) return NULL ; for (q = p ; *q && !isspace (*q) ; q++) /* nada */ ; *tail = q ; return p ; } /* strtok can't handle zero length tokens. */ char *mystrtok (char *line, const char *sep) { static char *newPoint ; char *oldline ; if (line == NULL && newPoint == NULL) return NULL ; if (line != NULL) { oldline = line ; while (*line != '\0' && strchr (sep,*line) == NULL) line++ ; if (*line == '\0') newPoint = NULL ; else { newPoint = line + 1 ; *line = '\0' ; } } else { if (newPoint == NULL) return NULL ; oldline = newPoint ; line = oldline ; while (*line != '\0' && strchr (sep,*line) == NULL) line++ ; if (*line == '\0') newPoint = NULL ; else { newPoint = line + 1 ; *line = '\0' ; } } return oldline ; } void trim_ws (char *string) { char *p ; u_int len ; assert (string != NULL) ; len = strlen (string) ; if (len == 0) return ; for (p = string + len - 1 ; p >= string && isspace (*p) ; p--) /* nada */ ; *++p = '\0' ; } #if 0 /* Scribble on top of memory we're about to free. */ void deadBeef (void *base, size_t byteCount) { unsigned char *b = (unsigned char *) base ; int i ; #if 0 memset (base, 0, byteCount) ; #else assert (b != NULL) ; for (i = 0 ; i < ((int) byteCount) - 4 ; i += 4) { #if 0 *((int *) (b + i)) = 0xdeadbeef ; #else b [i + 0] = (unsigned char) 0xde ; b [i + 1] = (unsigned char) 0xad ; b [i + 2] = (unsigned char) 0xbe ; b [i + 3] = (unsigned char) 0xef ; #endif } switch (byteCount % 4) { case 0: *(b + i + 3) = (unsigned char) 0xef ; case 3: *(b + i + 2) = (unsigned char) 0xbe ; case 2: *(b + i + 1) = (unsigned char) 0xad ; case 1: *b = (unsigned char) 0xde ; } #endif } #endif /* Not using plain flock or lockf 'cause I don't want to waste file descriptors. This routine is based on the file shlock.c from INN. */ bool lockFile (const char *fileName) { char buff [20] ; char tmpName [MAXPATHLEN], realName [MAXPATHLEN] ; char *p ; int fd, i ; pid_t pid = getpid () ; strcpy (realName,fileName) ; if ((p = strrchr (realName, '/')) != NULL) { *p = '\0' ; sprintf (tmpName, "%s/lockf%ld", realName, (long) pid) ; *p = '/' ; } else sprintf (tmpName, "lockf%ld", (long) pid) ; /* Create the temporary name for the lock file. */ while ((fd = open (tmpName, O_RDWR | O_CREAT | O_EXCL, 0644)) < 0) { switch (errno) { default: unlink (tmpName) ; syslog (LOG_ERR,NO_OPEN_LOCK,tmpName) ; return false ; case EEXIST: if (unlink (tmpName) < 0) { syslog (LOG_ERR,NO_UNLINK_LOCK,tmpName) ; return false ; } break; } } /* stick our pid in the temp file. */ sprintf (buff,"%ld\n",(long) pid) ; if (write (fd,buff,(size_t) strlen (buff)) != (int) strlen (buff)) { syslog (LOG_ERR,NO_WRITE_LOCK_PID) ; close (fd) ; unlink (tmpName) ; return false ; } close (fd) ; /* now link the real name to the temp file. */ while (link (tmpName,realName) < 0) { switch (errno) { default: /* opps. bailing out. */ syslog (LOG_ERR,NO_LINK_LOCK,realName) ; unlink (tmpName) ; return false ; case EEXIST: /* the real lock file exists. So pull out the pid in there and see if that process is still alive. */ if ((fd = open (realName,O_RDONLY)) < 0) { syslog (LOG_ERR,NO_OPEN_LOCK,realName) ; unlink (tmpName) ; return false ; } if ((i = read (fd,buff,sizeof (buff) - 1)) <= 0) { close (fd) ; unlink (tmpName) ; return false ; } close (fd) ; buff [i] = '\0' ; pid = (pid_t) atol (buff) ; if (pid <= 0) { syslog (LOG_ERR,BAD_PID,realName,buff) ; unlink (tmpName) ; return false ; } /* now send a null signal to the process named inside to see if it's still alive. */ if (kill (pid,0) == 0) { syslog (LOG_ERR,LOCK_EXISTS,realName,(int) pid) ; unlink (tmpName) ; return false ; /* process is still alive */ } /* process that took out the lock is gone */ if (unlink (realName) < 0) { syslog (LOG_ERR,NO_UNLINK_LOCK,realName) ; unlink (tmpName) ; return false ; } } } unlink (tmpName) ; return true ; } void unlockFile (const char *lockfile) { (void) unlink (lockfile) ; } #if defined (DO_NEED_STRERROR) extern int sys_nerr; extern char *sys_errlist[]; char *strerror (int errnum) { static char buff[30]; if (errnum >= 0 && errnum < sys_nerr) return sys_errlist[errnum]; (void)sprintf(buff, "Error code %d\n", errnum); return buff; } #endif /* defined (NEED_STRERROR) */ bool endsIn (const char *string, const char *tail) { size_t len = strlen (tail) ; size_t slen = strlen (string) ; if (slen < len) return false ; else if (strcmp (string + slen - len, tail) == 0) return true ; else return false ; } /* append the contents of src to dest. src is removed if append if successful */ bool appendFile (const char *dest, const char *src) { FILE *inTmp, *outTmp ; char buff [BUFSIZ] ; size_t rval ; /* append the outputFilename file to the inputFilename file */ if ((outTmp = fopen (dest, "a")) == NULL) die ("fopen (%s): %s",dest, strerror (errno)) ; if ((inTmp = fopen (src, "r")) == NULL) die ("fopen (%s): %s",src, strerror (errno)) ; while ((rval = fread (buff,sizeof (char),BUFSIZ,inTmp)) > 0) { if (fwrite (buff,sizeof (char), rval, outTmp) != rval) die ("fwrite: %s", strerror (errno)) ; } if (ferror (inTmp)) die ("Error on inTmp in newTape") ; if (ferror (outTmp)) die ("Error on outTmp in newTape") ; if (fclose (inTmp) != 0) die ("fclose (inTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ; if (fclose (outTmp) != 0) die ("fclose (outTmp): appendFile (%s,%s): %s",dest,src,strerror (errno)) ; if (unlink (src) != 0) die ("unlink (%s): %s", src, strerror (errno)) ; return true ; } /* return true if file1 is older than file2 */ bool isOlder (const char *file1, const char *file2) { struct stat buf1 ; struct stat buf2 ; if (stat (file1,&buf1) < 0) return false ; if (stat (file2,&buf2) < 0) return false ; return ((buf1.st_mtime < buf2.st_mtime) ? true : false) ; } void freeCharP (char *charp) { FREE (charp) ; } /* return the length of the file reference by the given file descriptor */ long fileLength (int fd) { struct stat buf ; if (fstat (fd,&buf) < 0) return false ; return ((long) buf.st_size) ; } const char *boolToString (bool val) { return val ? "true" : "false" ; } void addPointerFreedOnExit (char *pointerToFree) { static int totalPointers = 0 ; static int nextPointer = 0 ; if (nextPointer == 0 || nextPointer == totalPointers - 1) { register int i; totalPointers += 16 ; if (PointersFreedOnExit == NULL) PointersFreedOnExit = ALLOC (char *, totalPointers) ; else PointersFreedOnExit = REALLOC (PointersFreedOnExit, char *, totalPointers) ; ASSERT (PointersFreedOnExit != NULL) ; for (i = nextPointer; i < totalPointers; i++) PointersFreedOnExit [i] = NULL; } PointersFreedOnExit [nextPointer++] = pointerToFree ; } /* malloc a buffer and build the filename in it. */ char *buildFilename (const char *directory, const char *fname) { int len = 0 ; char *p = NULL ; if (fname == NULL) return NULL ; if (directory == NULL) directory = "." ; len = strlen (directory) + strlen (fname) + 2 ; if (len < pathMax(directory) - 2) { p = malloc (len + 1) ; p [0] = '\0' ; if (fname [0] != '/') { strcat (p,directory) ; if (p [strlen(p) - 1] != '/') strcat (p,"/") ; } strcat (p,fname) ; } return p ; } /* borrows heavily from the shrinkfile program by chongo. */ bool shrinkfile (FILE *fp, long size, char *name, const char *mode) { long currlen = ftell (fp) ; char *tmpname ; char buffer [BUFSIZ] ; FILE *tmpFp ; int c ; int i ; tmpname = malloc (pathMax(NULL) + 1) ; sprintf (tmpname,"%s.XXXXXX",name) ; mktemp (tmpname) ; if (currlen <= size) { FREE(tmpname) ; dprintf (1,"No need to shrink file (%s %ld vs %ld\n", name,size,currlen) ; return true ; } /* create a temp file that will go away when closed. */ if ((tmpFp = fopen (tmpname,"w")) == NULL) { syslog (LOG_ERR,SHRINK_TEMP_OPEN,tmpname) ; FREE (tmpname) ; return false ; } if (fseek (fp,currlen - size,SEEK_SET) != 0) { fclose (tmpFp) ; syslog (LOG_ERR,SHRINK_SEEK,currlen - size,name) ; FREE(tmpname) ; return false ; } /* find the end of the next line in the shrinking file. */ while ((c = fgetc (fp)) != '\n') if (c == EOF) { syslog (LOG_WARNING,SHRINK_NONL,name) ; fclose (tmpFp) ; fseek (fp,currlen,SEEK_SET) ; FREE(tmpname) ; return false ; } /* copy the tail of the shrinking file to the temp file. */ while ((i = fread (buffer,1,sizeof (buffer),fp)) > 0) { if (fwrite (buffer,1,i,tmpFp) != (size_t) i) { fclose (tmpFp) ; syslog (LOG_ERR,SHRINK_WRITETMP,tmpname) ; fseek (fp,currlen, SEEK_SET) ; FREE(tmpname) ; return false ; } } if (i < 0) logAndExit (1,SHRINK_READ,name) ; fclose (tmpFp) ; if (unlink (name) != 0) logAndExit (1,UNLINK_FAILED,name) ; /* we're in the same directory so this is ok. */ if (rename (tmpname,name) != 0) logAndExit (1,RENAME_FAILED,tmpname,name) ; if (freopen (name,mode,fp) != fp) logAndExit (1,SHRINK_FREOPEN,name) ; fseek (fp,0,SEEK_END) ; size = ftell (fp) ; syslog (LOG_WARNING,FILE_SHRUNK,name,currlen,size) ; FREE(tmpname) ; return true ; } long pathMax (const char *pathname) { static long rval = 0 ; (void) pathname ; if (rval > 0) return rval ; #if defined (PATH_MAX) rval = PATH_MAX ; #elif defined (_POSIX_PATH_MAX) rval = _POSIX_PATH_MAX ; #elif defined (DO_HAVE_PATHCONF) && defined (_PC_PATH_MAX) if (pathname == NULL) pathname = "/tmp" ; rval = pathconf (pathname,_PC_PATH_MAX) ; #else rval = 255 ; if (!logged) { syslog (LOG_ERR,NO_PATH_MAX,rval) ; logged = true ; } #endif return rval ; } innfeed-0.10.1.7.orig/misc.h0100644000175100001440000001461106364204662013745 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Nov 29 23:44:47 1995 * Project: INN (innfeed) * File: misc.h * RCSId: $Id: misc.h,v 1.3 1997/07/19 18:38:42 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: * */ #if ! defined ( misc_h__ ) #define misc_h__ #include "config.h" #include #if defined (DO_NEED_BOOL) typedef enum { false = 0, true = 1 } bool ; #endif /* These typedefs are all here because C is too stupid to let me multiply define typedefs to the same things (as C++ will). Hence I can't redeclare the typedefs to get around recursive header file includes (like host.h and connection.h would need if they contained their own typedefs). */ typedef struct article_s *Article ; /* see article.h */ typedef struct buffer_s *Buffer ; /* see buffer.h */ typedef struct commander_s *Commander ; /* see commander.h */ typedef struct config_s *Config ; /* see config.h */ typedef struct connection_s *Connection ; /* see connection.h */ typedef struct endpoint_s *EndPoint ; /* see endpoint.h */ typedef struct host_s *Host ; /* see host.h */ typedef struct innlistener_s *InnListener ; /* see innlistener.h */ typedef struct tape_s *Tape ; /* see tape.h */ typedef int TimeoutId ; /* see endpoint.h */ typedef enum { /* see endpoint.h */ IoDone, IoIncomplete, IoFailed, IoEOF, IoProgress } IoStatus ; typedef void (*EndpRWCB) (EndPoint e, /* see endpoint.h */ IoStatus i, Buffer *b, void *d) ; typedef void (*EndpTCB) (TimeoutId tid, void *d) ; /* see endpoint.h */ typedef void (*EndpWorkCbk) (EndPoint ep, void *data) ; #if defined (DO_NEED_U_INT) typedef unsigned long u_long ; typedef unsigned int u_int ; typedef unsigned short u_short ; #endif /* debugging information */ extern char *program ; extern u_int loggingLevel ; /* if 0 then dprintf is a no-op */ /* the current count of file desccriptors */ extern u_int openfds ; /* return the maximum number of fds this process can have. */ int maxFds (void) ; /* if level <= loggingLevel then print */ void dprintf (u_int level, const char *fmt, ...) __attribute__ ((__format__ (printf, 2, 3))); /* for the gethostbyname() error code */ const char *host_err_str (void) ; /* parse a reponse line into it's code and body. *rest will end up pointing into the middle of p */ bool getNntpResponse (char *p, int *code, char **rest) ; /* parse out the first field of a nntp response code as a message-id. Caller must free the return value when done. */ char *getMsgId (const char *p) ; /* pick out the next non-blank string inside PTR. TAIL is set to point at the first blank (or NULL) after the string. Returns a pointer into PTR */ char *findNonBlankString (char *ptr, char **tail) ; /* if fp is not NULL then print to it, otherwise syslog at the level. */ void logOrPrint (int level, FILE *fp, const char *fmt, ...) __attribute__ ((__format__ (printf, 3, 4))); /* print the arguments like printf to stderr then abort() */ void die (const char *fmt, ...) __attribute__ ((__format__ (printf, 1, 2))); /* print the arguments like printf to stderr. */ void warn (const char *fmt, ...) __attribute__ ((__format__ (printf, 1, 2))); void logAndExit (int exitVal, const char *fmt, ...) __attribute__ ((__format__ (printf, 2, 3))); /* return true of the file exists and is a regular file */ bool fileExistsP (const char *filename) ; /* return true if file exists and is a directory */ bool isDirectory (const char *filename) ; char *mystrtok (char *string, const char *sep) ; /* remove trailing whitespace */ void trim_ws (char *string) ; #if defined (DO_NEED_STRDUP) char *strdup (const char *s) ; #endif /* locks the peer and returns true or returns false */ bool lockPeer (const char *peerName) ; /* return true if the end of string matches tail. */ bool endsIn (const char *string, const char *tail) ; /* scribble over then free up the null-terminated string */ void freeCharP (char *charp) ; /* append the contents of src to dest. src is removed if append if successful */ bool appendFile (const char *dest, const char *src) ; /* return the length of the file reference by the given file descriptor */ long fileLength (int fd) ; bool lockFile (const char *fileName) ; void unlockFile (const char *lockfile) ; /* return true if file1 is older than file2 */ bool isOlder (const char *file1, const char *file2) ; /* converts val into a printable string */ const char *boolToString (bool val) ; /* memory leak checker helper. */ void addPointerFreedOnExit (char *pointerToFree) ; /* splice direcotory and fname together and return free'able string */ char *buildFilename (const char *directory, const char *fname) ; /* string the file opened by FP to the give SIZE. The NEWNAME is the name of the file to have after truncation. FP will be reopened for writing on the new file and will be positioned at the end */ bool shrinkfile (FILE *fp, long size, char *name, const char *mode) ; /* max length of pathname */ long pathMax (const char *pathname) ; #define ASSERT(x) do{if (!(x))die("assertion -- %s -- failed in file %s line %d",#x,__FILE__,__LINE__);}while(0) #define INDENT_INCR 5 #define INDENT_BUFFER_SIZE 80 #if ! defined (MIN) #define MIN(A,B) ((A) < (B) ? (A) : (B)) #endif #endif /* misc_h__ */ innfeed-0.10.1.7.orig/msgs.h0100644000175100001440000003406006364204664013765 0ustar mdusers/* -*- c -*- * * Author: James A. Brister -- berkeley-unix -- * Start Date: Sun, 28 Jan 1996 18:37:49 +1100 * Project: INN -- innfeed * File: msgs.h * RCSId: $Id: msgs.h,v 1.7 1997/07/19 18:38:44 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: * */ #if ! defined ( msgs_h__ ) #define msgs_h__ /* Messages used in connection.c */ #define ARTICLE_TIMEOUT_MSG "%s:%d idle tearing down connection" #define ARTICLE_TIMEOUT_W_Q_MSG "%s:%d idle connection still has articles" #define CONNECT_ERROR "%s:%d connect : %m" #define CONNECTED "%s:%d connected" #define CXN_CLOSED "%s:%d closed" #define CXN_PERIODIC_CLOSE "%s:%d periodic close" #define CXN_REOPEN_FAILED "%s:%d flush re-connect failed" #define CXN_BUFFER_EXPAND_ERROR "%s:%d cxnsleep can't expand input buffer" #define FCNTL_ERROR "%s:%d cxnsleep can't set socket non-blocking: %m" #define IHAVE_WRITE_FAILED "%s:%d cxnsleep can't write IHAVE body : %m" #define MODE_STREAM_FAILED "%s:%d cxnsleep can't write MODE STREAM : %m" #define QUIT_WRITE_FAILED "%s:%d cxnsleep can't write QUIT : %m" #define COMMAND_WRITE_FAILED "%s:%d cxnsleep can't write command : %m" #define RESPONSE_READ_FAILED "%s:%d cxnsleep can't read response : %m" #define BANNER_READ_FAILED "%s:%d cxnsleep can't read banner : %m" #define SOCKET_CREATE_ERROR "%s:%d cxnsleep can't create socket : %m" #define MODE_WRITE_PENDING "%s:%d cxnsleep mode stream command still pending" #define NOMSGID "%s:%d cxnsleep message-id missing in reponse code %d: %s" #define INVALID_MSGID "%s:%d cxnsleep message-id invalid message-id in reponse code %d : %s" #define UNKNOWN_RESPONSE "%s:%d cxnsleep response unknown : %d : %s" #define UNKNOWN_BANNER "%s:%d cxnsleep response unknown banner %d : %s" #define BAD_RESPONSE "%s:%d cxnsleep response unexpected : %d" #define BAD_MODE_RESPONSE "%s:%d cxnsleep response to MODE STREAM : %s" #define INVALID_RESP_FORMAT "%s:%d cxnsleep response format : %s" #define PREPARE_READ_FAILED "%s:%d cxnsleep prepare read failed" #define RESPONSE_TIMEOUT "%s:%d cxnsleep non-responsive connection" #define WRITE_TIMEOUT "%s:%d cxnsleep write timeout" #define GETSOCKOPT_FAILED "%s:%d cxnsleep internal getsockopt : %m" #define CONNECTION_FAILURE "%s:%d cxnsleep connect : %m" #define IO_FAILED "%s:%d cxnsleep i/o failed : %m" #define NO_TRANSFER_NNRPD "%s:%d cxnsleep transfer permission denied" #define NO_TALK_NNRPD "%s:%d cxnsleep no permission to talk: %s" #define CXN_BAD_STATE "%s:%d cxnsleep connection in bad state: %s" #define CXN_STREAM_RESP "%s:%d cxnsleep unexpected streaming response for non-streaming connection: %s" #define CXN_NONSTREAM_RESP "%s:%d cxnsleep unexpected non-streaming response for streaming connection: %s" #define MISSING_ART_IHAVE_BODY "%s:%d missing article for IHAVE-body" #define NOCR_MSG "%s:%d remote not giving out CR characters" #define PREPARE_WRITE_FAILED "%s:%d fatal prepare write failed" #define QUIT_WHILE_WRITING "%s:%d internal QUIT while write pending" #define CXN_BLOCKED "%s:%d remote cannot accept articles: %s" /* key word is "checkpoint" or "final" */ #define STATS_MSG "%s:%d %s seconds %ld offered %d accepted %d refused %d rejected %d" /* messages used in host.c */ #define NO_STATUS "ME oserr status file open: %s : %m" #define CONNECTION_DISAPPEARING "%s:%d connection vanishing" #define STREAMING_MODE_SWITCH "%s mode no-CHECK entered [%.2f,%.2f,%.2f]" #define STREAMING_MODE_UNDO "%s mode no-CHECK exited [%.2f,%.2f,%.2f]" #define REALLY_FINAL_STATS "%s global seconds %ld offered %d accepted %d refused %d rejected %d missing %d" #define PROCESS_FINAL_STATS "ME global seconds %ld offered %ld accepted %ld refused %ld rejected %ld missing %ld" /* key word in next two is "checkpoint" or "final" */ #define HOST_STATS_MSG "%s %s seconds %ld offered %d accepted %d refused %d rejected %d missing %d spooled %d on_close %d unspooled %d deferred %d requeued %d queue %.1f/%d:%.0f,%.0f,%.0f,%.0f,%.0f,%.0f" #define HOST_SPOOL_STATS "%s %s seconds %ld spooled %d on_close %d sleeping %d" #define HOST_MAX_CONNECTIONS "%s hostChkCxns - maxConnections was %d now %d" #define REMOTE_BLOCKED "%s remote cannot accept articles initial : %s" #define REMOTE_STILL_BLOCKED "%s remote cannot accept articles still : %s" #define CHANGED_REMOTE_BLOCKED "%s remote cannot accept articles change : %s" #define SPOOLING "%s spooling no active connections" #define BACKLOG_TO_TAPE "%s spooling some backlog." #define SPOOL_DEFERRED "%s spooling deferred articles" #define REMOTE_DOES_STREAMING "%s remote MODE STREAM" #define REMOTE_STREAMING_OFF "%s remote MODE STREAM disabled" #define REMOTE_NO_STREAMING "%s remote MODE STREAM failed" #define STREAMING_CHANGE "%s remote MODE STREAM change" #define HOST_RESOLV_ERROR "%s can't resolve hostname: %s : %s" /* messages used in innlistener.c */ #define INN_GONE "ME source lost . Exiting" #define INN_IO_ERROR "ME source read error Exiting : %m" #define INN_BAD_CMD "ME source format bad Exiting : %s" #define TOO_MANY_HOSTS "ME internal too may hosts. (max is %d)" #define DYNAMIC_PEER "ME unconfigured peer %s added" #define UNKNOWN_PEER "ME unconfigured peer %s" #define STOPPING_PROGRAM "ME finishing at %s" #define FAST_EXIT_PROGRAM "ME finishing (quickly) at %s" #define NO_DROPPED_FILE "ME cant open %s: loosing articles: %m" #define NO_NULL_FILE "ME cant open /dev/null: %m" #define LOST_ARTICLE_COUNT "ME lost %ld articles" #define DROPPED_ARTICLE_COUNT "ME dropped %ld articles" #define DROPPED_LOCATION "ME dropping articles into %s" #define L_BUFFER_EXPAND_ERROR "ME error expanding input buffer" #define L_PREPARE_READ_FAILED "ME error prepare read failed" #define SHRINK_TEMP_OPEN "ME error opening temp shrink file %s: %m" #define SHRINK_SEEK "ME error seeking to point %ld in %s" #define SHRINK_NONL "ME no newline in shrinking file %s" #define SHRINK_WRITETMP "ME fwrite failed to temp shrink file %s: %m" #define SHRINK_READ "ME fread failed on file %s: %m" #define SHRINK_FREOPEN "ME freopen on shrink file failed %s %m" #define SHRINK_WRITEORIG "ME fwrite failed to shrink file %s %m" /* messages used in commander.c */ #define CMDR_PREP_RD_FAILED "ME commander error listen failed" #define NO_CONNECT "ME commander connections disallowed" #define COMMANDER_CONNECT "ME commander connect %s" /* endpoint.c */ #define NO_FD_FREE "ME cant move fd %d above %d" #define CLOSE_FAILED "ME oserr close (%d): %m" #define DUP2_FAILED "ME oserr dup2 (%d, %d): %m" #define FD_TOO_BIG "ME fd (%d) looks too big (%d -- %s)" #define BAD_SELECT "ME exception: select failed: %d %m" #define GETSOCKOPT_FAILURE "ME exception: getsockopt (%d): %m" #define EXCEPTION_NOTICE "ME exception: fd %d : %m" #define UNKNOWN_EXCEPTION "ME exception: fd %d : Unknown error." /* article.c */ #define EMPTY_ARTICLE "ME article 0 bytes: %s" #define DOUBLE_NAME "ME two filenames for same article: %s: %s" #define BAD_ART_READ "ME article read-error : %s : %m" #define NO_ARTICLE "ME article missing : %s : %s" #define FSTAT_FAILURE "ME oserr fstat %s : %m" #define REGFILE_FAILURE "ME article file-type : %s" #define PREPARED_NEWLINES "ME newline to file size ratio: %0.2f (%d/%d)" #define PREPARE_FAILED "ME internal failed to prepare buffer for NNTP" #define ACTIVE_ARTICLES "ME articles active %d bytes %d" #define ARTICLE_ALLOCS "ME articles total %d bytes %d" #define MAX_BYTES_LIMIT "ME exceeding maximum article byte limit: %d (max), %d (cur)" #define MMAP_FAILURE "ME mmap failure %s: %m" #define MUNGED_ARTICLE "ME munged article %s" /* main.c */ /* first %s is "innfeed v a.b.c" second is ctime fmt */ #define STARTING_PROGRAM "ME starting %s at %s" #define NOSUCH_CONFIG "ME config aborting No such config file: %s" #define FOPEN_FAILURE "ME fopen %s : %m" #define CFG_FOPEN_FAILURE "ME config aborting fopen %s : %m" #define PARSE_FAILURE "ME config aborting Error parsing config file" #define NO_HOST "ME locked cannot setup peer %s" #define NO_X_AND_S "ME usage aborting Can't use both '-s' and '-x'" #define SETRLIM_FAILED "ME oserr setrlimit(RLIM_NOFILE,%ld): %m" #define GETRLIM_FAILED "ME oserr getrlimit(RLIM_NOFILE): %m" #define PIPE_FAILURE "ME fatal pipe: %m" #define FORK_FAILURE "ME fatal fork: %m" #define CD_FAILED "ME fatal chdir %s : %m" #define SHUTDOWN_SIGNAL "ME received shutdown signal" #define CONFIG_RELOAD "ME reloading config file %s" #define INCR_LOGLEVEL "ME increasing logging level to %d" #define DECR_LOGLEVEL "ME decreasing logging level to %d" #define NO_PATH_MAX "ME cant determine PATH_MAX. Guessing %ld" #define IGNORE_SIGALRM "ME signal SIGALRM in non-funnel-file mode ignored." /* misc.c */ #define NO_OPEN_LOCK "ME lock file open: %s : %m" #define NO_UNLINK_LOCK "ME lock file unlink: %s : %m" #define NO_LINK_LOCK "ME lock file link: %s : %m" #define NO_WRITE_LOCK_PID "ME lock file pid-write: %m" #define BAD_PID "ME lock bad-pid info in %s : %s" #define LOCK_EXISTS "ME lock in-use already: %s by pid %d" #define FILE_SHRUNK "ME file %s shrunk from %ld to %ld" /* tape.c */ #define FSTAT_NE_FTELL "ME fstat and ftell do not agree" #define NO_FACTOR "%s no backlog-factor or backlog-high-limit" #define NEW_HAND_FILE "%s new hand-prepared backlog file." #define TAPE_OPEN_FAILED "ME tape open failed (%s) %s: %m" #define FILE_SHORT "ME tape short : %s %ld %ld" #define NO_LOCK_TAPE "ME lock failed for host: %s" #define TAPE_INPUT_ERROR "ME ioerr on tape file: %s : %m" #define FCLOSE_FAILED "ME ioerr fclose %s : %m" #define UNLINK_FAILED "ME oserr unlink %s : %m" #define RENAME_FAILED "ME oserr rename %s,%s : %m" #define FTELL_FAILED "ME oserr ftell %s : %m" #define FGETS_FAILED "ME oserr fgets %s : %m" #define FSEEK_FAILED "ME oserr fseek %s,%ld,SEEK_SET : %m" #define CHECKPOINT_OPEN "ME oserr open checkpoint file: %s %m" #define BAD_CHECKPOINT "ME internal bad data in checkpoint file: %s" #define CKPT_BNDRY "ME internal checkpoint line boundary missed: %s %ld vs. %ld" #define FLUSHING_TAPES "ME flushing tapes" #define ROTATING_HAND_DROPPED "%s grabbing external tape file" #define ADDMISSINGBOOL "ME config: adding missing key/value %s: %s" #define ADDMISSINGINT "ME config: adding missing key/value %s: %ld" #define ADDMISSINGREAL "ME config: adding missing key/value %s: %f" #define BADSPOOL_CHANGE "ME config: definition of news-spool (%s) is a non-existant directory. Using %s" #define BAD_TAPEDIR_CHANGE "ME config: definition of backlog-directory (%s) is a non-existant directory. Using %s" #define BATCH_AND_NO_CXNS "%s config ignored batch mode with initial connection count of 0" #define CONFIG_PARSE_FAILED "%sconfig file error: %s\n" #define INT_TO_HIGH "ME config: value of %s (%ld) in %s is higher than maximum of %ld. Using %ld" #define INT_TO_LOW "ME config: value of %s (%ld) in %s is lower than minimum of %ld. Using %ld" #define INT_TOO_LARGE "ME config: value of %s (%ld) in %s too large. Using %ld" #define REALTOHIGH "ME config: value of %s (%f) is higher than maximum of %f" #define REALTOLOW "ME config: value of %s (%f) is lower than minimum of %f" #define LESS_THAN_ONE "ME config: value of %s (%ld) in %s cannot be less than 1. Using %ld" #define LESS_THAN_ZERO "ME config: value of %s (%ld) in %s cannot be less than 0. Using %ld" #define NO_PEER_FIELD_BOOL "ME config: no value for %s in peer %s. Using %s" #define NO_PEER_FIELD_INT "ME config: no value for %s in peer %s. Using %ld" #define NO_PEER_FIELD_REAL "ME config: no value for %s in peer %s. Using %.2f" #define DUP_PEER_NAME "ME config: two peers with the same name: %s" #define NODEFN "ME config: no definition for required key %s" #define NOTBOOLEAN "ME config: value of %s is not a boolean" #define NOTINT "ME config: value of %s is not an integer" #define NOTREAL "ME config: value of %s is not a floating point number" #define NOCK_LOWHIGHCLOSE "ME config: no-check-low and no-check-high are close together (%f vs %f)" #define NOCK_LOWVSHIGH "ME config: no-check-low value greater than no-check-high (%f vs %f). Setting to %f and %f" #define NOFACTORHIGH "ME config: must define at least one of backlog-factor and backlog-limit-high. Adding %s: %f" #define NO_CHANGE_BACKLOG "ME config: cannot change backlog-directory of a running process" #define NO_STDIO_FDMAX "ME config: cannot define stdio-fdmax if FD_SETSIZE is not available" #define NO_TAPE_DIR "ME config: no usable value for backlog-directory" #define SPOOL_NODEF "ME config: no definition of news-spool, and %s is no good" #endif innfeed-0.10.1.7.orig/procbatch.pl0100644000175100001440000001372706331417055015146 0ustar mdusers#!/usr/bin/perl # # Author: James Brister -- berkeley-unix -- # Start Date: Thu May 16 10:32:02 1996 +0200 # Project: INN -- innfeed # File: procbatch.pl # RCSId: $Id: procbatch.pl,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ # # Copyright: Copyright (c) 1996 by Internet Software Consortium # # Permission to use, copy, modify, and distribute this # software for any purpose with or without fee is hereby # granted, provided that the above copyright notice and this # permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE # CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS # SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET # SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE # USE OR PERFORMANCE OF THIS SOFTWARE. # # Description: Take a file of the form generated by innd in the out.going # directory ("Wnm*") and separate it into tape files for inn. # # Thanks to Clayton O'Neill for serious speed # improvments. # # If you have inn 1.5 or higher, then set $innshellvars to point at the # installed innshellvars.pl file. $innshellvars = "/var/news/etc/innshellvars.pl" ; if ( -f $innshellvars ) { require $innshellvars ; $spoolTop = $inn'spool ; #' # if $inn'batch is /var/news/spool/out.going then set tapeDir to # /var/news/spool/innfeed $tapeDir = $inn'batch ; #' $tapeDir =~ s!(.*)/.*!$1! ; $tapeDir .= "/innfeed" ; $destDir = $inn'spooltemp ; #' } else { # the top of the article spool. In this directory are alt, comp etc. # Overriden with the ``-s'' option. $spoolTop = "/var/news/spool/articles" ; # The directory where innfeed stores its backlog files. # Overriden with the ``-t'' option. $tapeDir = "/var/news/spool/innfeed" ; # Where the files get written to before possibly being moved to the # tape directory. The ``-d'' option overrides $destDir = "/var/tmp" ; } ## ## Everything below here should probably be left alone. ## $0 =~ s!.*/!! ; require 'getopts.pl' ; $usage = "$0 [ -q ][ -v ][ -u ][ -e host ][ -d dir ][ -c [ -s dir ]][ -m [-t dir ]] inn-batchfile\n -e host to process on entries for only that host -d dir to put the output file(s) in that directory ($destDir) -c to check pathnames of articles before storing them -s dir to specify the top of the news spool tree ($spoolTop) -m to have $0 move the new files to the backlog directory. -t dir to specify the backlog directory ($tapeDir) -u to unlink the input files when finished -v for verbosity -q quiet mode; only display error messages. Good for cron jobs. $0 will take an inn funnel file (normally a file in /var/spool/news/out.going), or an innfeed ``dropped'' file, which is presumed to be of the format: pathname message-id peer1 peer2 peer3 ... and will break it up into files peer1.tmp peer2.tmp peer3.tmp... Each of these files wil be of the format: pathname message-id that is the same as innfeed's backlog file format. Simply rename these files to peer1 peer2 peer3 in a running innfeed's backlog directory and they will be picked up automatically and processed by innfeed. Use the '-m' flag and they'll be moved automatically. " ; $opt_u = $opt_h = ""; # shut up, perl -w &Getopts ("he:t:s:d:cvumq") || die $usage ; die $usage if ( $opt_h ) ; die "Cannot specify both -q and -v\n\n" . $usage if ($opt_q && $opt_v); $spoolTop = $opt_s if $opt_s ; $destDir = $opt_d if $opt_d ; $tapeDir = $opt_t if $opt_t ; $inputFile = shift ; die $usage if !$inputFile ; unless (-f $inputFile) { exit if $opt_q; die "No such file: $inputFile\n\n" . $usage; } die "No such directory: $spoolTop\n\n" . $usage if ( ! -d $spoolTop && $opt_c ) ; die "No such directory: $destDir\n\n" . $usage if ( ! -d $destDir ) ; die "No such directory: $tapeDir\n\n" . $usage if ( ! -d $tapeDir && $opt_m ) ; print "Using $inputFile\n" if $opt_v ; open (INPUT,"<$inputFile") || die "$0: open ($inputFile): $!\n" ; while () { chop ; @F = split ; # Check the format of the line vigorously next unless (m!^\S+/\d+ <.+@.+> \S+!) ; if ( $opt_c ) { if ( ! -f "$spoolTop/$F[0]" ) { $missing++ ; print "Dropping file: $spoolTop/$F[0]\n" if $opt_v ; next ; } } for ($i = 2 ; $i <= $#F ; $i++) { $host = $F[$i] ; next if ($opt_e && $opt_e ne $host) ; # Keep out host names with any funny characters (from # corrupted files) if ($host !~ /^[-\._0-9A-Za-z]+$/) { warn "$0: bad site name ignored: \"$host\"\n"; next; } if ($hosts{$host}) { print {$hosts{$host}} "$F[0] $F[1]\n"; } else { $outputFile = "$destDir/$host.tmp" ; print "Starting $host\n" if ($opt_v); $hosts{$host}=$host; open ($hosts{$host},">>$outputFile") || die "open >>$outputFile: $!\n" ; print {$hosts{$host}} "$F[0] $F[1]\n"; } } } close (INPUT) ; foreach $host (keys %hosts) { close($hosts{$host}); $outputFile = "$destDir/$host.tmp" ; $tmpTape = "$tapeDir/$host.tmp" ; $tapeFile = "$tapeDir/$host" ; if ( $opt_m ) { if ($outputFile ne $tmpTape) { $cmd = "mv $outputFile $tmpTape" ; system ($cmd) ; die "$0: $cmd: failed\n" unless ($? == 0) ; } $cmd = "cat $tmpTape |sort -u >> $tapeFile && rm -f $tmpTape" ; system ($cmd) ; die "$0: $cmd: failed\n" unless ($? == 0) ; } } unlink($inputFile) if ($opt_u); print "$missing articles dropped\n" if ( $opt_v && $missing > 0 ) ; innfeed-0.10.1.7.orig/startinnfeed.c0100644000175100001440000000510306340727324015466 0ustar mdusers/* * $Id: startinnfeed.c,v 1.2 1997/05/22 02:26:28 scrappy Exp $ * * Start innfeed and pass it all the arguments given. Sets up process * limits for innfeed. */ #if ! defined (USER) #define USER "news" #endif #if ! defined (INNFEED) #define INNFEED "/usr/news/local/innfeed" #endif #include /* getpwent */ #include /* fprintf */ #include /* errno, sys_errlist */ #include /* setgid, setuid, execve */ #include /* exit */ #include /* setrlimit */ #include /* setrlimit */ #include /* setrlimit */ #include /* setrlimit */ #include static const char *innfeed = INNFEED; void main(int ac, char **av, char **ep) { struct passwd *pwd; struct rlimit rl; char *progname; /* (try to) unlimit datasize and stacksize for us and our children */ rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_DATA, &rl) == -1) (void)fprintf(stderr, "%s: setrlimit(RLIMIT_DATA, RLIM_INFINITY): %s\n", *av, strerror(errno)); if (setrlimit(RLIMIT_STACK, &rl) == -1) (void)fprintf(stderr, "%s: setrlimit(RLIMIT_STACK, RLIM_INFINITY): %s\n", *av, strerror (errno)); if (setrlimit(RLIMIT_NOFILE, &rl) == -1) (void)fprintf(stderr, "%s: setrlimit(RLIMIT_NOFILE, RLIM_INFINITY): %s\n", *av, strerror (errno)); /* stop being root */ pwd = getpwnam(USER); if (pwd == (struct passwd *)NULL) (void)fprintf(stderr, "%s: getpwnam(%s): %s\n", *av, USER, strerror (errno)); else if (setgid(pwd->pw_gid) == -1) (void)fprintf(stderr, "%s: setgid(%d): %s\n", *av, pwd->pw_gid, strerror (errno)); else if (setuid(pwd->pw_uid) == -1) (void)fprintf(stderr, "%s: setuid(%d): %s\n", *av, pwd->pw_uid, strerror (errno)); else { char **evp = NULL ; progname = av[0]; av[0] = (char *) innfeed; #if defined (USE_DMALLOC) { int i ; for (i = 0 ; ep[i] != NULL ; i++) /* nada */ ; evp = (char **) malloc (sizeof (char *) * i + 2) ; for (i = 0 ; ep[i] != NULL ; i++) evp [i] = ep [i] ; evp [i] = "DMALLOC_OPTIONS=debug=0x4e405c3,inter=100,log=innfeed-logfile"; evp [i+1] = NULL ; } #else evp = ep ; #endif if (execve(innfeed, av, evp) == -1) (void)fprintf(stderr, "%s: execve: %s\n", progname, strerror (errno)); } exit(1); } innfeed-0.10.1.7.orig/sysconfig.h0100644000175100001440000002522106400214370015001 0ustar mdusers/* -*- c -*- * * Author: James A. Brister -- berkeley-unix -- * Start Date: Thu, 01 Feb 1996 22:17:51 +1100 * Project: INN -- innfeed * File: sysconfig.h * RCSId: $Id: sysconfig.h,v 1.5 1997/08/25 05:32:40 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: This file should be the only thing you need to touch when * moving innfeed across platforms. * * This file is broken into two sections. The first contains * all defines etc. that are private to innfeed. The second * contains defines etc. that identical to those in inn. By * compiling with '-DUSE_INN_INCLUDES' and an appropriate '-I' * you won't actually need to touch this second section. * */ /* PLEASE FORWARD ANY CHANGES YOU HAVE TO MAKE TO AND LET ME KNOW WHAT, WHY AND FOR WHAT PLATFORM. THANKS. */ #ifndef sysconfig_h__ #define sysconfig_h__ 1 /*************************************************************************** ** INNFEED PRIVATE SECTION ** ***************************************************************************/ /* ** BSD/OS */ #if defined (__bsdi__) #define MAX_WRITEV_VEC 1024 #define HAVE_MMAP #endif /* ** FreeBSD */ #if defined (__FreeBSD__) #define MAX_WRITEV_VEC 1024 #define HAVE_MMAP #endif /* ** NetBSD ** */ #if defined (__NetBSD__) #define MAX_WRITEV_VEC 1024 #define HAVE_MMAP #endif /* ** LINUX */ #if defined (linux) /* Note: If you are running version 5.4.3 or better of libc, then 16 is the number to use. Lower than that, and you should use 1. */ #define MAX_WRITEV_VEC 1 #undef DO_NEED_SYS_SELECT #define HAVE_MMAP 1 #define NBIO_FCNTL 1 #define USE_SIGACTION #endif /* ** DEC Unix ** */ #if defined (__osf__) #define GETSOCKOPT_ARG char * #define MAX_WRITEV_VEC 1024 #endif /* ** SOLARIS ** */ #if defined (SOLARIS) /* #define DO_NEED_STRERROR 1 */ /* this is needed for version < 2.5 */ #define GETSOCKOPT_ARG char * #define MAX_WRITEV_VEC 16 #define DO_NEED_STREAM 1 #define HAVE_MMAP #define MAX_STDIO_FD 256 /* this may be needed for solaris version < 2.6 */ /* #define wait3(a,b,c) waitpid(-1,a,b) */ #endif /* defined (SOLARIS) */ /* ** SunOS 4.x */ #if defined (sun) && ! defined (SOLARIS) #define MAX_WRITEV_VEC 16 #define DO_NEED_STRERROR 1 #define HAVE_MMAP #define MAX_STDIO_FD 128 #define atexit(arg) on_exit (arg,0) #endif /* ** IRIX ** */ #if defined (__sgi) #define MAX_WRITEV_VEC 16 /* actually bigger on 5.2, but this on 5.3 and 6.x */ #define HAVE_MMAP #endif /* ** NEC UX/4800 ** */ #if defined (_nec_ews) #define DO_NEED_STRDUP 1 #define MAX_WRITEV_VEC 16 #define DO_NEED_STREAM 1 #define NO_SBRK 1 #endif /* ** AIX 3.2.x, 4.1.3 ** ** NOTE FOR 3.2.x. Be careful ! GCC ONLY !! If you compile ** innfeed 0.9.1 with AIX cc, machine will be rebooted during building ** connection.o. You can't believe it but I tested it with two different ** AIX 3.2.5 boxes 10 times !! :) ** 1996/11/10, seokchan lee */ #if defined (_AIX32) #define DO_NEED_STREAM 1 #define DONT_USE_UNION_WAIT 1 #define FDCOUNT_GETDTAB 1 #define MAX_WRITEV_VEC 16 #define UIO_MAXIOV 16 #define GETSOCKOPT_ARG char * /* If UIO_MAXIOV not defined, AIX 4.1.x defines UIO_MAXIOV as 1024. (see /usr/include/sys/socket.h). But "make check-maxiov" says it should be 16 and it works. There is no UIO_MAXIOV predefines for 3.2.x */ #define LOG_PERROR 0 #define _BSD 44 #endif #if ! defined (USE_SIGSET) && ! defined(USE_SIGVEC) #define USE_SIGACTION #endif /* Defaults below here. If you need to change something it should really be done in the architecture specific section just above. */ /* If you compiler doesn't support `volatile' then add a line like #define VOLATILE above */ #if ! defined (VOLATILE) #define VOLATILE volatile #endif /* Some broken system (all SunOS versions) have a lower limit for the maximum number of stdio files that can be open, than the limit of open file the OS will let you have. If this value is > 0 (and ``stdio-fdmax'' is *not* used in the config file), then all non-stdio file descriptors will be kept above this value (by dup'ing them). */ #if ! defined (MAX_STDIO_FD) #define MAX_STDIO_FD 0 #endif /* define DONT_NEED_SYS_SELECT or DO_NEED_SYS_SELECT depending on whether endpoint.c fails to compile properly and you have /usr/include/sys/select.h */ #if ! defined (DO_NEED_SYS_SELECT) && !defined (DONT_NEED_SYS_SELECT) #define DONT_NEED_SYS_SELECT 1 #endif /* define DONT_NEED_STRDUP or DO_NEED_STRDUP depending on if you a strdup on your machine or not. */ #if ! defined (DO_NEED_STRDUP) && ! defined (DONT_NEED_STRDUP) #define DONT_NEED_STRDUP 1 #endif /* define DONT_NEED_U_INT or DO_NEED_U_INT depending on if you have a `u_long', `u_int', `u_short' in your system include path or not */ #if ! defined (DO_NEED_U_INT) && ! defined (DONT_NEED_U_INT) #define DONT_NEED_U_INT 1 #endif /* define DONT_NEED_BOOL or DO_NEED_BOOL depending on if you have a `bool' in your include path or not */ #if ! defined (DO_NEED_BOOL) && ! defined (DONT_NEED_BOOL) #define DO_NEED_BOOL 1 #endif /* maximum number of struct iovec in a writev. Build and run the program uio_maxiov to determine the proper value to use here (or look for the define for UIO_MAXIOV in your system's include file).. Please send me value you determined and the appropriate CPP symbols to test for your system. 16 is the smallest number I've come across yet. Having a number that's too small won't break anything. Having a number that's too big will. */ #if ! defined (MAX_WRITEV_VEC) #define MAX_WRITEV_VEC 16 #endif /* Defined DO_NEED_STREAM or DONT_NEED_STREAM depending on if you need to include included (Solaris and other SVR4(?)) */ #if ! defined (DO_NEED_STREAM) && ! defined (DONT_NEED_STREAM) #define DONT_NEED_STREAM 1 #endif /* Define DONT_NEED_STRERROR or DO_NEED_STRERROR depending on if you have strerror() in your libraries */ #if ! defined (DO_NEED_STRERROR) && ! defined (DONT_NEED_STRERROR) #define DONT_NEED_STRERROR 1 #endif /* argument type for 4th argument to getsockopt. */ #if ! defined (GETSOCKOPT_ARG) #define GETSOCKOPT_ARG void * #endif #if ! defined (USE_SIGSET) && ! defined(USE_SIGVEC) #define USE_SIGACTION #endif /*************************************************************************** ** INN SHARED SECTION ** ***************************************************************************/ /* * If you compile with -DUSE_INN_INCLUDES (and an appropriate '-I' from * within) the INN source tree, then you shouldn't need to touch this next * section */ #if defined (USE_INN_INCLUDES) #include "configdata.h" #include "logging.h" #else /* all the defines below here match INN in its configdata.h or logging.h */ #if defined (__bsdi__) /* BSD/OS */ #elif defined (__NetBSD__) /* NetBSD */ #define DONT_USE_UNION_WAIT 1 #elif defined (linux) /* LINUX */ #elif defined (__osf__) /* DEC Unix */ #define DONT_USE_UNION_WAIT 1 #elif defined (SOLARIS) /* SunOS 5.x */ #define DO_HAVE_WAITPID 1 #define LOG_PERROR 0 #elif defined (sun) && !defined (SOLARIS) /* SunOS 4.x */ #define LOG_PERROR 0 #elif defined (_nec_ews) #define DO_HAVE_WAITPID 1 #define LOG_PERROR 0 #endif /* * how syslog should be opened. LOG_PID not needed--it is added * automatically. */ #define L_OPENLOG_FLAGS (LOG_NDELAY) /* same as INN's define DO_HAVE_UNISTD or DONT_HAVE_UNISTD */ #if ! defined (DO_HAVE_UNISTD) && ! defined (DONT_HAVE_UNISTD) #define DO_HAVE_UNISTD 1 #endif /* same as INN's define DO_HAVE_UNIX_DOMAIN or DONT_HAVE_UNIX_DOMAIN */ #if ! defined (DO_HAVE_UNIX_DOMAIN) && ! defined (DONT_HAVE_UNIX_DOMAIN) #define DO_HAVE_UNIX_DOMAIN 1 #endif /* same as INN's for maximum number of file descriptors. Choose one of these */ /* #define FDCOUNT_GETRLIMIT 1 */ /* #define FDCOUNT_GETDTAB 1 */ /* #define FDCOUNT_SYSCONF 1 */ /* #define FDCOUNT_ULIMIT 1 */ /* #define FDCOUNT_CONSTANT 1 */ #if ! defined (FDCOUNT_GETRLIMIT) && ! defined (FDCOUNT_GETDTAB) #if ! defined (FDCOUNT_SYSCONF) && ! defined (FDCOUNT_ULIMIT) #if ! defined (FDCOUNT_CONSTANT) #define FDCOUNT_GETRLIMIT 1 #endif #endif #endif /* Same as INN's. defined DO_USE_UNION_WAIT or DONT_USE_UNION_WAIT */ #if ! defined (DO_USE_UNION_WAIT) && ! defined (DONT_USE_UNION_WAIT) #define DONT_USE_UNION_WAIT 1 #endif /* define DO_HAVE_WAITPID or DONT_HAVE_WAITPID depending on if you have to use waitpid() rather than wait3() */ #if ! defined (DO_HAVE_WAITPID) && ! defined (DONT_HAVE_WAITPID) #define DONT_HAVE_WAITPID 1 #endif /* define NBIO_FCNTL or NBIO_IOCTL depending on the type of non-blocking i/o you use. */ #if ! defined (NBIO_FCNTL) && ! defined (NBIO_IOCTL) #define NBIO_FCNTL 1 #endif /* define DO_NEED_TIME or DONT_NEED_TIME depending on if you need as well as */ #if ! defined (DO_NEED_TIME) && ! defined (DONT_NEED_TIME) #define DO_NEED_TIME 1 #endif /* define DO_BIND_USE_SIZEOF or DONT_BIND_USE_SIZEOF depending on your AF_UNIX bind use sizeof for the socket size? */ #if ! defined (DO_BIND_USE_SIZEOF) && ! defined (DONT_BIND_USE_SIZEOF) #define DO_BIND_USE_SIZEOF 1 #endif /* define DIR_DIRENT or DIR_DIRECT depending on the type of your system's directory reading routines */ #if ! defined (DIR_DIRENT) && ! defined (DIR_DIRECT) #define DIR_DIRENT 1 #endif /* define DO_HAVE_SETBUFFER if you have setbuffer(), or DONT_HAVE_SETBUFFER to use setbuf(). */ #if ! defined (DO_HAVE_SETBUFFER) && ! defined (DONT_HAVE_SETBUFFER) #define DONT_HAVE_SETBUFFER 1 #endif #endif /* defined (USE_INN_INCLUDES) */ #endif /* sysconfig_h__ */ innfeed-0.10.1.7.orig/tape.c0100644000175100001440000010357706375220175013747 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Sun Dec 31 18:52:04 1995 * Project: INN (innfeed) * File: tape.c * RCSId: $Id: tape.c,v 1.4 1997/08/16 03:37:33 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The implementation of the Tape class. Tapes are read-only * or write-only files that are accessed sequentially. Their * basic unit of i/o is an Article. Tapes work out of a single * directory and manage all file names themselves. * * Tapes will checkpoint themselves periodically so * that when innfeed exits or crashes things can * restart close to where they were last. The period * checkpointing is handled entirely by the Tape class, * but the checkpoint period needs to be set by some * external user before the first tape is created. * */ #if ! defined (lint) static const char *rcsid = "$Id: tape.c,v 1.4 1997/08/16 03:37:33 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #include #include #include #include #include #include #if defined (DIR_DIRENT) #include typedef struct dirent DIRENTRY ; #endif #if defined (DIR_DIRECT) #include typedef struct direct DIRENTRY ; #endif #if defined (DO_NEED_TIME) #include #endif #include #include "tape.h" #include "article.h" #include "endpoint.h" #include "msgs.h" #include "configfile.h" #if 0 /* a structure for temporary storage of articles. */ typedef struct q_e_s { Article article ; struct q_e_s *next ; } *QueueElem ; #endif /* The Tape class type. */ struct tape_s { /* the pathname of the file the administrator can drop in by hand. */ char *handFilename ; /* the pathname of the file the Tape will read from */ char *inputFilename ; /* the pathname of the file the Tape will write to. */ char *outputFilename ; /* the pathname of the file used in locking */ char *lockFilename ; /* the peer we're doing this for. */ char *peerName ; FILE *inFp ; /* input FILE */ FILE *outFp ; /* output FILE */ time_t lastRotated ; /* time files last got switched */ bool checkNew ; /* set bool when we need to check for hand-crafted file. */ #if 0 /* the tape holds a small output queue in memory to avoid thrashing. */ QueueElem head ; QueueElem tail ; u_int qLength ; /* amount on the queue */ #endif long outputSize ; /* the current size of the output file. */ long lossage ; /* the number of bytes we try to keep the output under. We actually wait for the outputSize to get 10% greater than this amount before shrinking the file down again. A value of zero means no limit. */ long outputLowLimit ; long outputHighLimit ; double backlogFactor ; bool scribbled ; /* have we scribbled a checkpoint value in the file. */ long tellpos ; /* for input file checkpointing. */ bool changed ; /* true if tape was read since last checkpoint or start. */ /* true if articles that are output are NOT later input. */ bool noRotate ; /* true if no articles should ever be spooled */ bool noBacklog ; }; static void checkpointTape (Tape tape) ; static void removeTapeGlobally (Tape tape) ; static void addTapeGlobally (Tape tape) ; static void prepareFiles (Tape tape) ; static void tapeCkNewFileCbk (TimeoutId id, void *d) ; static void tapeCheckpointCallback (TimeoutId id, void *d) ; #if 0 static void flushTape (Tape tape) ; #endif static void tapesSetCheckNew (void) ; static void initTape (Tape nt) ; static void tapeCleanup (void) ; /* pathname of directory we store tape files in. */ static char *tapeDirectory ; /* the callback ID of the checkpoint timer callback. */ static TimeoutId checkPtId ; static TimeoutId ckNewFileId ; /* number of seconds between tape checkpoints. */ static u_int tapeCkPtPeriod ; static u_int tapeCkNewFilePeriod ; /* the callback ID of the tape rotation timer callback. */ static TimeoutId rotateId ; /* XXX */ static time_t rotatePeriod = TAPE_ROTATE_PERIOD ; /* global list of tapes so we can checkpoint them periodically */ static Tape *activeTapes ; /* Size of the activeTapes array */ static size_t activeTapeSize ; /* index of last element in activeTapes that's being used. */ static size_t activeTapeIdx ; #if 0 /* default limit of the size of output tapes. */ static long defaultSizeLimit ; #endif u_int tapeHighwater ; bool debugShrinking = false ; extern bool talkToSelf ; /* main.c */ /* callback when config file is loaded */ int tapeConfigLoadCbk (void *data) { int rval = 1 ; long iv ; int bv ; FILE *fp = (FILE *) data ; char *dir ; if (getString (topScope,"backlog-directory",&dir,NO_INHERIT)) { if (tapeDirectory != NULL && strcmp (tapeDirectory,dir) != 0) { syslog (LOG_ERR,NO_CHANGE_BACKLOG) ; FREE (dir) ; dir = strdup (tapeDirectory) ; } if (!isDirectory (dir) && isDirectory (TAPE_DIRECTORY)) { logOrPrint (LOG_ERR,fp,BAD_TAPEDIR_CHANGE,dir,TAPE_DIRECTORY) ; FREE (dir) ; dir = strdup (TAPE_DIRECTORY) ; } else if (!isDirectory (dir)) logAndExit (1,NO_TAPE_DIR) ; } else if (!isDirectory (TAPE_DIRECTORY)) logAndExit (1,NO_TAPE_DIR) ; else dir = strdup (TAPE_DIRECTORY) ; if (tapeDirectory != NULL) FREE (tapeDirectory) ; tapeDirectory = dir ; if (getInteger (topScope,"backlog-highwater",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"backlog-highwater", iv,"global scope",(long)TAPE_HIGHWATER); iv = TAPE_HIGHWATER ; } } else iv = TAPE_HIGHWATER ; tapeHighwater = (u_int) iv ; if (getInteger (topScope,"backlog-rotate-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"backlog-rotate-period", iv,"global scope",(long)TAPE_ROTATE_PERIOD); iv = TAPE_ROTATE_PERIOD ; } } else iv = TAPE_ROTATE_PERIOD ; rotatePeriod = (u_int) iv ; if (getInteger (topScope,"backlog-ckpt-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"backlog-ckpt-period",iv, "global scope",(long)TAPE_CHECKPOINT_PERIOD); iv = TAPE_CHECKPOINT_PERIOD ; } } else iv = TAPE_CHECKPOINT_PERIOD ; tapeCkPtPeriod = (u_int) iv ; if (getInteger (topScope,"backlog-newfile-period",&iv,NO_INHERIT)) { if (iv < 0) { rval = 0 ; logOrPrint (LOG_ERR,fp,LESS_THAN_ZERO,"backlog-newfile-period", iv,"global scope",(long)TAPE_NEWFILE_PERIOD); iv = TAPE_NEWFILE_PERIOD ; } } else iv = TAPE_NEWFILE_PERIOD ; tapeCkNewFilePeriod = (u_int) iv ; if (getBool (topScope,"debug-shrinking",&bv,NO_INHERIT)) debugShrinking = (bv ? true : false) ; return rval ; } /* Create a new Tape object. There are three potential files involved in I/O. 'peerName' is what the admin may have dropped in by hand. 'peerName.input' is the file that was being used as input the last time things were run. 'peerName.output' is the file that was being used as output. The file 'peerName' is appended to 'peerName.input' (or renamed if 'peerName.input' doesn't exist). Then 'peerName.output' is appeneded (or renamed) to 'peerName.input' */ static bool inited = false ; Tape newTape (const char *peerName, bool dontRotate) { Tape nt = ALLOC (struct tape_s, 1) ; size_t pLen = strlen (peerName) ; size_t dLen = strlen (tapeDirectory) ; if (!inited) { inited = true ; atexit (tapeCleanup) ; } ASSERT (nt != NULL) ; if (endsIn (peerName,INPUT_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",INPUT_TAIL) ; if (endsIn (peerName,OUTPUT_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",OUTPUT_TAIL) ; if (endsIn (peerName,LOCK_TAIL)) die ("Sorry, can't have a peer name ending in \"%s\"",LOCK_TAIL) ; nt->peerName = strdup (peerName) ; nt->handFilename = MALLOC (pLen + dLen + 2) ; ASSERT (nt->handFilename != NULL) ; sprintf (nt->handFilename,"%s/%s",tapeDirectory,peerName) ; nt->lockFilename = MALLOC (pLen + dLen + strlen(LOCK_TAIL) + 2) ; ASSERT (nt->lockFilename != NULL) ; sprintf (nt->lockFilename,"%s/%s%s",tapeDirectory,peerName,LOCK_TAIL) ; nt->inputFilename = MALLOC (pLen + dLen + strlen(INPUT_TAIL) + 2) ; ASSERT (nt->inputFilename != NULL) ; sprintf (nt->inputFilename,"%s/%s%s",tapeDirectory,peerName,INPUT_TAIL) ; nt->outputFilename = MALLOC (pLen + dLen + strlen(OUTPUT_TAIL) + 2) ; ASSERT (nt->outputFilename != NULL) ; sprintf (nt->outputFilename,"%s/%s%s",tapeDirectory,peerName,OUTPUT_TAIL) ; if ( !lockFile (nt->lockFilename) ) { syslog (LOG_ERR,NO_LOCK_TAPE,nt->lockFilename) ; FREE (nt->handFilename) ; FREE (nt->lockFilename) ; FREE (nt->inputFilename) ; FREE (nt->outputFilename) ; FREE (nt) ; return NULL ; } nt->noRotate = false ; /* for first time prepare */ initTape (nt) ; nt->noRotate = dontRotate ; addTapeGlobally (nt) ; if (checkPtId == 0 && tapeCkPtPeriod > 0) /* only done once. */ checkPtId = prepareSleep (tapeCheckpointCallback,tapeCkPtPeriod,NULL); if (ckNewFileId == 0 && tapeCkNewFilePeriod > 0) ckNewFileId = prepareSleep (tapeCkNewFileCbk,tapeCkNewFilePeriod,NULL) ; return nt ; } static void initTape (Tape nt) { value *peerVal = findPeer (nt->peerName) ; scope *s = (peerVal == NULL ? NULL : peerVal->v.scope_val) ; nt->inFp = NULL ; nt->outFp = NULL ; nt->lastRotated = 0 ; nt->checkNew = false ; #if 0 nt->head = NULL ; nt->tail = NULL ; nt->qLength = 0 ; #endif nt->scribbled = false ; nt->tellpos = 0 ; nt->changed = false ; nt->outputSize = 0 ; nt->lossage = 0 ; nt->noBacklog = false ; nt->backlogFactor = 0.0 ; nt->outputLowLimit = 0 ; nt->outputHighLimit = 0 ; if (!talkToSelf) { int bval ; if (getBool (s, "no-backlog", &bval, INHERIT)) nt->noBacklog = (bval ? true : false); else nt->noBacklog = TAPE_DISABLE; if (getInteger (s,"backlog-limit",&nt->outputLowLimit,INHERIT)) { if (!getReal (s,"backlog-factor",&nt->backlogFactor,INHERIT)) { if (!getInteger (s,"backlog-limit-highwater", &nt->outputHighLimit,INHERIT)) { syslog (LOG_WARNING,NO_FACTOR,nt->peerName) ; nt->outputLowLimit = 0 ; nt->outputHighLimit = 0 ; nt->backlogFactor = 0.0 ; } } else nt->outputHighLimit = (long)(nt->outputLowLimit * nt->backlogFactor); } else syslog (LOG_ERR,NODEFN,"backlog-limit") ; } dprintf (1, "%s spooling: %s\n", nt->peerName, nt->noBacklog ? "disabled" : "enabled"); dprintf (1,"%s tape backlog limit: [%ld %ld]\n",nt->peerName, nt->outputLowLimit, nt->outputHighLimit) ; prepareFiles (nt) ; } void gFlushTapes (void) { u_int i ; syslog (LOG_NOTICE,FLUSHING_TAPES) ; for (i = 0 ; i < activeTapeIdx ; i++) tapeFlush (activeTapes [i]) ; } /* close the input and output tapes and reinitialize everything in the tape. */ void tapeFlush (Tape tape) { if (tape->inFp != NULL) { checkpointTape (tape) ; fclose (tape->inFp) ; } if (tape->outFp != NULL) fclose (tape->outFp) ; initTape (tape) ; } void gPrintTapeInfo (FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Tape List : (count %d) {\n", indent,activeTapeIdx) ; for (i = 0 ; i < activeTapeIdx ; i++) printTapeInfo (activeTapes [i],fp,indentAmt + INDENT_INCR) ; fprintf (fp,"%s}\n",indent) ; } void tapeLogGlobalStatus (FILE *fp) { fprintf (fp,"Backlog file global values\n") ; fprintf (fp," directory: %s\n",tapeDirectory) ; fprintf (fp," rotate period: %-3ld seconds\n",(long) rotatePeriod) ; fprintf (fp,"checkpoint period: %-3ld seconds\n",(long) tapeCkPtPeriod) ; fprintf (fp," newfile period: %-3ld seconds\n",(long) tapeCkNewFilePeriod); fprintf (fp,"\n") ; } void tapeLogStatus (Tape tape, FILE *fp) { if (tape == NULL) fprintf (fp,"(no tape)\n") ; else if (tape->noBacklog) fprintf (fp," spooling: DISABLED\n"); else { fprintf (fp," backlog low limit: %ld\n", tape->outputLowLimit) ; fprintf (fp," backlog upper limit: %ld", tape->outputHighLimit) ; if (tape->backlogFactor > 0.0) fprintf (fp," (factor %1.2f)",tape->backlogFactor) ; fputc ('\n',fp) ; fprintf (fp," backlog shrinkage: ") ; fprintf (fp,"%ld bytes (from current file)\n",tape->lossage) ; } } void printTapeInfo (Tape tape, FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; #if 0 QueueElem qe ; #endif for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sTape : %p {\n",indent,tape) ; if (tape == NULL) { fprintf (fp,"%s}\n",indent) ; return ; } fprintf (fp,"%s master-file : %s\n", indent, tape->handFilename) ; fprintf (fp,"%s input-file : %s\n", indent, tape->inputFilename) ; fprintf (fp,"%s output-file : %s\n",indent, tape->outputFilename) ; fprintf (fp,"%s lock-file : %s\n",indent, tape->lockFilename) ; fprintf (fp,"%s peerName : %s\n",indent,tape->peerName) ; fprintf (fp,"%s input-FILE : %p\n",indent, tape->inFp) ; fprintf (fp,"%s output-FILE : %p\n",indent, tape->outFp) ; fprintf (fp,"%s output-limit : %ld\n",indent, tape->outputLowLimit) ; #if 0 fprintf (fp,"%s in-memory article queue (length %d) {\n",indent, tape->qLength) ; for (qe = tape->head ; qe != NULL ; qe = qe->next) { #if 0 printArticleInfo (qe->article,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,qe->article) ; #endif } fprintf (fp,"%s }\n",indent) ; #endif fprintf (fp,"%s tell-position : %ld\n",indent,(long) tape->tellpos) ; fprintf (fp,"%s input-FILE-changed : %s\n",indent, boolToString (tape->changed)) ; fprintf (fp,"%s no-rotate : %s\n",indent, boolToString (tape->noRotate)); fprintf (fp,"%s}\n",indent) ; } /* delete the tape. Spools the in-memory articles to disk. */ void delTape (Tape tape) { struct stat st ; if (tape == NULL) return ; #if 1 if (tape->outFp != NULL && fclose (tape->outFp) != 0) syslog (LOG_ERR,FCLOSE_FAILED, tape->outputFilename) ; if (stat(tape->outputFilename, &st) == 0 && st.st_size == 0) { dprintf (1,"removing empty output tape: %s\n",tape->outputFilename) ; (void) unlink (tape->outputFilename) ; } tape->outFp = NULL ; tape->outputSize = 0 ; #else tapeClose (tape) ; #endif if (tape->inFp != NULL) { checkpointTape (tape) ; (void) fclose (tape->inFp) ; } unlockFile (tape->lockFilename) ; freeCharP (tape->handFilename) ; freeCharP (tape->inputFilename) ; freeCharP (tape->outputFilename) ; freeCharP (tape->lockFilename) ; freeCharP (tape->peerName) ; removeTapeGlobally (tape) ; FREE (tape) ; } void tapeTakeArticle (Tape tape, Article article) { #if 0 QueueElem elem ; #endif int amt ; const char *fname, *msgid ; ASSERT (tape != NULL) ; /* return immediately if spooling disabled - jgarzik */ if (tape->noBacklog) { delArticle (article) ; return; } fname = artFileName (article) ; msgid = artMsgId (article) ; amt = fprintf (tape->outFp,"%s %s\n", fname, msgid) ; /* I'd rather know where I am each time, and I don't trust all fprintf's to give me character counts. */ #if defined (TRUST_FPRINTF) tape->outputSize += amt ; #else #if defined (NO_TRUST_STRLEN) tape->outputSize = ftell (tape->outFp) ; #else tape->outputSize += strlen(fname) + strlen(msgid) + 2 ; /* " " + "\n" */ #endif #endif delArticle (article) ; if (debugShrinking) { struct stat sb ; fflush (tape->outFp) ; if (fstat (fileno (tape->outFp),&sb) != 0) syslog (LOG_ERR,FSTAT_FAILURE,tape->outputFilename) ; else if (sb.st_size != tape->outputSize) syslog (LOG_ERR,"fstat and ftell do not agree: %ld %ld for %s\n", (long)sb.st_size,tape->outputSize,tape->outputFilename) ; } if (tape->outputHighLimit > 0 && tape->outputSize >= tape->outputHighLimit) { long oldSize = tape->outputSize ; (void) shrinkfile (tape->outFp, tape->outputLowLimit,tape->outputFilename,"a+") ; tape->outputSize = ftell (tape->outFp) ; tape->lossage += oldSize - tape->outputSize ; } } /* Pick an article off a tape and return it. NULL is returned if there are no more articles. */ Article getArticle (Tape tape) { char line [2048] ; /* ick. 1024 for filename + 1024 for msgid */ char *p, *q ; char *msgid, *filename ; Article art = NULL ; time_t now = theTime() ; ASSERT (tape != NULL) ; if (tape->inFp == NULL && (now - tape->lastRotated) > rotatePeriod) prepareFiles (tape) ; /* will flush queue too. */ while (tape->inFp != NULL && art == NULL) { tape->changed = true ; if (fgets (line,sizeof (line), tape->inFp) == NULL) { if (ferror (tape->inFp)) syslog (LOG_ERR,TAPE_INPUT_ERROR,tape->inputFilename) ; else if ( !feof (tape->inFp) ) syslog (LOG_ERR,FGETS_FAILED,tape->inputFilename) ; if (fclose (tape->inFp) != 0) syslog (LOG_ERR,FCLOSE_FAILED, tape->inputFilename); dprintf (1,"No more articles on tape %s\n",tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; (void) unlink (tape->inputFilename) ; if ((now - tape->lastRotated) > rotatePeriod) prepareFiles (tape) ; /* rotate files to try next. */ } else { msgid = filename = NULL ; for (p = line ; *p && isspace (*p) ; p++) /* eat whitespace */ /* nada */ ; if (*p != '\0') { q = strchr (p,' ') ; if (q != NULL) { filename = p ; *q = '\0' ; for (q++ ; *q && isspace (*q) ; q++) /* eat more white */ /* nada */ ; if (*q != '\0') { if (((p = strchr (q, ' ')) != NULL) || ((p = strchr (q, '\n')) != NULL)) *p = '\0' ; if (p != NULL) msgid = q ; else filename = NULL ; /* no trailing newline or blank */ } else filename = NULL ; /* line had one field and some blanks */ } else filename = NULL ; /* line only had one field */ } if (filename != NULL && msgid != NULL) art = newArticle (filename, msgid) ; /* art may be NULL here if the file is no longer valid. */ } } #if 0 /* now we either have an article or there is no more on disk */ if (art == NULL) { if (tape->inFp != NULL && ((c = fgetc (tape->inFp)) != EOF)) ungetc (c,tape->inFp) ; /* shouldn't happen */ else if (tape->inFp != NULL) { /* last article read was the end of the tape. */ if (fclose (tape->inFp) != 0) syslog (LOG_ERR,FCLOSE_FAILED,tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; /* toss out the old input file and prepare the new one */ (void) unlink (tape->inputFilename) ; if (now - tape->lastRotated > rotatePeriod) prepareFiles (tape) ; } } #endif if (art == NULL) dprintf (2,"%s All out of articles in the backlog\n", tape->peerName) ; else dprintf (2,"%s Peeled article %s from backlog\n",tape->peerName, artMsgId (art)) ; return art ; } /****************************************************/ /** CLASS FUNCTIONS **/ /****************************************************/ /* Cause all the Tapes to checkpoint themselves. */ void checkPointTapes (void) { u_int i ; for (i = 0 ; i < activeTapeIdx ; i++) checkpointTape (activeTapes [i]) ; } /* make all the tapes set their checkNew flag. */ static void tapesSetCheckNew (void) { u_int i ; for (i = 0 ; i < activeTapeIdx ; i++) activeTapes[i]->checkNew = true ; } #if 0 /* Set the pathname of the directory for storing tapes in. */ void setTapeDirectory (const char *newDir) { /* the activeTape variable gets set when the first Tape object is created */ if (activeTapes != NULL) { syslog (LOG_CRIT,"Resetting backlog directory") ; abort() ; } if (tapeDirectory != NULL) freeCharP (tapeDirectory) ; tapeDirectory = strdup (newDir) ; addPointerFreedOnExit (tapeDirectory) ; } #endif /* Get the pathname of the directory tapes are stored in. */ const char *getTapeDirectory (void) { ASSERT (tapeDirectory != NULL) ; #if 0 if (tapeDirectory == NULL) { tapeDirectory = strdup (TAPE_DIRECTORY) ; addPointerFreedOnExit (tapeDirectory) ; } #endif return tapeDirectory ; } #if 0 void setOutputSizeLimit (long val) { defaultSizeLimit = val ; } #endif /**********************************************************************/ /* PRIVATE FUNCTIONS */ /**********************************************************************/ /* Add a new tape to the class-level list of active tapes. */ static void addTapeGlobally (Tape tape) { ASSERT (tape != NULL) ; if (activeTapeSize == activeTapeIdx) { u_int i ; activeTapeSize += 10 ; if (activeTapes != NULL) activeTapes = REALLOC (activeTapes, Tape, activeTapeSize) ; else activeTapes = ALLOC (Tape, activeTapeSize) ; ASSERT (activeTapes != NULL) ; for (i = activeTapeIdx ; i < activeTapeSize ; i++) activeTapes [i] = NULL ; } activeTapes [activeTapeIdx++] = tape ; } /* Remove a tape for the class-level list of active tapes. */ static void removeTapeGlobally (Tape tape) { u_int i ; if (tape == NULL) return ; ASSERT (activeTapeIdx > 0) ; for (i = 0 ; i < activeTapeIdx ; i++) if (activeTapes [i] == tape) break ; ASSERT (i < activeTapeIdx) ; for ( ; i < (activeTapeIdx - 1) ; i++) activeTapes [i] = activeTapes [i + 1] ; activeTapes [--activeTapeIdx] = NULL ; if (activeTapeIdx == 0) { FREE (activeTapes) ; activeTapes = NULL ; } } /* Have a tape checkpoint itself so that next process can pick up where this one left off. */ static void checkpointTape (Tape tape) { if (tape->inFp == NULL) /* no input file being read. */ return ; if (!tape->changed) /* haven't read since last checkpoint */ { dprintf (1,"Not checkpointing unchanged tape: %s\n", tape->peerName) ; return ; } if ((tape->tellpos = ftell (tape->inFp)) < 0) { syslog (LOG_ERR,FTELL_FAILED,tape->inputFilename) ; return ; } /* strlen of "18446744073709551616\n" (2^64) */ #define BITS64 21 /* make sure we're not right at the beginning of the file so we can write. */ if (tape->tellpos > BITS64) { rewind (tape->inFp) ; /* scribble blanks over the first lines characters */ if (!tape->scribbled) { int currloc = 0 ; int c ; while ((c = fgetc (tape->inFp)) != '\n' || currloc <= BITS64) if (c == EOF) return ; else currloc++ ; rewind (tape->inFp) ; while (currloc-- > 0) fputc (' ',tape->inFp) ; rewind (tape->inFp) ; fflush (tape->inFp) ; tape->scribbled = true ; } fprintf (tape->inFp,"%ld",tape->tellpos) ; if (fseek (tape->inFp,tape->tellpos,SEEK_SET) != 0) syslog (LOG_ERR,FSEEK_FAILED,tape->inputFilename,tape->tellpos) ; } tape->changed = false ; } /* Prepare the tape file(s) for input and output */ /* For a given Tape there are * three possible files: PEER.input PEER and * PEER.output. PEER.input and PEER.output are private to * innfeed. PEER is where a sysadmin can drop a file that (s)he * wants to inject into the process. If the first line of the input file * contains only an integer (possibly surrounded by spaces), then this is * taken to be the position to seek to before reading. * * prepareFiles will process them in a manner much like the following shell * commands: * * if [ ! -f PEER.input ]; then * if [ -f PEER ]; then mv PEER PEER.input * elif [ -f PEER.output ]; then mv PEER.output PEER; fi * fi * * At this point PEER.input is opened for reading if it exists. * * The checkpoint file is left as-is unless the PEER.input file * happens to be newer that the checkpoint file. */ static void prepareFiles (Tape tape) { bool inpExists ; bool outExists ; bool newExists ; #if 0 /* flush any in memory articles to disk */ if (tape->head != NULL && tape->outFp != NULL) flushTape(tape) ; #endif tape->tellpos = 0 ; /* First time through, or something external has set checkNew */ if (tape->lastRotated == 0 || tape->checkNew) { newExists = fileExistsP (tape->handFilename) ; if (newExists) syslog (LOG_NOTICE,NEW_HAND_FILE,tape->peerName) ; } else newExists = false ; if (tape->lastRotated == 0) /* first time here */ { inpExists = fileExistsP (tape->inputFilename) ; outExists = fileExistsP (tape->outputFilename) ; } else { inpExists = (tape->inFp != NULL) ; /* can this ever be true?? */ outExists = (tape->outFp != NULL && tape->outputSize > 0) ; } /* move the hand-dropped file to the input file if needed. */ if (newExists && !inpExists) { if (rename (tape->handFilename,tape->inputFilename) != 0) syslog (LOG_ERR,RENAME_FAILED,tape->handFilename, tape->inputFilename); else { syslog (LOG_NOTICE,ROTATING_HAND_DROPPED,tape->peerName) ; inpExists = true ; } } /* now move the output file to the input file, if needed and only if in not in NOROTATE mode. */ if (outExists && !inpExists && !tape->noRotate) { if (tape->outFp != NULL) { fclose (tape->outFp) ; tape->outFp = NULL ; } if (rename (tape->outputFilename,tape->inputFilename) != 0) syslog (LOG_ERR,RENAME_FAILED, tape->outputFilename,tape->inputFilename); else inpExists = true ; outExists = false ; } /* now open up the input file and seek to the proper position. */ if (inpExists) { int c ; long flength ; if ((tape->inFp = fopen (tape->inputFilename,"r+")) == NULL) syslog (LOG_ERR,FOPEN_FAILURE,tape->inputFilename) ; else { char buffer [64] ; if (fgets (buffer,sizeof (buffer) - 1, tape->inFp) == NULL) { if (feof (tape->inFp)) { dprintf (1,"Empty input file: %s",tape->inputFilename) ; unlink (tape->inputFilename) ; } else syslog (LOG_ERR,FGETS_FAILED,tape->inputFilename) ; fclose (tape->inFp) ; tape->inFp = NULL ; tape->scribbled = false ; } else { u_int len = strlen (buffer) ; long newPos = 0 ; if (len > 0 && buffer [len - 1] == '\n') buffer [--len] = '\0' ; if (len > 0 && strspn (buffer,"0123456789 \n") == len) { if (sscanf (buffer,"%ld",&newPos) == 1) { tape->scribbled = true ; tape->tellpos = newPos ; } } if ((flength = fileLength (fileno (tape->inFp))) < tape->tellpos) { syslog (LOG_ERR,FILE_SHORT,tape->inputFilename, flength, tape->tellpos); tape->tellpos = 0 ; } else if (tape->tellpos == 0) rewind (tape->inFp) ; else if (fseek (tape->inFp,tape->tellpos - 1,SEEK_SET) != 0) syslog (LOG_ERR,FSEEK_FAILED,tape->inputFilename,tape->tellpos); else if ((c = fgetc (tape->inFp)) != '\n') { while (c != EOF && c != '\n') c = fgetc (tape->inFp) ; if (c == EOF) { fclose (tape->inFp) ; unlink (tape->inputFilename) ; tape->inFp = NULL ; tape->scribbled = false ; prepareFiles (tape) ; } else { long oldPos = tape->tellpos ; tape->changed = true ; checkpointTape (tape) ; syslog (LOG_ERR,CKPT_BNDRY,tape->inputFilename, tape->tellpos, oldPos) ; } } } } } tape->lastRotated = theTime() ; tape->checkNew = false ; /* now open up the output file. */ if (tape->outFp == NULL) { if ((tape->outFp = fopen (tape->outputFilename,"a+")) == NULL) { syslog (LOG_ERR,TAPE_OPEN_FAILED, "a+", tape->outputFilename) ; return ; } fseek (tape->outFp,0,SEEK_END) ; tape->outputSize = ftell (tape->outFp) ; tape->lossage = 0 ; } } static void tapeCkNewFileCbk (TimeoutId id, void *d) { (void) d ; ASSERT (id == ckNewFileId) ; ASSERT (tapeCkNewFilePeriod > 0) ; tapesSetCheckNew () ; ckNewFileId = prepareSleep (tapeCkNewFileCbk,tapeCkNewFilePeriod,NULL) ; } /* The timer callback function that will checkpoint all the active tapes. */ static void tapeCheckpointCallback (TimeoutId id, void *d) { (void) d ; /* keep lint happy */ ASSERT (id == checkPtId) ; ASSERT (tapeCkPtPeriod > 0) ; dprintf (1,"Checkpointing tapes\n") ; checkPointTapes () ; checkPtId = prepareSleep (tapeCheckpointCallback,tapeCkPtPeriod,NULL) ; } #if 0 static void flushTape (Tape tape) { QueueElem elem ; /* flush out queue to disk. */ elem = tape->head ; while (elem != NULL) { tape->head = tape->head->next ; fprintf (tape->outFp,"%s %s\n", artFileName (elem->article), artMsgId (elem->article)) ; delArticle (elem->article) ; FREE (elem) ; elem = tape->head ; } tape->tail = NULL ; tape->qLength = 0; /* I'd rather know where I am each time, and I don't trust all fprintf's to give me character counts. */ tape->outputSize = ftell (tape->outFp) ; if (debugShrinking) { struct stat buf ; static bool logged = false ; fflush (tape->outFp) ; if (fstat (fileno (tape->outFp),&buf) != 0 && !logged) { syslog (LOG_ERR,FSTAT_FAILURE,tape->outputFilename) ; logged = true ; } else if (buf.st_size != tape->outputSize) { syslog (LOG_ERR,FSTAT_NE_FTELL,tape->outputFilename) ; logged = true ; } } if (tape->outputHighLimit > 0 && tape->outputSize > tape->outputHighLimit) { (void) shrinkfile (tape->outFp, tape->outputLowLimit,tape->outputFilename,"a+") ; tape->outputSize = ftell (tape->outFp) ; } } #endif static void tapeCleanup (void) { FREE (tapeDirectory) ; } innfeed-0.10.1.7.orig/tape.h0100644000175100001440000000657306331417054013746 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 09:55:07 1995 * Project: INN (innfeed) * File: tape.h * RCSId: $Id: tape.h,v 1.1.1.1 1997/04/29 16:13:32 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: The public interface to the Tape class. * * The Tape class simulates a mag tape. It only reads or writes * Articles. A tape is either in an Input or Output state. When * an Article is given to a Tape it will store the Article in * memory until it reaches a highwater mark at which point it * dumps all it's articles to disk. * * Input tapes generate article objects on request if the * underlying tape file has info in it. The Tapes take care of * cleaning up used-up files as needed. * */ #if ! defined ( tape_h__ ) #define tape_h__ #include #include "misc.h" /* If dontRotate is true, then any articles that get written to the tape will never be read back in again. This is for the batch-mode-only case where articles written to tape were done so 'cause the remote temporarily rejected them. */ Tape newTape (const char *peerName, bool dontRotate) ; void gPrintTapeInfo (FILE *fp, u_int inedntAmt) ; void printTapeInfo (Tape tape, FILE *fp, u_int indentAmt) ; /* deletes the tape objects. If it has any articles cached then it dumps them to the disk. */ void delTape (Tape tape) ; /* give an article to the Tape for storage */ void tapeTakeArticle (Tape tape, Article article) ; /* get a new article from an Input tape. */ Article getArticle (Tape tape) ; /* close the input and output files and reopen them. */ void gFlushTapes (void) ; void tapeFlush (Tape tape) ; /**************************************************/ /* CLASS LEVEL FUNCTIONS */ /**************************************************/ /* get all the active input tapes to checkpoint their current positions */ void checkPointTapes (void) ; /* get the name of the directory tapes are being stored in. */ const char *getTapeDirectory (void) ; /* set the size limit of the output tapes. Default is zero which is no limit. */ void setOutputSizeLimit (long limit) ; int tapeConfigLoadCbk (void *data) ; void tapeLogGlobalStatus (FILE *fp) ; void tapeLogStatus (Tape tape, FILE *fp) ; #endif /* tape_h__ */ innfeed-0.10.1.7.orig/testListener.pl0100644000175100001440000001021106331417054015646 0ustar mdusers#!/usr/bin/perl # # Author: James Brister -- berkeley-unix -- # Start Date: Wed Jan 3 00:09:01 1996 # Project: INN -- innfeed # File: testListener.pl # RCSId: $Id: testListener.pl,v 1.1.1.1 1997/04/29 16:13:32 scrappy Exp $ # Description: Generate news files for testing the innfeed feeder. # # Run like this: # # testListener.pl -t 30 -d tmp | innfeed # # or like this: # # innfeed -s 'perl testListener.pl -t 30 -d tmp' # $0 =~ s!.*/!! ; require 'getopts.pl' ; $usage = "$0 [ -a -b name -d directory -c count -t sleep-amt -r -u ] peers\n" . " -a is for duplicate article id's periodically\n" . " -u is for random unlinking of article\n" . " -b add bogus peername periodically\n" . " -d is the directory where articles show be written.\n" . " -c is how many articles to create (0 the default meamns no limit)\n" . " -t is the number of seconds to sleep between each article.\n" . " -r is to have articles be created in NNTP ready format\n" ; &Getopts ("a:b:c:d:t:rl:h:") || die $usage ; die $usage if $opt_h ; $total = $opt_c ; $sleepAmt = 1 ; $sleepAmt = $opt_t if ($opt_t =~ /^[\d\.]+/) ; $lineCount = 50 ; $lineCount = $opt_l if ($opt_l =~ /^\d+$/) ; $directory = "." ; $directory = $opt_d if $opt_d ; $bogus = $opt_b ; if ( $bogus && $bogus !~ /^[a-zA-Z]+$/ ) { print "The bogus peername must contain only letters\n" ; } $cr = ($opt_r ? "\r" : "") ; $SIG{'INT'} = 'IGNORE' ; $SIG{'TERM'} = 'sigHup' ; $SIG{'QUIT'} = 'sigHup' ; $SIG{'HUP'} = 'sigHup' ; sub sigHup { exit (1) ; } $monstr = "JanFebMarAprMayJunJulAugSepOctNovDec" ; $letstr = "abcdefghijklmnopqrstuvwxyz" ; sub createArticle { local ($counter) = @_ ; local ($filename,$msgid,$i) ; local ($time) = $^T ; local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time); local ($index) = $counter ; if ($opt_a && ((int (rand (4)) % 2) == 0)) { $index = int ($index / 2) ; } $msgid = "<$index.$$.$time\@home.octet.com.au>" ; $filename = sprintf ("%s/SampleArticle.%06d",$directory,$index) ; open (ARTICLE,">$filename") || die "open ($filename): $!\n" ; print ARTICLE "Path: home.octet.com.au!not-for-mail$cr\n" ; print ARTICLE "From: brister\@home.octet.com.au$cr\n" ; print ARTICLE "Newsgroups: junk,local.test$cr\n" ; print ARTICLE "Subject: Test$cr\n" ; print ARTICLE "Date: " ; printf ARTICLE "%d %s %d %02d:%02d:%02d UTC$cr\n", $mday, substr($monstr,$mon * 3, 3), $year + 1900, $hour, $min, $sec ; print ARTICLE "Organization: None that I can think of$cr\n" ; print ARTICLE "Lines: 5$cr\n" ; print ARTICLE "Distribution: world$cr\n" ; print ARTICLE "Message-ID: $msgid$cr\n" ; print ARTICLE "NNTP-Posting-Host: localhost$cr\n" ; print ARTICLE "$cr\n" ; for ($i = 0 ; $i < $lineCount ; $i++) { print ARTICLE "x" x ($lineCount + 1), "$cr\n"; } print ARTICLE ".This line has a leading dot.$cr\n" ; print ARTICLE "And the next line only has a dot.$cr\n" ; if ($opt_r) { print ARTICLE "..$cr\n" ; } else { print ARTICLE ".$cr\n" ; } print ARTICLE "And the next line has just two dots...$cr\n" ; print ARTICLE "...$cr\n" ; print ARTICLE "foo$cr\n" ; print ARTICLE "And the next line is the last line of the article$cr\n" ; print ARTICLE "and it only has a single dot on it.$cr\n" ; if ($opt_r) { print ARTICLE "..$cr\n" ; } else { print ARTICLE ".$cr\n" ; } close (ARTICLE) ; return ($msgid, $filename) ; } srand ; $| = 1 ; if ( ! -t STDERR ) { open (STDERR,">>/tmp/TESTLISTENER.LOG") || die ; } srand ; $sleepAmt = 1 if ($sleepAmt < 0) ; foreach $peer ( @ARGV ) { $PEERS{$peer} = 1 ; } die "Must give peernames on command line:\n$usage" if ( ! @ARGV ) ; for ( $i = 0 ; $total == 0 || $i < $total ; $i++ ) { ($msgid,$filename) = &createArticle ($i) ; if ($opt_a && ((rand (3) % 3) == 0)) { print TTY "Removing file $filename\n" ; unlink ($filename) if $opt_u ; } print "$filename $msgid @ARGV" ; print " $bogus" if ($bogus && (rand (5) % 5) == 0) ; print "\n" ; select (undef,undef,undef,(rand ($sleepAmt-1) + 1)) if $sleepAmt ; } sleep 11500 unless -f STDOUT ; innfeed-0.10.1.7.orig/uio_maxiov.c0100644000175100001440000000616706331417055015167 0ustar mdusers/* -*- c -*- * * Author: James A. Brister -- berkeley-unix -- * Start Date: Thu, 01 Feb 1996 18:52:39 +1100 * Project: INN -- innfeed * File: uio_maxiov.c * RCSId: $Id: uio_maxiov.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $ * * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * * Description: Attempt to figure out the maximum number of iovec's a * writev() call will handle. * */ #if ! defined (lint) static const char *rcsid = "$Id: uio_maxiov.c,v 1.1.1.1 1997/04/29 16:13:33 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #include #include #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #include /* the largest number we'll try. */ #define MAXTRY 10000 int main(int argc, char ** argv) { int fd = open ("/dev/null",O_WRONLY,0666) ; struct iovec *array ; int size, i, rval ; unsigned int amt ; char data ; (void) argc ; /* keep lint happy */ (void) argv ; /* keep lint happy */ if (fd < 0) { perror ("open (\"/dev/null\")") ; exit (1) ; } data = 'x' ; for (size = 1 ; size <= MAXTRY ; size++) { amt = sizeof (struct iovec) * size ; array = (struct iovec *) malloc (amt) ; if (array == NULL) { printf ("Unable to allocate %d bytes\n", amt) ; exit (1) ; } for (i = 0 ; i < size ; i++) { array [i].iov_base = &data ; array [i].iov_len = sizeof (char) ; } if ((rval = writev (fd,array,size)) < 0) { if (errno != EINVAL) { perror ("writev") ; exit (1) ; } else { printf ("UIO_MAXIOV (MAX_WRITEV_VEC) looks to be %d\n", size - 1) ; exit (0) ; } } else if (size == MAXTRY) printf ("UIO_MAXIOV (MAX_WRITEV_VEC) looks to be *at least* %d\n", MAXTRY); free (array) ; } exit (0) ; } innfeed-0.10.1.7.orig/article.c0100644000175100001440000010152606400214354014420 0ustar mdusers/* -*- c -*- * * Author: James Brister -- berkeley-unix -- * Start Date: Wed Dec 27 18:45:55 1995 * Project: INN (innfeed) * File: article.c * RCSId: $Id: article.c,v 1.4 1997/08/25 05:32:28 scrappy Exp $ * Copyright: Copyright (c) 1996 by Internet Software Consortium * * Permission to use, copy, modify, and distribute this * software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this * permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE * CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET * SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE * USE OR PERFORMANCE OF THIS SOFTWARE. * Description: The implementation of the Article class. Articles are the * abstraction for the actual news articles. They are a * reference counted object because they need to be shared by * multiple Host and Connection objects. When an Article is * created it verifies that the actual file exists. When a * Connection wants the article's contents for transmission * the Article reads the data off disk and returns a set of * Buffer objects. The Article holds onto these Buffers so * that the next Connection that wants to transmit it won't * have to wait for a disk read to be done again. * * */ #if ! defined (lint) static const char *rcsid = "$Id: article.c,v 1.4 1997/08/25 05:32:28 scrappy Exp $" ; static void use_rcsid (const char *rid) { /* Never called */ use_rcsid (rcsid) ; use_rcsid (rid) ; } #endif #include "config.h" #if defined (DO_HAVE_UNISTD) #include #endif #include #include #include #ifdef HAVE_MMAP #include #endif /* HAVE_MMAP */ #include #include #include #include #include #include #include "article.h" #include "buffer.h" #include "endpoint.h" #include "msgs.h" #if defined (NDEBUG) #define VALIDATE_HASH_TABLE() (void (0)) #else #define VALIDATE_HASH_TABLE() hashValidateTable () #endif extern bool useMMap ; struct article_s { int refCount ; /* the reference count on this article */ char *fname ; /* the file name of the article */ char *msgid ; /* the msgid of the article (INN tells us) */ Buffer contents ; /* the buffer of the actual on disk stuff */ Buffer *nntpBuffers ; /* list of buffers for transmisson */ caddr_t mMapping ; /* base of memory mapping, or NULL if none */ bool loggedMissing ; /* true if article is missing and we logged */ bool articleOk ; /* true until we know otherwise. */ bool inWireFormat ; /* true if ->contents is \r\n/dot-escaped */ } ; struct hash_entry_s { struct hash_entry_s *next ; struct hash_entry_s *prev ; struct hash_entry_s *nextTime ; struct hash_entry_s *prevTime ; u_int hash ; Article article ; }; typedef struct hash_entry_s *HashEntry ; #ifdef XXX_RAWHACK #include "configdata.h" #include "raw.h" /* This is a really ugly thing to do.... */ extern RAWPART_OFF_T RAWlastartsize; #endif /* XXX_RAWHACK */ /* * Private functions */ static Buffer artGetContents (Article article) ; /* Return the buffer that fillContents() filled up. */ /* Log statistics on article memory usage. */ static void logArticleStats (TimeoutId id, void *data) ; static bool fillContents (Article article) ; /* Read the article's bits off the disk. */ /* Append buffer B to the buffer array BUFFS. */ static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen); static bool prepareArticleForNNTP (Article article) ; /* Do the necessary CR-LF stuff */ static bool artFreeContents (Article art) ; /* Tell the Article to release its contents buffer if possible. */ static void artUnmap (Article art, int size) ; /* munmap an mmap()ed article */ /* * Hash table routine declarations. */ /* Returns a has value for the given string */ static u_int hashString (const char *string) ; /* Locates the article with the given message ID, in the has table. */ static Article hashFindArticle (const char *msgid) ; /* Puts the given article in the hash table. */ static void hashAddArticle (Article article) ; /* Removes the given article from the has table */ static bool hashRemoveArticle (Article article) ; /* Does some simple-minded hash table validation */ static void hashValidateTable (void) ; /* * Private data */ static u_int missingArticleCount ; /* Number of articles that were missing */ static bool logMissingArticles ; /* if true then we log the details on a missing article. */ static bool bitFiddleContents ; /* If true then we memcpy() the contents around to yield CR-LF on every line */ static u_int preparedBytes ; /* The number of areticle bytes read in so far of disk (will wrap around) */ static u_int preparedNewlines ; /* The number of newlines found in all the preparedBytes */ static u_int avgCharsPerLine ; /* The average number of characters per line over all articles. */ static bool rolledOver ; /* true if preparedBytes wrapped around */ static u_int bytesInUse ; /* count of article contents bytes in use--just the amount read from the article files, not all memory involved in. */ static u_int maxBytesInUse ; /* the limit we want to stay under for total bytes resident in memory dedicated to article contents. */ static u_int articlesInUse ; /* number of articles currently allocated. */ static u_int byteTotal ; /* number of bytes for article contents allocated totally since last log. */ static u_int articleTotal ; /* number of articles alloced since last log. */ static TimeoutId articleStatsId ; /* The timer callback id. */ /* * Hash Table data */ static HashEntry *hashTable ; /* the has table itself */ static HashEntry chronList ; /* chronologically ordered. Points at newest */ #define TABLE_SIZE 2048 /* MUST be a power of 2 */ #define HASH_MASK (TABLE_SIZE - 1) #define TABLE_ENTRY(hash) ((hash) & HASH_MASK) /*******************************************************************/ /** PUBLIC FUNCTIONS **/ /*******************************************************************/ /* Create a new article object. First looks to see if one with the given message id already exists in the hash table and if so returns that (after incrementing the reference count). */ Article newArticle (const char *filename, const char *msgid) { Article newArt = NULL ; if (hashTable == NULL) { /* first-time through initialization. */ int i ; ASSERT ((TABLE_SIZE & HASH_MASK) == 0) ; hashTable = ALLOC (HashEntry, TABLE_SIZE) ; ASSERT (hashTable != NULL) ; addPointerFreedOnExit ((char *)hashTable) ; for (i = 0 ; i < TABLE_SIZE ; i++) hashTable [i] = NULL ; if (ARTICLE_STATS_PERIOD > 0) articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0); } /* now look for it in the hash table. We presume the disk file is still ok */ if ((newArt = hashFindArticle (msgid)) == NULL) { newArt = CALLOC (struct article_s, 1) ; ASSERT (newArt != NULL) ; newArt->fname = MALLOC (strlen (filename) + 1) ; ASSERT (newArt->fname != NULL) ; strcpy (newArt->fname,filename) ; newArt->msgid = MALLOC (strlen (msgid) + 1) ; ASSERT (newArt->msgid != NULL) ; strcpy (newArt->msgid,msgid) ; newArt->contents = NULL ; newArt->mMapping = NULL ; newArt->refCount = 1 ; newArt->loggedMissing = false ; newArt->articleOk = true ; newArt->inWireFormat = false ; dprintf (3,"Adding a new article(%p): %s\n", newArt, msgid) ; articlesInUse++ ; articleTotal++ ; hashAddArticle (newArt) ; } else { if (strcmp (filename,newArt->fname) != 0) syslog (LOG_ERR, DOUBLE_NAME, filename, newArt->fname) ; newArt->refCount++ ; dprintf (2,"Reusing existing article for %s\nx",msgid) ; } return newArt ; } /* Decrement the reference count on the article and free up its memory if the ref count gets to 0. */ void delArticle (Article article) { if (article == NULL) return ; ASSERT (article->refCount > 0) ; if (--(article->refCount) == 0) { bool removed = hashRemoveArticle (article) ; ASSERT (removed == true) ; dprintf (2,"Cleaning up article (%p): %s\n",article, article->msgid) ; if (article->contents != NULL) { if (article->mMapping) artUnmap(article, bufferSize(article->contents)) ; else bytesInUse -= bufferDataSize (article->contents) ; if (article->nntpBuffers != NULL) freeBufferArray (article->nntpBuffers) ; delBuffer (article->contents) ; } articlesInUse-- ; FREE (article->fname) ; FREE (article->msgid) ; FREE (article) ; } VALIDATE_HASH_TABLE () ; } void gPrintArticleInfo (FILE *fp, u_int indentAmt) { char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sGlobal Article information : (count %d) {\n", indent, articlesInUse) ; fprintf (fp,"%s missingArticleCount : %d\n",indent,missingArticleCount) ; fprintf (fp,"%s logMissingArticles : %d\n",indent,logMissingArticles) ; fprintf (fp,"%s bitFiddleContents : %d\n",indent,bitFiddleContents) ; fprintf (fp,"%s preparedBytes : %d\n",indent,preparedBytes) ; fprintf (fp,"%s preparedNewlines : %d\n",indent,preparedNewlines) ; fprintf (fp,"%s avgCharsPerLine : %d\n",indent,avgCharsPerLine) ; fprintf (fp,"%s rolledOver : %s\n",indent,boolToString (rolledOver)) ; fprintf (fp,"%s bytesInUse : %d\n",indent,bytesInUse) ; fprintf (fp,"%s maxBytesInUse : %d\n",indent,maxBytesInUse) ; fprintf (fp,"%s articlesInUse : %d\n",indent,articlesInUse) ; fprintf (fp,"%s byteTotal : %d\n",indent,byteTotal) ; fprintf (fp,"%s articleTotal : %d\n",indent,articleTotal) ; fprintf (fp,"%s articleStatsId : %d\n",indent,articleStatsId) ; { HashEntry he ; for (he = chronList ; he != NULL ; he = he->nextTime) printArticleInfo (he->article,fp,indentAmt + INDENT_INCR) ; } fprintf (fp,"%s}\n",indent) ; } void printArticleInfo (Article art, FILE *fp, u_int indentAmt) { Buffer *b ; char indent [INDENT_BUFFER_SIZE] ; u_int i ; for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++) indent [i] = ' ' ; indent [i] = '\0' ; fprintf (fp,"%sArticle : %p {\n",indent,art) ; fprintf (fp,"%s article ok : %s\n",indent,boolToString (art->articleOk)) ; fprintf (fp,"%s refcount : %d\n",indent,art->refCount) ; fprintf (fp,"%s filename : %s\n",indent,art->fname) ; fprintf (fp,"%s msgid : %s\n",indent,art->msgid) ; fprintf (fp,"%s contents buffer : {\n",indent) ; #if 0 printBufferInfo (art->contents,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) art->contents) ; #endif fprintf (fp,"%s }\n",indent) ; fprintf (fp,"%s nntp buffers : {\n",indent) ; for (b = art->nntpBuffers ; b != NULL && *b != NULL ; b++) #if 0 printBufferInfo (*b,fp,indentAmt + INDENT_INCR) ; #else fprintf (fp,"%s %p\n",indent,(void *) *b) ; #endif fprintf (fp,"%s }\n", indent) ; fprintf (fp,"%s logged missing : %s\n", indent,boolToString (art->loggedMissing)); fprintf (fp,"%s}\n", indent) ; } /* return true if the file this article is in still exists. (actually return true if we have the contents of the article in memory or if the file is still OK). */ bool artFileIsValid (Article article) { bool rval = false ; /* we may already know it's not valid */ if (article->articleOk) { #ifdef XXX_RAWHACK int fd = RAWartopen(article->fname, article->msgid); if (fd < 0) { rval = false; /* Redundant, but that's OK */ } else { /* We're sharing this fd globally ... don't close. close(fd); */ rval = true; } #else /* XXX_RAWHACK */ if (article->contents != NULL) rval = true ; else if (fileExistsP (article->fname)) rval = true ; #endif /* XXX_RAWHACK */ #ifdef XXX_RAWHACK notrval: #endif /* XXX_RAWHACK */ if (!rval) { article->articleOk = false ; missingArticleCount++ ; if (logMissingArticles && !article->loggedMissing) { syslog (LOG_NOTICE,NO_ARTICLE,article->msgid,article->fname) ; article->loggedMissing = true ; } } } return rval ; } /* return true if we have or are able to get the contents off the disk */ bool artContentsOk (Article article) { bool rval = false ; if ( prepareArticleForNNTP (article) ) rval = true ; return rval ; } /* bump reference count on the article. */ Article artTakeRef (Article article) { if (article != NULL) article->refCount++ ; return article ; } /* return the filename of the article */ const char *artFileName (Article article) { if (article == NULL) return NULL ; else return article->fname ; } /* Get a NULL terminated array of Buffers that is ready for sending via NNTP */ Buffer *artGetNntpBuffers (Article article) { if ( !prepareArticleForNNTP (article) ) return NULL ; return dupBufferArray (article->nntpBuffers) ; } /* return the message id of the article */ const char *artMsgId (Article article) { return article->msgid ; } /* return how many NNTP-ready buffers the article contains */ u_int artNntpBufferCount (Article article) { if ( !prepareArticleForNNTP (article) ) return 0 ; return bufferArrayLen (article->nntpBuffers) ; } /* if VAL is true then all missing articles will be logged. */ void artLogMissingArticles (bool val) { logMissingArticles = val ; } /* if VAL is true then when an article's contents are read off disk they're modified to include the appropriate carriage returns */ void artBitFiddleContents (bool val) { ASSERT (val == true || bitFiddleContents == false ) ; /* can only set once */ ASSERT (hashTable == NULL) ; /* must be called before any articles made */ bitFiddleContents = val ; } /* set the limit we want to stay under. */ void artSetMaxBytesInUse (u_int val) { ASSERT (maxBytesInUse > 0) ; /* can only set one time. */ ASSERT (val > 0) ; maxBytesInUse = val ; } /**********************************************************************/ /** STATIC FUNCTIONS **/ /**********************************************************************/ /* return a single buffer that contains the disk image of the article (i.e. not fixed up for NNTP). Note that if artBitFiddleContents (true) has been called then the Buffer this functions returns will be fixed up tfor NNTP. */ static Buffer artGetContents (Article article) { Buffer rval = NULL ; if (article->articleOk) { if (article->contents == NULL) fillContents (article) ; if (article->contents != NULL) rval = bufferTakeRef (article->contents) ; } return rval ; } static void artUnmap (Article article, int size) { #ifdef HAVE_MMAP if (munmap(article->mMapping, size) < 0) syslog (LOG_NOTICE, "munmap %s: %m", article->fname) ; #endif article->mMapping = NULL ; } static void logArticleStats (TimeoutId id, void *data) { ASSERT (id == articleStatsId) ; (void) data ; /* keep lint happy */ syslog (LOG_NOTICE,ACTIVE_ARTICLES,articlesInUse,bytesInUse) ; syslog (LOG_NOTICE,ARTICLE_ALLOCS,articleTotal,byteTotal) ; byteTotal = 0 ; articleTotal = 0 ; articleStatsId = prepareSleep (logArticleStats,ARTICLE_STATS_PERIOD,0) ; } /* do the actual read of the article off disk into a Buffer that is stored in the Article object. The Article will end up with its contents field having a buffer with the article data in it. This buffer may be holding a mmapped pointer, or it may be simply a regular buffer with the data read off disk into it. In the regular buffer case the contents may be copied around after reading to insert a carriage return before each newline. */ static bool fillContents (Article article) { int fd ; register char *p; struct stat buf ; static bool maxLimitNotified ; ASSERT (article->contents == NULL) ; if (maxBytesInUse == 0) maxBytesInUse = SOFT_ARTICLE_BYTE_LIMIT ; if (avgCharsPerLine == 0) avgCharsPerLine = 75 ; /* roughly number of characters per line */ #ifdef XXX_RAWHACK if ((fd = RAWartopen(article->fname, article->msgid)) < 0) #else /* XXX_RAWHACK */ if ((fd = open (article->fname,O_RDONLY,0)) < 0) #endif /* XXX_RAWHACK */ { article->articleOk = false ; missingArticleCount++ ; if (logMissingArticles && !article->loggedMissing) { syslog (LOG_NOTICE,NO_ARTICLE,article->msgid,article->fname) ; article->loggedMissing = true ; } } #ifdef XXX_RAWHACK else if (1) #else /* XXX_RAWHACK */ else if (fstat (fd, &buf) < 0) { article->articleOk = false ; syslog (LOG_ERR,FSTAT_FAILURE,article->fname) ; } else if (!S_ISREG (buf.st_mode)) { article->articleOk = false ; syslog (LOG_ERR,REGFILE_FAILURE,article->fname) ; } else if (buf.st_size == 0) { article->articleOk = false ; syslog (LOG_ERR,EMPTY_ARTICLE,article->fname) ; } else #endif /* XXX_RAWHACK */ { char *buffer = NULL ; int amt = 0 ; #ifdef XXX_RAWHACK size_t idx = 0, amtToRead = (size_t) RAWlastartsize; size_t newBufferSize = (size_t) RAWlastartsize; #else /* XXX_RAWHACK */ size_t idx = 0, amtToRead = (size_t) buf.st_size ; size_t newBufferSize = (size_t) buf.st_size ; #endif /* XXX_RAWHACK */ HashEntry h ; if (useMMap) { /* if HAVE_MMAP is not defined, useMMap should never be true, so we don't have to worry about other effects of this block. We only have to make the function compile. */ #ifdef HAVE_MMAP article->mMapping = mmap((caddr_t) 0, (size_t) buf.st_size, PROT_READ, MAP_SHARED, fd, 0); #endif if (article->mMapping == (caddr_t) -1) { /* dunno, but revert to plain reading */ article->mMapping = NULL ; syslog (LOG_NOTICE, MMAP_FAILURE, article->fname) ; } else { article->contents = newBufferByCharP((char *)article->mMapping, (size_t) buf.st_size, (size_t) buf.st_size); buffer = bufferBase (article->contents) ; if ((p = strchr(buffer, '\n')) == NULL) { article->articleOk = false; delBuffer (article->contents) ; article->contents = NULL ; syslog (LOG_NOTICE, MUNGED_ARTICLE, article->fname) ; } else if (p[-1] == '\r') article->inWireFormat = true ; else if (bitFiddleContents) { /* we need to copy the contents into a buffer below */ delBuffer (article->contents) ; article->contents = NULL ; } } } if (article->contents == NULL && article->articleOk) { if (bitFiddleContents) { /* an estimate to give some room for nntpPrepareBuffer to use. */ newBufferSize *= (1.0 + (1.0 / avgCharsPerLine)) ; newBufferSize ++ ; } /* if we're going over the limit try to free up some older article's contents. */ if (amtToRead + bytesInUse > maxBytesInUse) { for (h = chronList ; h != NULL ; h = h->nextTime) { if (artFreeContents (h->article)) if (amtToRead + bytesInUse <= maxBytesInUse) break ; } } /* we we couldn't get below, then log it (one time only) */ if ((amtToRead + bytesInUse) > maxBytesInUse && maxLimitNotified == false) { maxLimitNotified = true ; syslog (LOG_NOTICE,MAX_BYTES_LIMIT,maxBytesInUse, amtToRead + bytesInUse) ; } if ((article->contents = newBuffer (newBufferSize)) == NULL) amtToRead = 0 ; else { buffer = bufferBase (article->contents) ; #ifdef XXX_RAWHACK bytesInUse += RAWlastartsize; byteTotal += RAWlastartsize; #else /* XXX_RAWHACK */ bytesInUse += buf.st_size ; byteTotal += buf.st_size ; #endif /* XXX_RAWHACK */ } if (article->mMapping && buffer != NULL) { (void)memcpy(buffer, (char *) article->mMapping, (int) buf.st_size); artUnmap(article, (int) buf.st_size) ; amtToRead = 0; } while (amtToRead > 0) { if ((amt = read (fd, buffer + idx,amtToRead)) <= 0) { syslog (LOG_ERR,BAD_ART_READ, article->fname) ; #ifdef XXX_RAWHACK bytesInUse -= RAWlastartsize; byteTotal -= RAWlastartsize; #else /* XXX_RAWHACK */ bytesInUse -= buf.st_size ; byteTotal -= buf.st_size ; #endif /* XXX_RAWHACK */ amtToRead = 0 ; delBuffer (article->contents) ; article->contents = NULL ; } else { idx += amt ; amtToRead -= amt ; } } if (article->contents != NULL) { #ifdef XXX_RAWHACK bufferSetDataSize (article->contents, (size_t) RAWlastartsize) ; #else /* XXX_RAWHACK */ bufferSetDataSize (article->contents, (size_t) buf.st_size) ; #endif /* XXX_RAWHACK */ if ((p = strchr(buffer, '\n')) == NULL) { article->articleOk = false; syslog (LOG_NOTICE, MUNGED_ARTICLE, article->fname) ; } else if (p[-1] == '\r') { article->inWireFormat = true ; } else if (bitFiddleContents) if ( nntpPrepareBuffer (article->contents) ) { #ifdef XXX_RAWHACK size_t diff = (bufferDataSize (article->contents) - RAWlastartsize) ; #else /* XXX_RAWHACK */ size_t diff = (bufferDataSize (article->contents) - buf.st_size) ; #endif /* XXX_RAWHACK */ if (((u_int) UINT_MAX) - diff <= preparedBytes) { dprintf (2,"Newline ratio so far: %02.2f\n", ((double) preparedBytes / preparedNewlines)) ; syslog (LOG_NOTICE,PREPARED_NEWLINES, ((double) preparedBytes)/preparedNewlines, preparedBytes,preparedNewlines) ; preparedBytes = 0 ; preparedNewlines = 0 ; rolledOver = true ; } #ifdef XXX_RAWHACK preparedBytes += RAWlastartsize ; #else /* XXX_RAWHACK */ preparedBytes += buf.st_size ; #endif /* XXX_RAWHACK */ preparedNewlines += diff ; bytesInUse += diff ; byteTotal += diff ; if (preparedBytes > (1024 * 1024)) { avgCharsPerLine = ((double) preparedBytes) / preparedNewlines ; avgCharsPerLine++ ; } article->inWireFormat = true ; } else { syslog (LOG_ERR,PREPARE_FAILED) ; #ifdef XXX_RAWHACK bytesInUse -= RAWlastartsize; byteTotal -= RAWlastartsize; #else /* XXX_RAWHACK */ bytesInUse -= buf.st_size ; byteTotal -= buf.st_size ; #endif /* XXX_RAWHACK */ delBuffer (article->contents) ; article->contents = NULL ; } } } } #ifndef XXX_RAWHACK /* If we're not doin' RAW stuff, we should close a valid file descriptor */ if (fd >= 0) close (fd) ; #endif /* ! XXX_RAWHACK */ return (article->contents != NULL ? true : false) ; } /* stick the buffer B into the Buffer array pointed at by BUFFS *BUFFS is reallocated if necessary. NEWSPOT points at the index where B should be put (presumably the end). CURLEN points at the length of the BUFFS and it will get updated if BUFFS is reallocated. */ static void appendBuffer (Buffer b, Buffer **buffs, int *newSpot, int *curLen) { if (*newSpot == *curLen) { *curLen += 10 ; *buffs = REALLOC (*buffs, Buffer, *curLen) ; ASSERT (*buffs != NULL) ; } (*buffs) [(*newSpot)++] = b ; } /* Takes the articles contents buffer and overlays a set of new buffers on top of it. These buffers insert the required carriage return and dot characters as needed */ static bool prepareArticleForNNTP (Article article) { static Buffer dotFirstBuffer ; static Buffer dotBuffer ; static Buffer crlfBuffer ; Buffer *nntpBuffs = NULL ; int buffLen = 0 ; int buffIdx = 0 ; char *start, *end ; Buffer contents = artGetContents (article) ; /* returns a reference */ if (contents == NULL) return false ; else if (article->nntpBuffers != NULL) { delBuffer (contents) ; return true ; /* already done */ } if (dotBuffer == NULL) { dotBuffer = newBufferByCharP (".\r\n",3,3) ; dotFirstBuffer = newBufferByCharP ("\r\n.",3,3) ; crlfBuffer = newBufferByCharP ("\r\n",2,2) ; } /* overlay a set of buffers on top of the articles contents buffer. This is a real speed loss at the moment, so by default it's disabled (by calling artBitFiddleContents(true) in main(). */ if (article->inWireFormat == false) { end = bufferBase (contents) ; do { start = end ; while (*end && *end != '\n') end++ ; appendBuffer (newBufferByCharP (start, (size_t) (end - start), (size_t) (end - start)), &nntpBuffs,&buffIdx,&buffLen) ; if (*end != '\0') end++ ; #ifndef XXX_RAWHACK_CRLFSTORAGE if (*end == '.') /* start of next line is a dot */ appendBuffer (bufferTakeRef (dotFirstBuffer),&nntpBuffs, &buffIdx,&buffLen); else appendBuffer (bufferTakeRef (crlfBuffer),&nntpBuffs, &buffIdx,&buffLen) ; #endif /* ! XXX_RAWHACK_CRLFSTORAGE */ } while (*end != '\0') ; appendBuffer (bufferTakeRef (dotBuffer), &nntpBuffs,&buffIdx,&buffLen) ; appendBuffer (NULL,&nntpBuffs,&buffIdx,&buffLen) ; } else { /* we already fixed the contents up when we read in the article */ nntpBuffs = ALLOC (Buffer, 3) ; ASSERT (nntpBuffs != NULL) ; nntpBuffs [0] = newBufferByCharP (bufferBase (contents), bufferDataSize (contents), bufferDataSize (contents)) ; nntpBuffs [1] = bufferTakeRef (dotBuffer) ; nntpBuffs [2] = NULL ; } delBuffer (contents) ; /* the article is still holding a reference */ article->nntpBuffers = nntpBuffs ; return true ; } /* free the contents of the buffers if article is the only thing holding a reference. Returns true if it could, false if it couldn't */ static bool artFreeContents (Article art) { if (art->contents == NULL) return false ; if (art->nntpBuffers != NULL) if (bufferRefCount (art->nntpBuffers[0]) > 1) return false ; else { freeBufferArray (art->nntpBuffers) ; art->nntpBuffers = NULL ; } ASSERT (bufferRefCount (art->contents) == 1) ; if (art->mMapping) artUnmap(art, bufferSize(art->contents)) ; else bytesInUse -= bufferDataSize (art->contents) ; delBuffer (art->contents) ; art->contents = NULL ; return true ; } /**********************************************************************/ /* Private hash table and routines for storing articles */ /**********************************************************************/ /* Hash function lifted from perl 5 */ static u_int hashString (const char *string) { u_int i ; for (i = 0 ; string && *string ; string++) i = 33 * i + (u_char) *string ; return i ; } /* find the article in the has table and return it. */ static Article hashFindArticle (const char *msgid) { u_int hash = hashString (msgid) ; HashEntry h ; for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next) if (hash == h->hash && strcmp (msgid,h->article->msgid) == 0) break ; return (h == NULL ? NULL : h->article) ; } /* add the article to the hash table. */ static void hashAddArticle (Article article) { u_int hash = hashString (article->msgid) ; HashEntry h ; HashEntry ne ; h = hashTable [TABLE_ENTRY(hash)] ; ne = ALLOC (struct hash_entry_s, 1) ; ASSERT (ne != NULL) ; ne->article = article ; ne->hash = hash ; ne->next = hashTable [TABLE_ENTRY(hash)] ; ne->prev = NULL ; if (h != NULL) h->prev = ne ; hashTable [TABLE_ENTRY(hash)] = ne ; ne->nextTime = chronList ; ne->prevTime = NULL ; if (chronList != NULL) chronList->prevTime = ne ; chronList = ne ; } /* remove the article from the hash table and chronological list. Does not delete the article itself. */ static bool hashRemoveArticle (Article article) { u_int hash = hashString (article->msgid) ; HashEntry h ; for (h = hashTable [TABLE_ENTRY(hash)] ; h != NULL ; h = h->next) if (hash == h->hash && strcmp (article->msgid,h->article->msgid) == 0) break ; if (h == NULL) return false ; if (h == hashTable [TABLE_ENTRY(hash)]) { hashTable [TABLE_ENTRY(hash)] = h->next ; if (h->next != NULL) h->next->prev = NULL ; } else { h->prev->next = h->next ; if (h->next != NULL) h->next->prev = h->prev ; } if (chronList == h) { chronList = h->nextTime ; if (chronList != NULL) chronList->prevTime = NULL ; } else { h->prevTime->nextTime = h->nextTime ; if (h->nextTime != NULL) h->nextTime->prevTime = h->prevTime ; } FREE (h) ; return true ; } #define HASH_VALIDATE_BUCKET_COUNT 1 /* hash buckets to check per call */ static void hashValidateTable (void) { static int hbn = 0 ; int i ; HashEntry he ; #if ! defined (NDEBUG) for (i = 0 ; i < HASH_VALIDATE_BUCKET_COUNT ; i++) { for (he = hashTable [hbn] ; he != NULL ; he = he->next) ASSERT (he->article->refCount > 0) ; if (++hbn >= TABLE_SIZE) hbn = 0 ; } #endif }