chiark / gitweb /
REORG Delete everything that's not innduct or build system or changed for innduct
[innduct.git] / innd / site.c
diff --git a/innd/site.c b/innd/site.c
deleted file mode 100644 (file)
index 09c405e..0000000
+++ /dev/null
@@ -1,1246 +0,0 @@
-/*  $Id: site.c 7748 2008-04-06 13:49:56Z iulius $
-**
-**  Routines to implement site-feeding.  Mainly working with channels to
-**  do buffering and determine what to send.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-
-#include "inn/innconf.h"
-#include "innd.h"
-
-
-static int     SITEcount;
-static int     SITEhead = NOSITE;
-static int     SITEtail = NOSITE;
-static char    SITEshell[] = _PATH_SH;
-
-
-/*
-**  Called when input is ready to read.  Shouldn't happen.
-*/
-static void
-SITEreader(CHANNEL *cp)
-{
-    syslog(L_ERROR, "%s internal SITEreader: %s", LogName, CHANname(cp));
-}
-
-
-/*
-**  Called when write is done.  No-op.
-*/
-static void
-SITEwritedone(CHANNEL *cp UNUSED)
-{
-}
-
-
-/*
-**  Make a site start spooling.
-*/
-static bool
-SITEspool(SITE *sp, CHANNEL *cp)
-{
-    int                        i;
-    char               buff[SPOOLNAMEBUFF];
-    char               *name;
-
-    name = sp->SpoolName;
-    i = open(name, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
-    if (i < 0 && errno == EISDIR) {
-       FileGlue(buff, sp->SpoolName, '/', "togo");
-       name = buff;
-       i = open(buff, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
-    }
-    if (i < 0) {
-       i = errno;
-       syslog(L_ERROR, "%s cant open %s %m", sp->Name, name);
-       IOError("site batch file", i);
-       sp->Channel = NULL;
-       return false;
-    }
-    if (cp) {
-      if (cp->fd >= 0)
-        /* syslog(L_ERROR, "DEBUG ERROR SITEspool trashed:%d %s:%d", cp->fd, sp->Name, i);
-          CPU-eating bug, killed by kre. */
-       WCHANremove(cp);
-       RCHANremove(cp);
-       SCHANremove(cp);
-       close(cp->fd);
-       cp->fd = i;
-       return true;
-    }
-    sp->Channel = CHANcreate(i, CTfile, CSwriting, SITEreader, SITEwritedone);
-    if (sp->Channel == NULL) {
-       syslog(L_ERROR, "%s cant channel %m", sp->Name);
-       close(i);
-       return false;
-    }
-    WCHANset(sp->Channel, "", 0);
-    sp->Spooling = true;
-    return true;
-}
-
-
-/*
-**  Delete a site from the file writing list.  Can be called even if
-**  site is not on the list.
-*/
-static void
-SITEunlink(SITE *sp)
-{
-    if (sp->Next != NOSITE || sp->Prev != NOSITE
-     || (SITEhead != NOSITE && sp == &Sites[SITEhead]))
-       SITEcount--;
-
-    if (sp->Next != NOSITE)
-       Sites[sp->Next].Prev = sp->Prev;
-    else if (SITEtail != NOSITE && sp == &Sites[SITEtail])
-       SITEtail = sp->Prev;
-
-    if (sp->Prev != NOSITE)
-       Sites[sp->Prev].Next = sp->Next;
-    else if (SITEhead != NOSITE && sp == &Sites[SITEhead])
-       SITEhead = sp->Next;
-
-    sp->Next = sp->Prev = NOSITE;
-}
-
-
-/*
-**  Find the oldest "file feed" site and buffer it.
-*/
-static void
-SITEbufferoldest(void)
-{
-    SITE               *sp;
-    struct buffer       *bp;
-    struct buffer       *out;
-
-    /* Get the oldest user of a file. */
-    if (SITEtail == NOSITE) {
-       syslog(L_ERROR, "%s internal no oldest site found", LogName);
-       SITEcount = 0;
-       return;
-    }
-
-    sp = &Sites[SITEtail];
-    SITEunlink(sp);
-
-    if (sp->Buffered) {
-       syslog(L_ERROR, "%s internal oldest (%s) was buffered", LogName,
-           sp->Name);
-       return;
-    }
-
-    if (sp->Type != FTfile) {
-       syslog(L_ERROR, "%s internal oldest (%s) not FTfile", LogName,
-           sp->Name);
-       return;
-    }
-
-    /* Write out what we can. */
-    WCHANflush(sp->Channel);
-
-    /* Get a buffer for the site. */
-    sp->Buffered = true;
-    bp = &sp->Buffer;
-    bp->used = 0;
-    bp->left = 0;
-    if (bp->size == 0) {
-       bp->size = sp->Flushpoint;
-       bp->data = xmalloc(bp->size);
-    }
-    else {
-       bp->size = sp->Flushpoint;
-        bp->data = xrealloc(bp->data, bp->size);
-    }
-
-    /* If there's any unwritten data, copy it. */
-    out = &sp->Channel->Out;
-    if (out->left) {
-        buffer_set(bp, &out->data[out->used], out->left);
-       out->left = 0;
-    }
-
-    /* Now close the original channel. */
-    CHANclose(sp->Channel, sp->Name);
-    sp->Channel = NULL;
-}
-
-/*
- * *  Bilge Site's Channel out buffer.
- */
-static bool
-SITECHANbilge(SITE *sp)
-{
-    int             fd;
-    int             i;
-    char            buff[SPOOLNAMEBUFF];
-    char           *name;
-
-    name = sp->SpoolName;
-    fd = open(name, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
-    if (fd < 0 && errno == EISDIR) {
-        FileGlue(buff, sp->SpoolName, '/', "togo");
-        name = buff;
-        fd = open(buff, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
-    }
-    if (fd < 0) {
-       int oerrno = errno ;
-        syslog(L_ERROR, "%s cant open %s %m", sp->Name, name);
-        IOError("site batch file",oerrno);
-        sp->Channel = NULL;
-        return false;
-    }
-    while (sp->Channel->Out.left > 0) {
-        i = write(fd, &sp->Channel->Out.data[sp->Channel->Out.used],
-                  sp->Channel->Out.left);
-        if(i <= 0) {
-            syslog(L_ERROR,"%s cant spool count %lu", CHANname(sp->Channel),
-                (unsigned long) sp->Channel->Out.left);
-            close(fd);
-            return false;
-        }
-        sp->Channel->Out.left -= i;
-        sp->Channel->Out.used += i;
-    }
-    close(fd);
-    free(sp->Channel->Out.data);
-    sp->Channel->Out.data = xmalloc(SMBUF);
-    sp->Channel->Out.size = SMBUF;
-    sp->Channel->Out.left = 0;
-    sp->Channel->Out.used = 0;
-    return true;
-}
-
-/*
-**  Check if we need to write out the site's buffer.  If we're buffered
-**  or the feed is backed up, this gets a bit complicated.
-*/
-static void
-SITEflushcheck(SITE *sp, struct buffer *bp)
-{
-    int                        i;
-    CHANNEL            *cp;
-
-    /* If we're buffered, and we hit the flushpoint, do an LRU. */
-    if (sp->Buffered) {
-       if (bp->left < sp->Flushpoint)
-           return;
-       while (SITEcount >= MaxOutgoing)
-           SITEbufferoldest();
-       if (!SITEsetup(sp) || sp->Buffered) {
-           syslog(L_ERROR, "%s cant unbuffer %m", sp->Name);
-           return;
-       }
-       WCHANsetfrombuffer(sp->Channel, bp);
-       WCHANadd(sp->Channel);
-       /* Reset buffer; data has been moved. */
-       buffer_set(bp, "", 0);
-    }
-
-    if (PROCneedscan)
-       PROCscan();
-
-    /* Handle buffering. */
-    cp = sp->Channel;
-    i = cp->Out.left;
-    if (i < sp->StopWriting)
-       WCHANremove(cp);
-    if ((sp->StartWriting == 0 || i > sp->StartWriting)
-     && !CHANsleeping(cp)) {
-       if (sp->Type == FTchannel) {    /* channel feed, try the write */
-           int j;
-           if (bp->left == 0)
-               return;
-           j = write(cp->fd, &bp->data[bp->used], bp->left);
-           if (j > 0) {
-               bp->left -= j;
-               bp->used += j;
-               i = cp->Out.left;
-               /* Since we just had a successful write, we need to clear the 
-                * channel's error counts. - dave@jetcafe.org */
-               cp->BadWrites = 0;
-               cp->BlockedWrites = 0;
-           }
-           if (bp->left <= 0) {
-                /* reset Used, Left on bp, keep channel buffer size from
-                   exploding. */
-                bp->used = bp->left = 0;
-               WCHANremove(cp);
-            } else
-               WCHANadd(cp);
-       }
-       else
-           WCHANadd(cp);
-    }
-
-    cp->LastActive = Now.time;
-
-    /* If we're a channel that's getting big, see if we need to spool. */
-    if (sp->Type == FTfile || sp->StartSpooling == 0 || i < sp->StartSpooling)
-       return;
-
-    syslog(L_ERROR, "%s spooling %d bytes", sp->Name, i);
-    if(!SITECHANbilge(sp)) {
-       syslog(L_ERROR, "%s overflow %d bytes", sp->Name, i);
-       return;
-    }
-}
-
-
-/*
-**  Send a control line to an exploder.
-*/
-void
-SITEwrite(SITE *sp, const char *text)
-{
-    static char                PREFIX[] = { EXP_CONTROL, '\0' };
-    struct buffer       *bp;
-
-    if (sp->Buffered)
-       bp = &sp->Buffer;
-    else {
-       if (sp->Channel == NULL)
-           return;
-       sp->Channel->LastActive = Now.time;
-       bp = &sp->Channel->Out;
-    }
-    buffer_append(bp, PREFIX, strlen(PREFIX));
-    buffer_append(bp, text, strlen(text));
-    buffer_append(bp, "\n", 1);
-    if (sp->Channel != NULL)
-       WCHANadd(sp->Channel);
-}
-
-
-/*
-**  Send the desired data about an article down a channel.
-*/
-static void
-SITEwritefromflags(SITE *sp, ARTDATA *Data)
-{
-    HDRCONTENT         *hc = Data->HdrContent;
-    static char                ITEMSEP[] = " ";
-    static char                NL[] = "\n";
-    char               pbuff[12];
-    char               *p;
-    bool               Dirty;
-    struct buffer       *bp;
-    SITE               *spx;
-    int                        i;
-
-    if (sp->Buffered)
-       bp = &sp->Buffer;
-    else {
-       /* This should not happen, but if we tried to spool and failed,
-        * e.g., because of a bad F param for this site, we can get
-        * into this state.  We already logged a message so give up. */
-       if (sp->Channel == NULL)
-           return;
-       bp = &sp->Channel->Out;
-    }
-    for (Dirty = false, p = sp->FileFlags; *p; p++) {
-       switch (*p) {
-       default:
-           syslog(L_ERROR, "%s internal SITEwritefromflags %c", sp->Name, *p);
-           continue;
-       case FEED_BYTESIZE:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->Bytes + sizeof("Bytes: ") - 1,
-                          Data->BytesLength);
-           break;
-       case FEED_FULLNAME:
-       case FEED_NAME:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->TokenText, sizeof(TOKEN) * 2 + 2);
-           break;
-       case FEED_HASH:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, "[", 1);
-           buffer_append(bp, HashToText(*(Data->Hash)), sizeof(HASH)*2);
-           buffer_append(bp, "]", 1);
-           break;
-       case FEED_HDR_DISTRIB:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, HDR(HDR__DISTRIBUTION),
-                          HDR_LEN(HDR__DISTRIBUTION));
-           break;
-       case FEED_HDR_NEWSGROUP:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, HDR(HDR__NEWSGROUPS), HDR_LEN(HDR__NEWSGROUPS));
-           break;
-       case FEED_HEADERS:
-           if (Dirty)
-               buffer_append(bp, NL, strlen(NL));
-           buffer_append(bp, Data->Headers.data, Data->Headers.left);
-           break;
-       case FEED_OVERVIEW:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->Overview.data, Data->Overview.left);
-           break;
-       case FEED_PATH:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-            if (!Data->Hassamepath || Data->AddAlias || Pathcluster.used) {
-                if (Pathcluster.used)
-                    buffer_append(bp, Pathcluster.data, Pathcluster.used);
-                buffer_append(bp, Path.data, Path.used);
-                if (Data->AddAlias)
-                    buffer_append(bp, Pathalias.data, Pathalias.used);
-            }
-            if (Data->Hassamecluster)
-                buffer_append(bp, HDR(HDR__PATH) + Pathcluster.used,
-                              HDR_LEN(HDR__PATH) - Pathcluster.used);
-            else
-                buffer_append(bp, HDR(HDR__PATH), HDR_LEN(HDR__PATH));
-           break;
-       case FEED_REPLIC:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->Replic, Data->ReplicLength);
-           break;
-       case FEED_STOREDGROUP:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->Newsgroups.List[0],
-                          Data->StoredGroupLength);
-           break;
-       case FEED_TIMERECEIVED:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Arrived);
-           buffer_append(bp, pbuff, strlen(pbuff));
-           break;
-       case FEED_TIMEPOSTED:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Posted);
-           buffer_append(bp, pbuff, strlen(pbuff));
-           break;
-       case FEED_TIMEEXPIRED:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Expires);
-           buffer_append(bp, pbuff, strlen(pbuff));
-           break;
-       case FEED_MESSAGEID:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, HDR(HDR__MESSAGE_ID), HDR_LEN(HDR__MESSAGE_ID));
-           break;
-       case FEED_FNLNAMES:
-           if (sp->FNLnames.data) {
-               /* Funnel; write names of our sites that got it. */
-               if (Dirty)
-                   buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-               buffer_append(bp, sp->FNLnames.data, sp->FNLnames.used);
-           }
-           else {
-               /* Not funnel; write names of all sites that got it. */
-               for (spx = Sites, i = nSites; --i >= 0; spx++)
-                   if (spx->Sendit) {
-                       if (Dirty)
-                           buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-                       buffer_append(bp, spx->Name, spx->NameLength);
-                       Dirty = true;
-                   }
-           }
-           break;
-       case FEED_NEWSGROUP:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           if (sp->ng)
-               buffer_append(bp, sp->ng->Name, sp->ng->NameLength);
-           else
-               buffer_append(bp, "?", 1);
-           break;
-       case FEED_SITE:
-           if (Dirty)
-               buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
-           buffer_append(bp, Data->Feedsite, Data->FeedsiteLength);
-           break;
-       }
-       Dirty = true;
-    }
-    if (Dirty) {
-       buffer_append(bp, "\n", 1);
-       SITEflushcheck(sp, bp);
-    }
-}
-
-
-/*
-**  Send one article to a site.
-*/
-void
-SITEsend(SITE *sp, ARTDATA *Data)
-{
-    int                        i;
-    char               *p;
-    char               *temp;
-    char               buff[BUFSIZ];
-    char *             argv[MAX_BUILTIN_ARGV];
-
-    switch (sp->Type) {
-    default:
-       syslog(L_ERROR, "%s internal SITEsend type %d", sp->Name, sp->Type);
-       break;
-    case FTlogonly:
-       break;
-    case FTfunnel:
-       syslog(L_ERROR, "%s funnel_send", sp->Name);
-       break;
-    case FTfile:
-    case FTchannel:
-    case FTexploder:
-       SITEwritefromflags(sp, Data);
-       break;
-    case FTprogram:
-       /* Set up the argument vector. */
-       if (sp->FNLwantsnames) {
-           i = strlen(sp->Param) + sp->FNLnames.used;
-           if (i + (sizeof(TOKEN) * 2) + 3 >= sizeof buff) {
-               syslog(L_ERROR, "%s toolong need %lu for %s",
-                   sp->Name, (unsigned long) (i + (sizeof(TOKEN) * 2) + 3),
-                   sp->Name);
-               break;
-           }
-           temp = xmalloc(i + 1);
-           p = strchr(sp->Param, '*');
-           *p = '\0';
-           strlcpy(temp, sp->Param, i + 1);
-           strlcat(temp, sp->FNLnames.data, i + 1);
-           strlcat(temp, &p[1], i + 1);
-           *p = '*';
-           snprintf(buff, sizeof(buff), temp, Data->TokenText);
-           free(temp);
-       }
-       else
-           snprintf(buff, sizeof(buff), sp->Param, Data->TokenText);
-
-       if (NeedShell(buff, (const char **)argv, (const char **)ARRAY_END(argv))) {
-           argv[0] = SITEshell;
-           argv[1] = (char *) "-c";
-           argv[2] = buff;
-           argv[3] = NULL;
-       }
-
-       /* Start the process. */
-       i = Spawn(sp->Nice, 0, (int)fileno(Errlog), (int)fileno(Errlog), argv);
-       if (i >= 0)
-           PROCwatch(i, -1);
-       break;
-    }
-}
-
-
-/*
-**  The channel was sleeping because we had to spool our output to
-**  a file.  Flush and restart.
-*/
-static void
-SITEspoolwake(CHANNEL *cp)
-{
-    SITE       *sp;
-    int                *ip;
-
-    ip = (int *) cp->Argument;
-    sp = &Sites[*ip];
-    free(cp->Argument);
-    cp->Argument = NULL;
-    if (sp->Channel != cp) {
-       syslog(L_ERROR, "%s internal SITEspoolwake %s got %d, not %d",
-           LogName, sp->Name, cp->fd, sp->Channel->fd);
-        return;
-    }
-    syslog(L_NOTICE, "%s spoolwake", sp->Name);
-    SITEflush(sp, true);
-}
-
-
-/*
-**  Start up a process for a channel, or a spool to a file if we can't.
-**  Create a channel for the site to talk to.
-*/
-static bool
-SITEstartprocess(SITE *sp)
-{
-    pid_t              i;
-    char               *argv[MAX_BUILTIN_ARGV];
-    char               *process;
-    int                        *ip;
-    int                        pan[2];
-
-#if HAVE_SOCKETPAIR
-    /* Create a socketpair. */
-    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pan) < 0) {
-       syslog(L_ERROR, "%s cant socketpair %m", sp->Name);
-       return false;
-    }
-#else
-    /* Create a pipe. */
-    if (pipe(pan) < 0) {
-       syslog(L_ERROR, "%s cant pipe %m", sp->Name);
-       return false;
-    }
-#endif
-    close_on_exec(pan[PIPE_WRITE], true);
-
-    /* Set up the argument vector. */
-    process = xstrdup(sp->Param);
-    if (NeedShell(process, (const char **)argv, (const char **)ARRAY_END(argv))) {
-       argv[0] = SITEshell;
-       argv[1] = (char *) "-c";
-       argv[2] = process;
-       argv[3] = NULL;
-    }
-
-    /* Fork a child. */
-    i = Spawn(sp->Nice, pan[PIPE_READ], (int)fileno(Errlog),
-             (int)fileno(Errlog), argv);
-    if (i > 0) {
-       sp->pid = i;
-       sp->Spooling = false;
-       sp->Process = PROCwatch(i, sp - Sites);
-       close(pan[PIPE_READ]);
-       sp->Channel = CHANcreate(pan[PIPE_WRITE],
-                       sp->Type == FTchannel ? CTprocess : CTexploder,
-                       CSwriting, SITEreader, SITEwritedone);
-       free(process);
-       return true;
-    }
-
-    /* Error.  Switch to spooling. */
-    syslog(L_ERROR, "%s cant spawn spooling %m", sp->Name);
-    close(pan[PIPE_WRITE]);
-    close(pan[PIPE_READ]);
-    free(process);
-    if (!SITEspool(sp, (CHANNEL *)NULL))
-       return false;
-
-    /* We'll try to restart the channel later. */
-    syslog(L_ERROR, "%s cant spawn spooling %m", sp->Name);
-    ip = xmalloc(sizeof(int));
-    *ip = sp - Sites;
-    SCHANadd(sp->Channel, Now.time + innconf->chanretrytime, NULL,
-             SITEspoolwake, ip);
-    return true;
-}
-
-
-/*
-**  Set up a site for internal buffering.
-*/
-static void
-SITEbuffer(SITE *sp)
-{
-    struct buffer *bp;
-
-    SITEunlink(sp);
-    sp->Buffered = true;
-    sp->Channel = NULL;
-    bp = &sp->Buffer;
-    buffer_resize(bp, sp->Flushpoint);
-    buffer_set(bp, "", 0);
-    syslog(L_NOTICE, "%s buffered", sp->Name);
-}
-
-
-/*
-**  Link a site at the head of the "currently writing to a file" list
-*/
-static void
-SITEmovetohead(SITE *sp)
-{
-    if ((SITEhead == NOSITE) && ((sp->Next != NOSITE) || (sp->Prev != NOSITE)))
-       SITEunlink(sp);
-
-    if ((sp->Next = SITEhead) != NOSITE)
-       Sites[SITEhead].Prev = sp - Sites;
-    sp->Prev = NOSITE;
-
-    SITEhead = sp - Sites;
-    if (SITEtail == NOSITE)
-       SITEtail = sp - Sites;
-
-    SITEcount++;
-}
-
-
-/*
-**  Set up a site's feed.  This means opening a file or channel if needed.
-*/
-bool
-SITEsetup(SITE *sp)
-{
-    int                        fd;
-    int                        oerrno;
-
-    switch (sp->Type) {
-    default:
-       syslog(L_ERROR, "%s internal SITEsetup %d",
-           sp->Name, sp->Type);
-       return false;
-    case FTfunnel:
-    case FTlogonly:
-    case FTprogram:
-       /* Nothing to do here. */
-       break;
-    case FTfile:
-       if (SITEcount >= MaxOutgoing)
-           SITEbuffer(sp);
-       else {
-           sp->Buffered = false;
-           fd = open(sp->Param, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
-           if (fd < 0) {
-               if (errno == EMFILE) {
-                   syslog(L_ERROR, "%s cant open %s %m", sp->Name, sp->Param);
-                   SITEbuffer(sp);
-                   break;
-               }
-               oerrno = errno;
-               syslog(L_NOTICE, "%s cant open %s %m", sp->Name, sp->Param);
-               IOError("site file", oerrno);
-               return false;
-           }
-           SITEmovetohead(sp);
-           sp->Channel = CHANcreate(fd, CTfile, CSwriting,
-                           SITEreader, SITEwritedone);
-           syslog(L_NOTICE, "%s opened %s", sp->Name, CHANname(sp->Channel));
-           WCHANset(sp->Channel, "", 0);
-       }
-       break;
-    case FTchannel:
-    case FTexploder:
-       if (!SITEstartprocess(sp))
-           return false;
-       syslog(L_NOTICE, "%s spawned %s", sp->Name, CHANname(sp->Channel));
-       WCHANset(sp->Channel, "", 0);
-       WCHANadd(sp->Channel);
-       break;
-    }
-    return true;
-}
-
-
-/*
-**  A site's channel process died; restart it.
-*/
-void
-SITEprocdied(SITE *sp, int process, PROCESS *pp)
-{
-    syslog(pp->Status ? L_ERROR : L_NOTICE, "%s exit %d elapsed %ld pid %ld",
-       sp->Name ? sp->Name : "?", pp->Status,
-       (long) (pp->Collected - pp->Started), (long) pp->Pid);
-    if (sp->Process != process || sp->Name == NULL)
-       /* We already started a new process for this channel
-        * or this site has been dropped. */
-       return;
-    if (sp->Channel != NULL)
-       CHANclose(sp->Channel, CHANname(sp->Channel));
-    sp->Working = SITEsetup(sp);
-    if (!sp->Working) {
-       syslog(L_ERROR, "%s cant restart %m", sp->Name);
-       return;
-    }
-    syslog(L_NOTICE, "%s restarted", sp->Name);
-}
-
-/*
-**  A channel is about to be closed; see if any site cares.
-*/
-void
-SITEchanclose(CHANNEL *cp)
-{
-    int                        i;
-    SITE               *sp;
-    int                        *ip;
-
-    for (i = nSites, sp = Sites; --i >= 0; sp++)
-       if (sp->Channel == cp) {
-           /* Found the site that has this channel.  Start that
-            * site spooling, copy any data that might be pending,
-            * and arrange to retry later. */
-           if (!SITEspool(sp, (CHANNEL *)NULL)) {
-               syslog(L_ERROR, "%s loss %lu bytes", sp->Name,
-                       (unsigned long) cp->Out.left);
-               return;
-           }
-           WCHANsetfrombuffer(sp->Channel, &cp->Out);
-           WCHANadd(sp->Channel);
-           ip = xmalloc(sizeof(int));
-           *ip = sp - Sites;
-           SCHANadd(sp->Channel, Now.time + innconf->chanretrytime, NULL,
-                     SITEspoolwake, ip);
-           break;
-       }
-}
-
-
-/*
-**  Flush any pending data waiting to be sent.
-*/
-void
-SITEflush(SITE *sp, const bool Restart)
-{
-    CHANNEL            *cp;
-    struct buffer       *out;
-    int                        count;
-
-    if (sp->Name == NULL)
-       return;
-
-    if (Restart)
-       SITEforward(sp, "flush");
-
-    switch (sp->Type) {
-    default:
-       syslog(L_ERROR, "%s internal SITEflush %d", sp->Name, sp->Type);
-       return;
-
-    case FTlogonly:
-    case FTprogram:
-    case FTfunnel:
-       /* Nothing to do here. */
-       return;
-
-    case FTchannel:
-    case FTexploder:
-       /* If spooling, close the file right now -- documented behavior. */
-       if (sp->Spooling && (cp = sp->Channel) != NULL) {
-           WCHANflush(cp);
-           CHANclose(cp, CHANname(cp));
-           sp->Channel = NULL;
-       }
-       break;
-
-    case FTfile:
-       /* We must ensure we have a file open for this site, so if
-        * we're buffered we HACK and pretend we have no sites
-        * for a moment. */
-       if (sp->Buffered) {
-           count = SITEcount;
-           SITEcount = 0;
-           if (!SITEsetup(sp) || sp->Buffered)
-               syslog(L_ERROR, "%s cant unbuffer to flush", sp->Name);
-           else
-               buffer_swap(&sp->Buffer, &sp->Channel->Out);
-           SITEcount += count;
-       }
-       break;
-    }
-
-    /* We're only dealing with files and channels now. */
-    if ((cp = sp->Channel) != NULL)
-       WCHANflush(cp);
-
-    /* Restart the site, copy any pending data. */
-    if (Restart) {
-       if (!SITEsetup(sp))
-           syslog(L_ERROR, "%s cant restart %m", sp->Name);
-       else if (cp != NULL) {
-           if (sp->Buffered) {
-               /* SITEsetup had to buffer us; save any residue. */
-               out = &cp->Out;
-               if (out->left)
-                   buffer_set(&sp->Buffer, &out->data[out->used], out->left);
-           }
-           else
-               WCHANsetfrombuffer(sp->Channel, &cp->Out);
-       }
-    }
-    else if (cp != NULL && cp->Out.left) {
-       if (sp->Type == FTfile || sp->Spooling) {
-           /* Can't flush a file?  Hopeless. */
-           syslog(L_ERROR, "%s dataloss %lu", sp->Name,
-                   (unsigned long) cp->Out.left);
-           return;
-       }
-       /* Must be a working channel; spool and retry. */
-       syslog(L_ERROR, "%s spooling %lu bytes", sp->Name,
-               (unsigned long) cp->Out.left);
-       if (SITEspool(sp, cp))
-           SITEflush(sp, false);
-       return;
-    }
-
-    /* Close the old channel if it was open. */
-    if (cp != NULL) {
-        /* Make sure we have no dangling pointers to it. */
-       if (!Restart)
-           sp->Channel = NULL;
-       CHANclose(cp, sp->Name);
-       if (sp->Type == FTfile)
-           SITEunlink(sp);
-    }
-}
-
-
-/*
-**  Flush all sites.
-*/
-void
-SITEflushall(const bool Restart)
-{
-    int                        i;
-    SITE               *sp;
-
-    for (i = nSites, sp = Sites; --i >= 0; sp++)
-       if (sp->Name)
-           SITEflush(sp, Restart);
-}
-
-
-/*
-**  Run down the site's pattern list and see if it wants the specified
-**  newsgroup.
-*/
-bool
-SITEwantsgroup(SITE *sp, char *name)
-{
-    bool               match;
-    bool               subvalue;
-    char               *pat;
-    char               **argv;
-
-    match = SUB_DEFAULT;
-    if (ME.Patterns) {
-       for (argv = ME.Patterns; (pat = *argv++) != NULL; ) {
-           subvalue = *pat != SUB_NEGATE && *pat != SUB_POISON;
-           if (!subvalue)
-               pat++;
-           if ((match != subvalue) && uwildmat(name, pat))
-               match = subvalue;
-       }
-    }
-    for (argv = sp->Patterns; (pat = *argv++) != NULL; ) {
-       subvalue = *pat != SUB_NEGATE && *pat != SUB_POISON;
-       if (!subvalue)
-           pat++;
-       if ((match != subvalue) && uwildmat(name, pat))
-           match = subvalue;
-    }
-    return match;
-}
-
-
-/*
-**  Run down the site's pattern list and see if specified newsgroup is
-**  considered poison.
-*/
-bool
-SITEpoisongroup(SITE *sp, char *name)
-{
-    bool               match;
-    bool               poisonvalue;
-    char               *pat;
-    char               **argv;
-
-    match = SUB_DEFAULT;
-    if (ME.Patterns) {
-       for (argv = ME.Patterns; (pat = *argv++) != NULL; ) {
-           poisonvalue = *pat == SUB_POISON;
-           if (*pat == SUB_NEGATE || *pat == SUB_POISON)
-               pat++;
-           if (uwildmat(name, pat))
-               match = poisonvalue;
-       }
-    }
-    for (argv = sp->Patterns; (pat = *argv++) != NULL; ) {
-       poisonvalue = *pat == SUB_POISON;
-       if (*pat == SUB_NEGATE || *pat == SUB_POISON)
-           pat++;
-       if (uwildmat(name, pat))
-           match = poisonvalue;
-    }
-    return match;
-}
-
-
-/*
-**  Find a site.
-*/
-SITE *
-SITEfind(const char *p)
-{
-    int                        i;
-    SITE               *sp;
-
-    for (i = nSites, sp = Sites; --i >= 0; sp++)
-       if (sp->Name && strcasecmp(p, sp->Name) == 0)
-           return sp;
-    return NULL;
-}
-
-
-/*
-**  Find the next site that matches this site.
-*/
-SITE *
-SITEfindnext(const char *p, SITE *sp)
-{
-    SITE               *end;
-
-    for (sp++, end = &Sites[nSites]; sp < end; sp++)
-       if (sp->Name && strcasecmp(p, sp->Name) == 0)
-           return sp;
-    return NULL;
-}
-
-
-/*
-**  Close a site down.
-*/
-void
-SITEfree(SITE *sp)
-{
-    SITE                *s;
-    HASHFEEDLIST       *hf, *hn;
-    int                 new;
-    int                 i;
-    
-    if (sp->Channel) {
-       CHANclose(sp->Channel, CHANname(sp->Channel));
-       sp->Channel = NULL;
-    }
-
-    SITEunlink(sp);
-
-    sp->Name = NULL;
-    if (sp->Process > 0) {
-       /* Kill the backpointer so PROCdied won't call us. */
-       PROCunwatch(sp->Process);
-       sp->Process = -1;
-    }
-    if (sp->Entry) {
-       free(sp->Entry);
-       sp->Entry = NULL;
-    }
-    if (sp->Originator) {
-       free(sp->Originator);
-       sp->Originator = NULL;
-    }
-    if (sp->Param) {
-       free(sp->Param);
-       sp->Param = NULL;
-    }
-    if (sp->SpoolName) {
-       free(sp->SpoolName);
-       sp->SpoolName = NULL;
-    }
-    if (sp->Patterns) {
-       free(sp->Patterns);
-       sp->Patterns = NULL;
-    }
-    if (sp->Exclusions) {
-       free(sp->Exclusions);
-       sp->Exclusions = NULL;
-    }
-    if (sp->Distributions) {
-       free(sp->Distributions);
-       sp->Distributions = NULL;
-    }
-    if (sp->Buffer.data) {
-       free(sp->Buffer.data);
-       sp->Buffer.data = NULL;
-       sp->Buffer.size = 0;
-    }
-    if (sp->FNLnames.data) {
-       free(sp->FNLnames.data);
-       sp->FNLnames.data = NULL;
-       sp->FNLnames.size = 0;
-    }
-    if (sp->HashFeedList) {
-       for (hf = sp->HashFeedList; hf; hf = hn) {
-           hn = hf->next;
-           free(hf);
-       }
-       sp->HashFeedList = NULL;
-    }
-
-    /* If this site was a master, find a new one. */
-    if (sp->IsMaster) {
-       for (new = NOSITE, s = Sites, i = nSites; --i >= 0; s++)
-           if (&Sites[s->Master] == sp) {
-               if (new == NOSITE) {
-                   s->Master = NOSITE;
-                   s->IsMaster = true;
-                   new = s - Sites;
-               }
-               else
-                   s->Master = new;
-            }
-       sp->IsMaster = false;
-    }
-}
-
-
-/*
-**  If a site is an exploder or funnels into one, forward a command
-**  to it.
-*/
-void
-SITEforward(SITE *sp, const char *text)
-{
-    SITE               *fsp;
-    char               buff[SMBUF];
-
-    fsp = sp;
-    if (fsp->Funnel != NOSITE)
-       fsp = &Sites[fsp->Funnel];
-    if (sp->Name == NULL || fsp->Name == NULL)
-       return;
-    if (fsp->Type == FTexploder) {
-       strlcpy(buff, text, sizeof(buff));
-       if (fsp != sp && fsp->FNLwantsnames) {
-            strlcat(buff, " ", sizeof(buff));
-            strlcat(buff, sp->Name, sizeof(buff));
-       }
-       SITEwrite(fsp, buff);
-    }
-}
-
-
-/*
-**  Drop a site.
-*/
-void
-SITEdrop(SITE *sp)
-{
-    SITEforward(sp, "drop");
-    SITEflush(sp, false);
-    SITEfree(sp);
-}
-
-
-/*
-**  Append info about the current state of the site to the buffer
-*/
-void
-SITEinfo(struct buffer *bp, SITE *sp, const bool Verbose)
-{
-    static char                FREESITE[] = "<<No name>>\n\n";
-    char               *p;
-    CHANNEL            *cp;
-    const char         *sep;
-    char               buff[BUFSIZ];
-
-    if (sp->Name == NULL) {
-       buffer_append(bp, FREESITE, strlen(FREESITE));
-       return;
-    }
-
-    p = buff;
-    snprintf(buff, sizeof(buff), "%s%s:\t", sp->Name,
-             sp->IsMaster ? "(*)" : "");
-    p += strlen(p);
-
-    if (sp->Type == FTfunnel) {
-       sp = &Sites[sp->Funnel];
-       sprintf(p, "funnel -> %s: ", sp->Name);
-       p += strlen(p);
-    }
-
-    switch (sp->Type) {
-    default:
-       sprintf(p, "unknown feed type %d", sp->Type);
-       p += strlen(p);
-       break;
-    case FTerror:
-    case FTfile:
-       p += strlen(strcpy(p, "file"));
-       if (sp->Buffered) {
-           sprintf(p, " buffered(%lu)", (unsigned long) sp->Buffer.left);
-           p += strlen(p);
-       }
-       else if ((cp = sp->Channel) == NULL)
-           p += strlen(strcpy(p, " no channel?"));
-       else {
-           sprintf(p, " open fd=%d, in mem %lu", cp->fd,
-                    (unsigned long) cp->Out.left);
-           p += strlen(p);
-       }
-       break;
-    case FTchannel:
-       p += strlen(strcpy(p, "channel"));
-       goto Common;
-    case FTexploder:
-       p += strlen(strcpy(p, "exploder"));
-Common:
-       if (sp->Process >= 0) {
-           sprintf(p, " pid=%ld", (long) sp->pid);
-           p += strlen(p);
-       }
-       if (sp->Spooling)
-           p += strlen(strcpy(p, " spooling"));
-       if ((cp = sp->Channel) == NULL)
-           p += strlen(strcpy(p, " no channel?"));
-       else {
-           sprintf(p, " fd=%d, in mem %lu", cp->fd,
-                    (unsigned long) cp->Out.left);
-           p += strlen(p);
-       }
-       break;
-    case FTfunnel:
-       p += strlen(strcpy(p, "recursive funnel"));
-       break;
-    case FTlogonly:
-       p += strlen(strcpy(p, "log only"));
-       break;
-    case FTprogram:
-       p += strlen(strcpy(p, "program"));
-       if (sp->FNLwantsnames)
-           p += strlen(strcpy(p, " with names"));
-       break;
-    }
-    *p++ = '\n';
-    if (Verbose) {
-       sep = "\t";
-       if (sp->Buffered && sp->Flushpoint) {
-           sprintf(p, "%sFlush @ %ld", sep, sp->Flushpoint);
-           p += strlen(p);
-           sep = "; ";
-       }
-       if (sp->StartWriting || sp->StopWriting) {
-           sprintf(p, "%sWrite [%ld..%ld]", sep,
-               sp->StopWriting, sp->StartWriting);
-           p += strlen(p);
-           sep = "; ";
-       }
-       if (sp->StartSpooling) {
-           sprintf(p, "%sSpool @ %ld", sep, sp->StartSpooling);
-           p += strlen(p);
-           sep = "; ";
-       }
-       if (sep[0] != '\t')
-           *p++ = '\n';
-       if (sp->Spooling && sp->SpoolName) {
-           sprintf(p, "\tSpooling to \"%s\"\n", sp->SpoolName);
-           p += strlen(p);
-       }
-       if ((cp = sp->Channel) != NULL) {
-           sprintf(p, "\tChannel created %.12s",
-               ctime(&cp->Started) + 4);
-           p += strlen(p);
-           sprintf(p, ", last active %.12s\n",
-               ctime(&cp->LastActive) + 4);
-           p += strlen(p);
-           if (cp->Waketime > Now.time) {
-               sprintf(p, "\tSleeping until %.12s\n",
-                   ctime(&cp->Waketime) + 4);
-               p += strlen(p);
-           }
-       }
-
-    }
-    buffer_append(bp, buff, p - buff);
-}