# To unbundle, sh this file echo Makefile.diffs 1>&2 sed 's/^X//' >Makefile.diffs <<'!' X*** /tmp/,RCSt1a03004 Sat Aug 17 13:37:22 1991 X--- Makefile Sat Aug 17 12:40:00 1991 X*************** X*** 1,4 **** X! # $Header: Makefile,v 1.3 90/12/22 10:16:02 sob Exp $ X # Makefile for NNTP server X # X X--- 1,4 ---- X! # $Header: Makefile,v 1.4 91/08/17 12:04:13 vixie Locked $ X # Makefile for NNTP server X # X X*************** X*** 6,18 **** X ahbs.o globals.o group.o help.o ihave.o list.o misc.o netaux.o \ X newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \ X slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \ X! batch.o auth.o timer.o ../common/version.o X X SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \ X ahbs.c globals.c group.c help.c ihave.c list.c misc.c netaux.c \ X newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \ X slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \ X! batch.c auth.c timer.c ../common/version.c X X SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h X X--- 6,18 ---- X ahbs.o globals.o group.o help.o ihave.o list.o misc.o netaux.o \ X newgroups.o newnews.o nextlast.o ngmatch.o post.o parsit.o scandir.o \ X slave.o spawn.o strcasecmp.o subnet.o time.o xhdr.o fakesyslog.o \ X! batch.o auth.o timer.o msgid.o ../common/version.o X X SRVRSRC = main.c serve.c access.c access_inet.c access_dnet.c active.c \ X ahbs.c globals.c group.c help.c ihave.c list.c misc.c netaux.c \ X newgroups.c newnews.c nextlast.c ngmatch.c post.c parsit.c scandir.c \ X slave.c spawn.c strcasecmp.c subnet.c time.c xhdr.c fakesyslog.c \ X! batch.c auth.c timer.c msgid.c ../common/version.c X X SRVRINC = common.h ../common/conf.h ../common/nntp.h timer.h X X*************** X*** 21,33 **** X # -ldbm here if you've #define'ed DBM in ../common/conf.h X LIBS = -ldbm X X! CFLAGS = -O X X # Where nntpd is going to live X X! DESTDIR = /etc X X! all: nntpd X X nntpd: ${SRVROBJ} ${SRVRINC} X ${CC} ${CFLAGS} -o nntpd ${SRVROBJ} ${LIBS} X--- 21,33 ---- X # -ldbm here if you've #define'ed DBM in ../common/conf.h X LIBS = -ldbm X X! CFLAGS = -O -I/usr/local/include X X # Where nntpd is going to live X X! DESTDIR = /usr/lib/news/etc X X! all: nntpd msgidd X X nntpd: ${SRVROBJ} ${SRVRINC} X ${CC} ${CFLAGS} -o nntpd ${SRVROBJ} ${LIBS} X*************** X*** 34,48 **** X X ${SRVROBJ}: ${SRVRINC} X X! install: nntpd X cp nntpd ${DESTDIR}/nntpd X chmod 711 ${DESTDIR}/nntpd X X lint: X lint ${SRVRSRC} X X clean: X! -rm -f *.o nntpd make*.out a.out X X distrib: clean X rm -rf SCCS save tags X--- 34,54 ---- X X ${SRVROBJ}: ${SRVRINC} X X! msgidd: msgidd.c X! ${CC} ${CFLAGS} -o msgidd msgidd.c X! X! install: nntpd msgidd X cp nntpd ${DESTDIR}/nntpd X chmod 711 ${DESTDIR}/nntpd X+ mv -f ${DESTDIR}/msgidd ${DESTDIR}/msgidd.old X+ cp msgidd ${DESTDIR}/msgidd X+ chmod 711 ${DESTDIR}/msgidd X X lint: X lint ${SRVRSRC} X X clean: X! -rm -f *.o nntpd msgidd make*.out a.out X X distrib: clean X rm -rf SCCS save tags ! echo README.decwrl 1>&2 sed 's/^X//' >README.decwrl <<'!' NNTP daemon -- DECWRL mods Paul Vixie 17 August 1991 (cleaned up, sniffing sigpipes) Paul Vixie 13 February 1991 (ported to 1.5.11, w/ leres) Paul Vixie 04 October 1990 (improved by Steve Schoch) Paul Vixie 18 September 1990 (improved by ken@sdd.hp.com) Paul Vixie 16 June 1990 (works on 1.5.8) These modifications add a "message ID daemon" to nntpd. Nntpd will query this daemon about all offered message ID's, and the daemon will keep a managed, memory resident list of recent ID's. "Recent" means ID's offered within the last N (~1440, or one full day) minutes. If an ID has been inquired of in the last N minutes, the daemon says "don't accept it", which keeps nntpd from accepting things that have already been accepted but which are sitting in the input batch queue, waiting to be unbatched. This whole scheme is obviously only useful if you run C News, since nntp-for-B-News just forks inews once per incoming article. Installation is hopefully trivial. Rebuild and reinstall nntpd with these diffs applied; install msgidd as well; add this line to rc.local, and execute it by hand (or reboot): echo /usr/lib/news/etc/msgidd \ X -s /usr/lib/news/nntp_msgid \ X -l /usr/lib/news/msgid.log \ X -h 1440 "&" | su news >/dev/console Note that /usr/lib/news/etc is where I keep nntpd and msgidd, and that X/usr/lib/news/nntp_msgid is the unix-domain socket used for the daemon. XFuture enhancements: X -> move all the dbz stuff out of ihave.c and just do it all here; X this will serialize the dbz reads into this one process, which X is debatable and needs more thought. Thanks to ken@sdd.hp.com for: X -> change the protocol so that requesting ID status X doesn't have the side effect of marking the article X as "received". Marking it as received shouldn't be X done until the article has actually been written to X the batch file. Thanks to Steve Schoch for: X -> handle SIGPIPE properly, it's pretty common and I was X just SIG_IGN'ing it. Thanks to James Brister for: X -> msgidd.pid file, useful for SIGHUP'ing for logfile changes ! echo ihave.c.diffs 1>&2 sed 's/^X//' >ihave.c.diffs <<'!' X*** /tmp/,RCSt1a17184 Wed Feb 13 11:37:06 1991 X--- ihave.c Wed Feb 13 11:24:55 1991 X*************** X*** 1,8 **** X #ifndef lint X! static char *sccsid = "@(#)$Header: ihave.c,v 1.18 90/12/23 11:49:59 vixie Locked $"; X #endif X X #include "common.h" X X #ifdef LOG X int ih_accepted; X--- 1,11 ---- X #ifndef lint X! static char *sccsid = "@(#)$Header: ihave.c,v 1.18 90/12/23 11:49:59 sob Exp $"; X #endif X X #include "common.h" X+ #ifdef MSGID X+ #include "msgid.h" X+ #endif /*MSGID*/ X X #ifdef LOG X int ih_accepted; X*************** X*** 23,28 **** X--- 26,32 ---- X char errbuf[2 * NNTP_STRLEN]; X int retcode; X register char *cp; X+ int dup = 0; X X if (!canxfer) X { X*************** X*** 42,49 **** X return; X } X X! cp = gethistent(argv[1], 1); X! if (cp != NULL) { X printf("%d Got it.\r\n", ERR_GOTIT); X (void) fflush(stdout); X #ifdef LOG X--- 46,66 ---- X return; X } X X! #ifdef MSGID X! if (msgid(argv[1], MADD)) X! dup++; X! #endif /*MSGID*/ X! X! if (!dup) { X! cp = gethistent(argv[1], 1); X! if (cp != NULL) { X! dup++; X! #ifdef MSGID X! (void) msgid(argv[1], MOLD); X! #endif /*MSGID*/ X! } X! } X! if (dup) { X printf("%d Got it.\r\n", ERR_GOTIT); X (void) fflush(stdout); X #ifdef LOG ! echo msgid.c 1>&2 sed 's/^X//' >msgid.c <<'!' X/* msgid -- message ID test X * vix 13feb91 [negative caching] X * vix 24may90 [written] X * X * with mods ken@sdd.hp.com 01jul90 X * X * $Header: msgid.c,v 1.6 91/08/17 12:04:11 vixie Locked $ X */ X#include X#include X#include X#include X#ifdef hpux X#include X#include X#endif X#include X#include X#include X#define NEEDMSGS X#include "msgid.h" X#include "../common/conf.h" X#ifdef MSGID X#ifdef WANT_MAIN char hostname[BUFSIZ]; X#else extern char hostname[]; X#endif X#define SERVERTIMEOUT 30 static int s = -1; static int read_answer(); X/* X * Protocol: X * Return value as used here is from the server to us. Note that this X * may not be the same as the return value from msgid(). X * X * 3 message types: X * MCANCEL: Delete an id from the holding queues. Return value X * is non-zero for failure. X * MADD: Check for dup and add as needed. Return value is X * non-zero for dup. X * MHOST: Used to inform the server who is on the other end of this X * nntpd. Return value is non-zero for failure. Used only X * in msgid_init(). X */ X/* X * returns: 0 for ok, 1 for failure X */ msgid_init() X{ X char buf[300]; X struct sockaddr_un n; X static dead_server_count = 0; X s = socket(PF_UNIX, SOCK_STREAM, 0); X if (s < 0) { X syslog(LOG_ERR, "msgid: can't get socket: %m"); X return(1); X } X n.sun_family = AF_UNIX; X (void) strcpy(n.sun_path, SOCKNAME); X if (0 > connect(s, &n, strlen(n.sun_path) + sizeof n.sun_family)) { X close(s); X s = -1; X /* only syslog every 128 messages, so that dead msgidd doesn't X * lead to multi-megabyte syslog files (vix, 13feb91) X */ X if (!(dead_server_count++ % 128)) { X syslog(LOG_ERR, "msgid: connect to %s: %m", SOCKNAME); X } X return(1); X } X (void) strcpy(buf, msgs[MHOST]); X (void) strcat(buf, hostname); X if (write(s, buf, strlen(buf)) < 0) { X close(s); X s = -1; X syslog(LOG_ERR, "msgid: host message write: %m", SOCKNAME); X return(1); X } X return(read_answer()); X} X/* X * returns: nonzero = duplicate, return value doesn't mean much for the X * MADD or MOLD messages X */ int msgid(id, mtype) X char *id; X int mtype; X{ X char *cp, buf[256], *rindex(); X if (s == -1 && msgid_init()) X return(0); X /* X * We need to do this just because gethistent does it X * "in place" so add vs old gets fried ... X * X * If running Bnews, converts "id" to lower case. X * If running Cnews, converts "id" per rfc822. X */ X#ifdef CNEWS X cp = rindex(id, '@'); /* look for @ in message id */ X if (cp != NULL) { X for(; *cp != '\0'; ++cp) X#else X { X for (cp = msg_id; *cp != '\0'; ++cp) X#endif X if (isupper(*cp)) X *cp = tolower(*cp); X } X (void) strcpy(buf, msgs[mtype]); X (void) strcat(buf, id); X if (0 > write(s, buf, strlen(buf))) { X syslog(LOG_ERR, "msgid: write: %m"); X close(s); X s = -1; X return(0); X } X return(read_answer()); X} static int read_answer() X{ X unsigned char c; X fd_set readfds; X struct timeval to; X int i; X FD_ZERO(&readfds); X FD_SET(s, &readfds); X to.tv_sec = SERVERTIMEOUT; X to.tv_usec = 0; X if ((i = select(s+1, &readfds, NULL, NULL, &to)) < 0) { X syslog(LOG_ERR, "msgid: select: %m"); X goto bad; X } X if (i == 0 || FD_ISSET(s, &readfds) == 0 || (i = read(s, &c, 1)) == 0) { X syslog(LOG_ERR, "msgid: read timeout"); X goto bad; X } X if (i < 0) { X syslog(LOG_ERR, "msgid: read: %m"); X goto bad; X } X if (c) X return(1); X return(0); bad: X close(s); X s = -1; X return 0; X} X#ifdef WANT_MAIN main(argc, argv) X int argc; X char *argv[]; X{ X register int n; X char buf[BUFSIZ], cmd[20], id[BUFSIZ]; X if (gethostname(hostname, BUFSIZ)) { X perror("hostname"); X exit(1); X } X (void) printf("host: %s\n", hostname); X if (argc != 1) { X (void) fprintf(stderr, "usage: %s\n", argv[0]); X exit(1); X } X#ifdef LOG_DAEMON X openlog("msgid-test", LOG_PID, LOG_DAEMON); X#else X openlog("msgid-test", LOG_PID); X#endif X while (fputs("cmd msgid: ", stdout), fflush(stdout), fgets(buf, BUFSIZ, stdin)) X if ((n = sscanf(buf, "%[^ \t]%*[ \t]%[^\n]", cmd, id)) == 2) { X if (strcmp(cmd, "cancel") == 0) X (void) printf("%s\n", (msgid(id, MCANCEL) ? "failed" : "ok")); X else if (strcmp(cmd, "add") == 0) X (void) printf("%d\n", msgid(id, MADD)); X /* X (void) printf("%sduplicate\n", X (msgid(id, MADD) ? "" : "not a ")); X */ X else if (strcmp(cmd, "old") == 0) X (void) printf("%s\n", (msgid(id, MOLD) ? "failed" : "ok")); X else X (void) printf("possible cmds are cancel, add, and old\n"); X } else X (void) printf("[%d] possible cmds are cancel, add, and old\n", n); X} X#endif X#endif MSGID ! echo msgid.h 1>&2 sed 's/^X//' >msgid.h <<'!' X/* X * Default place for the socket X */ X#define SOCKNAME "/usr/lib/news/nntp_msgid" X#define PIDFILE "/usr/lib/news/msgidd.pid" X#ifdef NEEDMSGS X/* X * Message types from client to server X */ char *msgs[] = { X "add-", X "can-", X "hst-", X "old-" X}; X#endif X/* X * Messages to clinet side called with (MUST BE IN SAME ORDER AS msgs[]) X */ X#define MADD 0 X#define MCANCEL 1 X#define MHOST 2 X#define MOLD 3 ! echo msgidd.c 1>&2 sed 's/^X//' >msgidd.c <<'!' X/* msgidd -- message ID daemon X * vix 24may90 [written] X * X * with mods ken@sdd.hp.com 01jul90 X * speedups by Geoff Collyer, 26 July 1992 X * X * $Header: msgidd.c,v 1.8 91/05/21 19:47:48 vixie Locked $ X */ X#include X#include X#include X#include X#include X#include X#include X#include X#include X#ifdef hpux X#include X#include X#endif X#define NEEDMSGS X#include "msgid.h" X#define ASSERT(e, m) if (!(e)) {fputs("assert failed... ", stderr);\ X perror(m); exit(1);} X#define STREQ(s1, s2) (*(s1) == *(s2) && strcmp(s1, s2) == 0) X#define STRN_EQ(s1, s2, n) (*(s1) == *(s2) && strncmp(s1, s2, n) == 0) X#define FLAGS_RESETLOG 0x02 X#define FLAGS_FLUSHLOG 0x04 X#define MAX_AGE 10 X#define ALARM_TIME 300 X#define HASHSIZE 1024 X#if 0 X#define dprintf fprintf X#else X#define dprintf (void) X#endif char *malloc(); extern int errno; int log = 0, flags = 0; time_t hold_time = MAX_AGE * 60; char *hosts[100], *lfn, *ptime(); XFILE *logfp = NULL; struct { X int connected; X int connects, drops; X int new, dup, cancel; X int freed; X} stats; struct el { X struct el *next; X time_t age; X int refcnt; X char id[1]; X} *ids[HASHSIZE]; char *months[12] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X}; static int transaction(); static unsigned mkhash(s) register unsigned char *s; X{ X register unsigned hash = 0; X register unsigned char c; X while ((c = *s++) != '\0') X hash += c; X return hash; X} static char * strdup(x) char *x; X{ X register char *y = malloc(strlen(x) + 1); X if (y) X strcpy(y, x); X return y; X} static void savepid () X{ X FILE *pidfp ; X if ((pidfp = fopen (PIDFILE,"w")) == NULL) X return ; X (void) fprintf (pidfp,"%d\n",getpid()) ; X (void) fclose (pidfp) ; X} char * ptime(now) X time_t now; X{ X static char buf[50]; X struct tm *tmp; X tmp = localtime(&now); X (void) sprintf(buf, "%s %2d %02d:%02d:%02d", X months[tmp->tm_mon], tmp->tm_mday, tmp->tm_hour, X tmp->tm_min, tmp->tm_sec); X return (buf); X} static void usage(me) X char *me; X{ X (void) fprintf(stderr, "Usage: %s [options]\n", me); X (void) fprintf(stderr, "Options: -s [%s]\n", X SOCKNAME); X (void) fprintf(stderr, " -l \n"); X (void) fprintf(stderr, " -h \n"); X exit(1); X} static void openlogfile() X{ X if (logfp) X (void) fclose(logfp); X if (log && (logfp = fopen(lfn, "a+")) == NULL) { X syslog(LOG_ERR, "Unable to open %s: %m", lfn); X log = 0; X logfp = NULL; X } X} static void bye() X{ X if (log) X (void) fclose(logfp); X dprintf(stderr,"Bye !!\n"); X exit(0); X} static void resetlog() { X flags |= FLAGS_RESETLOG; X} static void pstats() X{ X char msgbuf[1024]; X if (log) X flags |= FLAGS_FLUSHLOG; X sprintf(msgbuf, "stats: %d connected, %d connects, %d drops, %d dups, %d new, %d cancel, %d freed\n", X stats.connected, stats.connects, stats.drops, stats.dup, stats.new, X stats.cancel, stats.freed); X dprintf(stderr, "%s\n", msgbuf); X syslog(LOG_INFO, msgbuf); X stats.connects = stats.drops = stats.new = stats.cancel = stats.dup = X stats.freed = 0; X alarm(ALARM_TIME); X} int onpipe(); main(argc, argv) X int argc; X char *argv[]; X{ X register char *sn = SOCKNAME; X register int s; X int highest_fd; X struct sockaddr_un n, in; X fd_set clients; X extern char *optarg; X extern int optind; X while ((s = getopt(argc, argv, "l:h:s:")) != EOF) X switch(s) { X case 'h': X hold_time = 60 * atoi(optarg); X if (hold_time <= 0 || hold_time > (24 * 3600)) X usage(argv[0]); X break; X case 's': X sn = strdup(optarg); X break; X case 'l': X log++; X lfn = strdup(optarg); X break; X default: X usage(argv[0]); X break; X } X if (optind != argc) X usage(argv[0]); X X if (log) { X openlogfile(); X if (!log) { X (void) fprintf(stderr, "%s: Unable to open log file (%s)\n", X argv[0], lfn); X exit(1); X } X } X savepid () ; X#ifdef LOG_DAEMON X openlog("msgidd", LOG_PID, LOG_DAEMON); X#else X openlog("msgidd", LOG_PID); X#endif X s = socket(PF_UNIX, SOCK_STREAM, 0); X ASSERT(s>=0, "socket"); X highest_fd = s; X n.sun_family = AF_UNIX; X (void) strcpy(n.sun_path, sn); X (void) unlink(sn); X ASSERT(0<=bind(s, &n, strlen(n.sun_path) +sizeof n.sun_family),n.sun_path); X FD_ZERO(&clients); X listen(s, 5); X if (signal(SIGHUP, SIG_IGN) != SIG_IGN) X signal(SIGHUP, resetlog); X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X signal(SIGINT, bye); X signal(SIGUSR1, bye); /* for profiling, etc. */ X signal(SIGALRM, pstats); X signal(SIGPIPE, onpipe); X alarm(ALARM_TIME); X for (;;) { X register int nfound, fd; X fd_set readfds; X if (flags) { X if (flags & FLAGS_FLUSHLOG) X (void) fflush(logfp); X if (flags & FLAGS_RESETLOG) X openlogfile(); X flags = 0; X } X readfds = clients; /* we want to select the clients... */ X FD_SET(s, &readfds); /* ...and the main server socket. */ X nfound = select(highest_fd+1, &readfds, NULL, NULL, NULL); X if (nfound < 0 && errno == EINTR) X continue; X ASSERT(0<=nfound, "select"); X for (fd = 0; fd <= highest_fd; fd++) { X if (FD_ISSET(fd, &readfds)) { X if (fd == s) { X int fromlen = sizeof(in); X if ((fd = accept(s, &in, &fromlen)) == -1) { X syslog(LOG_ERR, "Accept failed: %m"); X } else { X FD_SET(fd, &clients); X if (fd > highest_fd) X highest_fd = fd; X stats.connects++; X stats.connected++; X } X } else if (FD_ISSET(fd, &clients)) { X if (-1 == transaction(fd)) { X close(fd); X FD_CLR(fd, &clients); X stats.connected--; X stats.drops++; X if (hosts[fd]) { X if (log) { X (void) fprintf(logfp, "%s Disconnect %s\n", X ptime(time((time_t *)0)), hosts[fd]); X } X dprintf(stderr, "Disconnect(%d/%s)\n", X fd, hosts[fd]); X free(hosts[fd]); X hosts[fd] = NULL; X } X } X } else { X dprintf(stderr, "Bad fd %d from select\n", fd); X } X } X } X } X} int sigpiped; static int reply(fd, ans) X register int fd, ans; X{ X int status; X sigpiped = 0; X while ((status = write(fd, (ans ? "\001" : "\000"), 1)) < 0 X && errno == EINTR && !sigpiped) X ; X if (status < 0) { X if (log) { X register time_t now = time((long *)0); X fprintf(logfp, "%s write failed (fd %d)\n", ptime(now), fd); X } X syslog(LOG_ERR, "write failed, closing connection: %m"); X return -1; X } X return (0); X} static void cancel(fd, bufp, now) X register int fd; X register char *bufp; X time_t now; X{ X register struct el *i, *p; X register int found = 0, hash = mkhash(bufp) % HASHSIZE; X for (i = ids[hash], p = NULL; i; p = i, i = i->next) X if (STREQ(i->id, bufp)) { X if (p == NULL) X ids[hash] = i->next; X else X p->next = i->next; X if (log) X (void) fprintf(logfp, "%s Cancel %s %s\n", X ptime(now), hosts[fd] ? hosts[fd] : "", i->id); X free(i); X found++; X stats.cancel++; X dprintf(stderr, "Cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp); X break; X } X if (!found) { X dprintf(stderr, "Bad-cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp); X syslog(LOG_ERR, "Cancel, %s not found", bufp); X if (log) X (void) fprintf(logfp, "%s Error cancel %s %s\n", X ptime(now), hosts[fd] ? hosts[fd] : "", i->id); X } X} static void add(fd, bufp, n, now) X register int fd; X register char *bufp; X register int n; X time_t now; X{ X register struct el *i; X register int hash = mkhash(bufp) % HASHSIZE; X /* this malloc includes the id[1] array, which means X * that there's already room for strcpy's null X */ X i = (struct el *) malloc(sizeof(struct el) + n); X if (i == NULL) X return; X i->age = now; X (void) strcpy(i->id, bufp); X if (log) { X i->refcnt = 1; X (void) fprintf(logfp, "%s Add %s %s\n", ptime(now), X hosts[fd] ? hosts[fd] : "", bufp); X } X i->next = ids[hash]; X ids[hash] = i; X stats.new++; X dprintf(stderr, "Add(%d/%s): `%s'\n", fd, hosts[fd], bufp); X} static int search(fd, bufp, now) X register int fd; X char *bufp; /* no leading '<' */ X time_t now; X{ X register struct el *i, *p; X register char bufc = *bufp; X register int hash = mkhash(bufp) % HASHSIZE; X int found = 0, searched = 0; X /* X * search the appropriate list. X */ X for (i = ids[hash], p = NULL; i; p = i, i = i->next) { X /* X * if too old, everything from here to the end X * can be nuked (we always add at the top). X */ X if ((now - i->age) > hold_time) { X while (i) { X register struct el *n = i->next; X if (p) X p->next = n; X else X ids[hash] = n; X free((char *)i); X i = n; X stats.freed++; X } X break; X } X searched++; X if (STREQ(i->id, bufp)) { X if (log) { X i->refcnt++; X (void) fprintf(logfp, "%s Lose %s %d %ld %s\n", X ptime(now), hosts[fd] ? hosts[fd] : "", i->refcnt, X (now - i->age), i->id); X } X found++; X stats.dup++; X break; X } X } X dprintf(stderr, "Search(%d/%s): %s(%d) `%s'\n", X fd, hosts[fd], (found ? "dup" : "new"), searched, bufp); X return (found); X} X/* returns: -1 == client is gone, close this fd please X * 0 == success X */ static int transaction(fd) X register fd; X{ X char buf[1023]; X register int n; X register char *bufp, *cmdp; X register time_t now = time((long *)0); X /* read the request. zero-length read means connection is gone. X */ X do { X n = read(fd, buf, sizeof(buf)); X if (n == 0) X return -1; X } while (n < 0 && errno == EINTR); X ASSERT(n>0, "read"); X if (hosts[fd]) { X dprintf(stderr, "Parse(%d/%s): `%s'\n", fd, hosts[fd], buf); X } X /* Separate cmd from id X */ X cmdp = buf; X bufp = buf + 4; X n -= 4; X /* find the first useful character, saving it and its address. X */ X if (*bufp == '<') { X bufp++; X n--; X } X /* rip out useless characters at end, remembering real length. X */ X while (n > 0) { X register x = n - 1; X register ch = bufp[x]; X if (ch == '\n' || ch == '\r' || ch == '>') X n = x; X else X break; X } X bufp[n] = '\0'; X /* X * Which cmd ? X */ X if (STRN_EQ(cmdp, msgs[MCANCEL], 4)) { X cancel(fd, bufp, now); X return reply(fd, 0); X } else if (STRN_EQ(cmdp, msgs[MADD], 4)) { X if (search(fd, bufp, now)) X return reply(fd, 1); X else { X add(fd, bufp, n, now); X return reply(fd, 0); X } X } else if (STRN_EQ(cmdp, msgs[MOLD], 4)) { X if (log) X (void) fprintf(logfp, "%s Old %s %s\n", ptime(now), X hosts[fd] ? hosts[fd] : "", bufp); X dprintf(stderr, "Old(%d/%s): `%s'\n", fd, hosts[fd], bufp); X return reply(fd, 0); X } else if (STRN_EQ(cmdp, msgs[MHOST], 4)) { X if (hosts[fd]) X free(hosts[fd]); X hosts[fd] = strdup(bufp); X if (log) X (void) fprintf(logfp, "%s Connect %s\n", ptime(now), hosts[fd]); X dprintf(stderr, "Connect(%d/%s)\n", fd, hosts[fd]); X return reply(fd, 0); X } else { X syslog(LOG_ERR, "Unknown command %s", cmdp); X if (log) X (void) fprintf(logfp, "%s Error %s unknown-cmd %s\n", ptime(now), X hosts[fd], cmdp); X dprintf(stderr, "Error(%d/%s) unknown-cmd %s\n", fd, hosts[fd], cmdp); X return reply(fd, 0); X } X#ifdef lint X /*NOTREACHED*/ X return (0); X#endif X} onpipe() X{ X register time_t now = time((long *)0); X if (log) X fprintf(logfp, "%s Got SIGPIPE\n", ptime(now)); X sigpiped++; X} !