chiark / gitweb /
REORG Delete everything that's not innduct or build system or changed for innduct
[innduct.git] / innd / cc.c
diff --git a/innd/cc.c b/innd/cc.c
deleted file mode 100644 (file)
index be17183..0000000
--- a/innd/cc.c
+++ /dev/null
@@ -1,2107 +0,0 @@
-/*  $Id: cc.c 7748 2008-04-06 13:49:56Z iulius $
-**
-**  Routines for the control channel.
-**
-**  Create a Unix-domain datagram socket that processes on the local server
-**  send messages to.  The control channel is used only by ctlinnd to tell
-**  the server to perform special functions.  We use datagrams so that we
-**  don't need to do an accept() and tie up another descriptor.  recvfrom
-**  seems to be broken on several systems, so the client passes in the
-**  socket name.
-**
-**  This module completely rips away all pretense of software layering.
-*/
-
-#include "config.h"
-#include "clibrary.h"
-
-#ifdef HAVE_UNIX_DOMAIN_SOCKETS
-# include <sys/un.h>
-#endif
-
-#include "inn/innconf.h"
-#include "inn/qio.h"
-#include "innd.h"
-#include "inndcomm.h"
-#include "innperl.h"
-
-/*
-**  An entry in the dispatch table.  The name, and implementing function,
-**  of every command we support.
-*/
-typedef struct _CCDISPATCH {
-    char Name;
-    int        argc;
-    const char * (*Function)(char *av[]);
-} CCDISPATCH;
-
-
-static const char *    CCallow(char *av[]);
-static const char *    CCbegin(char *av[]);
-static const char *    CCchgroup(char *av[]);
-static const char *    CCdrop(char *av[]);
-static const char *    CCfeedinfo(char *av[]);
-static const char *    CCflush(char *av[]);
-static const char *    CCflushlogs(char *unused[]);
-static const char *    CCgo(char *av[]);
-static const char *    CChangup(char *av[]);
-static const char *    CCreserve(char *av[]);
-static const char *    CClogmode(char *unused[]);
-static const char *    CCmode(char *unused[]);
-static const char *    CCname(char *av[]);
-static const char *    CCnewgroup(char *av[]);
-static const char *    CCparam(char *av[]);
-static const char *    CCpause(char *av[]);
-static const char *    CCreaders(char *av[]);
-static const char *    CCreject(char *av[]);
-static const char *    CCreload(char *av[]);
-static const char *    CCrenumber(char *av[]);
-static const char *    CCrmgroup(char *av[]);
-static const char *    CCsend(char *av[]);
-static const char *    CCshutdown(char *av[]);
-static const char *    CCsignal(char *av[]);
-static const char *    CCstathist(char *av[]);
-static const char *    CCstatus(char *av[]);
-static const char *    CCthrottle(char *av[]);
-static const char *    CCtimer(char *av[]);
-static const char *    CCtrace(char *av[]);
-static const char *    CCxabort(char *av[]);
-static const char *    CCxexec(char *av[]);
-static const char *    CCfilter(char *av[]);
-static const char *    CCperl(char *av[]);
-static const char *    CCpython(char *av[]);
-static const char *    CClowmark(char *av[]);
-
-
-static char            *CCpath = NULL;
-static char            **CCargv;
-static char            CCnosite[] = "1 No such site";
-static char            CCwrongtype[] = "1 Wrong site type";
-static char            CCnogroup[] = "1 No such group";
-static char            CCnochannel[] = "1 No such channel";
-static char            CCnoreason[] = "1 Empty reason";
-static char            CCbigreason[] = "1 Reason too long";
-static char            CCnotrunning[] = "1 Must be running";
-static struct buffer   CCreply;
-static CHANNEL         *CCchan;
-static int             CCwriter;
-static CCDISPATCH      CCcommands[] = {
-    {  SC_ADDHIST,     5, CCaddhist    },
-    {  SC_ALLOW,       1, CCallow      },
-    {  SC_BEGIN,       1, CCbegin      },
-    {  SC_CANCEL,      1, CCcancel     },
-    {  SC_CHANGEGROUP, 2, CCchgroup    },
-    {  SC_CHECKFILE,   0, CCcheckfile  },
-    {  SC_DROP,        1, CCdrop       },
-    {  SC_FEEDINFO,    1, CCfeedinfo   },
-    {  SC_FILTER,      1, CCfilter     },
-    {  SC_PERL,        1, CCperl       },
-    {  SC_PYTHON,      1, CCpython     },
-    {  SC_FLUSH,       1, CCflush      },
-    {  SC_FLUSHLOGS,   0, CCflushlogs  },
-    {  SC_GO,          1, CCgo         },
-    {  SC_HANGUP,      1, CChangup     },
-    {  SC_LOGMODE,     0, CClogmode    },
-    {  SC_MODE,        0, CCmode       },
-    {  SC_NAME,        1, CCname       },
-    {  SC_NEWGROUP,    3, CCnewgroup   },
-    {  SC_PARAM,       2, CCparam      },
-    {  SC_PAUSE,       1, CCpause      },
-    {  SC_READERS,     2, CCreaders    },
-    {  SC_REJECT,      1, CCreject     },
-    {  SC_RENUMBER,    1, CCrenumber   },
-    {  SC_RELOAD,      2, CCreload     },
-    {  SC_RESERVE,     1, CCreserve    },
-    {  SC_RMGROUP,     1, CCrmgroup    },
-    {  SC_SEND,        2, CCsend       },
-    {  SC_SHUTDOWN,    1, CCshutdown   },
-    {  SC_SIGNAL,      2, CCsignal     },
-    {  SC_STATHIST,    1, CCstathist   },
-    {  SC_STATUS,      1, CCstatus     },
-    {  SC_THROTTLE,    1, CCthrottle   },
-    {   SC_TIMER,       1, CCtimer      },
-    {  SC_TRACE,       2, CCtrace      },
-    {  SC_XABORT,      1, CCxabort     },
-    {  SC_LOWMARK,     1, CClowmark    },
-    {  SC_XEXEC,       1, CCxexec      }
-};
-
-static RETSIGTYPE CCresetup(int unused);
-\f
-
-void
-CCcopyargv(char *av[])
-{
-    char       **v;
-    int                i;
-
-    /* Get the vector size. */
-    for (i = 0; av[i]; i++)
-       continue;
-
-    /* Get the vector, copy each element. */
-    for (v = CCargv = xmalloc((i + 1) * sizeof(char *)); *av; av++) {
-       /* not to renumber */
-       if (strncmp(*av, "-r", 2) == 0)
-           continue;
-       *v++ = xstrdup(*av);
-    }
-    *v = NULL;
-}
-
-
-/*
-**  Return a string representing our operating mode.
-*/
-static const char *
-CCcurrmode(void)
-{
-    static char                buff[32];
-
-    /* Server's mode. */
-    switch (Mode) {
-    default:
-       snprintf(buff, sizeof(buff), "Unknown %d", Mode);
-       return buff;
-    case OMrunning:
-       return "running";
-    case OMpaused:
-       return "paused";
-    case OMthrottled:
-       return "throttled";
-    }
-}
-
-
-/*
-**  Add <> around Message-ID if needed.
-*/
-static const char *
-CCgetid(char *p, const char **store)
-{
-    static char        NULLMESGID[] = "1 Empty Message-ID";
-    static struct buffer Save = { 0, 0, 0, NULL };
-    int i;
-
-    if (*p == '\0')
-       return NULLMESGID;
-    if (*p == '<') {
-       if (p[1] == '\0' || p[1] == '>')
-           return NULLMESGID;
-       *store = p;
-       return NULL;
-    }
-
-    /* Make sure the Message-ID buffer has room. */
-    i = 1 + strlen(p) + 1 + 1;
-    buffer_resize(&Save, i);
-    *store = Save.data;
-    snprintf(Save.data, Save.size, "<%s>", p);
-    return NULL;
-}
-
-
-/*
-**  Abort and dump core.
-*/
-static const char *
-CCxabort(char *av[])
-{
-    syslog(L_FATAL, "%s abort %s", LogName, av[0]);
-    abort();
-    syslog(L_FATAL, "%s cant abort %m", LogName);
-    CleanupAndExit(1, av[0]);
-    /* NOTREACHED */
-}
-
-
-/*
-**  Do the work needed to add a history entry.
-*/
-const char *
-CCaddhist(char *av[])
-{
-    static char                DIGITS[] = "0123456789";
-    ARTDATA            Data;
-    const char *       p, *msgid;
-    bool               ok;
-    TOKEN              token;
-
-    /* You must pass a <message-id> ID, the history API will hash it as it
-     * wants */
-    if ((p = CCgetid(av[0], &msgid)) != NULL)
-       return p;
-
-    /* If paused, don't try to use the history database since expire may be
-       running */
-    if (Mode == OMpaused)
-       return "1 Server paused";
-
-    /* If throttled by admin, briefly open the history database. */
-    if (Mode != OMrunning) {
-       if (ThrottledbyIOError)
-           return "1 Server throttled";
-       InndHisOpen();
-    }
-
-    if (HIScheck(History, msgid)) {
-       if (Mode != OMrunning) InndHisClose();
-       return "1 Duplicate";
-    }
-    if (Mode != OMrunning) InndHisClose();
-    if (strspn(av[1], DIGITS) != strlen(av[1]))
-       return "1 Bad arrival date";
-    Data.Arrived = atol(av[1]);
-    if (strspn(av[2], DIGITS) != strlen(av[2]))
-       return "1 Bad expiration date";
-    Data.Expires = atol(av[2]);
-    if (strspn(av[3], DIGITS) != strlen(av[3]))
-       return "1 Bad posted date";
-    Data.Posted = atol(av[3]);
-
-    token = TextToToken(av[4]);
-    if (Mode == OMrunning)
-       ok = InndHisWrite(msgid, Data.Arrived, Data.Posted,
-                         Data.Expires, &token);
-    else {
-       /* Possible race condition, but documented in ctlinnd manpage. */
-       InndHisOpen();
-       ok = InndHisWrite(msgid, Data.Arrived, Data.Posted,
-                         Data.Expires, &token);
-       InndHisClose();
-    }
-    return ok ? NULL : "1 Write failed";
-}
-
-
-/*
-**  Do the work to allow foreign connectiosn.
-*/
-static const char *
-CCallow(char *av[])
-{
-    char       *p;
-
-    if (RejectReason == NULL)
-       return "1 Already allowed";
-    p = av[0];
-    if (*p && strcmp(p, RejectReason) != 0)
-       return "1 Wrong reason";
-    free(RejectReason);
-    RejectReason = NULL;
-    return NULL;
-}
-
-
-/*
-**  Do the work needed to start feeding a (new) site.
-*/
-static const char *
-CCbegin(char *av[])
-{
-    SITE       *sp;
-    int                i;
-    int                length;
-    char       *p;
-    const char *p1;
-    char       **strings;
-    NEWSGROUP  *ngp;
-    const char *error;
-    char       *subbed;
-    char       *poison;
-
-    /* If site already exists, drop it. */
-    if (SITEfind(av[0]) != NULL && (p1 = CCdrop(av)) != NULL)
-       return p1;
-
-    /* Find the named site. */
-    length = strlen(av[0]);
-    for (strings = SITEreadfile(true), i = 0; (p = strings[i]) != NULL; i++)
-       if ((p[length] == NF_FIELD_SEP || p[length] == NF_SUBFIELD_SEP)
-        && strncasecmp(p, av[0], length) == 0) {
-           p = xstrdup(p);
-           break;
-       }
-    if (p == NULL)
-       return CCnosite;
-
-    if (p[0] == 'M' && p[1] == 'E' && p[2] == NF_FIELD_SEP)
-       sp = &ME;
-    else {
-       /* Get space for the new site entry, and space for it in all
-        * the groups. */
-       for (i = nSites, sp = Sites; --i >= 0; sp++)
-           if (sp->Name == NULL)
-               break;
-       if (i < 0) {
-           nSites++;
-            Sites = xrealloc(Sites, nSites * sizeof(SITE));
-           sp = &Sites[nSites - 1];
-           sp->Next = sp->Prev = NOSITE;
-           for (i = nGroups, ngp = Groups; --i >= 0; ngp++) {
-                ngp->Sites = xrealloc(ngp->Sites, nSites * sizeof(int));
-                ngp->Poison = xrealloc(ngp->Poison, nSites * sizeof(int));
-           }
-       }
-    }
-
-    /* Parse. */
-    subbed = xmalloc(nGroups);
-    poison = xmalloc(nGroups);
-    error = SITEparseone(p, sp, subbed, poison);
-    free(subbed);
-    free(poison);
-    if (error != NULL) {
-       free((void *)p);
-       syslog(L_ERROR, "%s bad_newsfeeds %s", av[0], error);
-       return "1 Parse error";
-    }
-
-    if (sp != &ME && (!SITEsetup(sp) || !SITEfunnelpatch()))
-       return "1 Startup error";
-    SITEforward(sp, "begin");
-    return NULL;
-}
-
-
-/*
-**  Common code to change a group's flags.
-*/
-static const char *
-CCdochange(NEWSGROUP *ngp, char *Rest)
-{
-    int                        length;
-    char               *p;
-
-    if (ngp->Rest[0] == Rest[0]) {
-       length = strlen(Rest);
-       if (ngp->Rest[length] == '\n' && strncmp(ngp->Rest, Rest, length) == 0)
-           return "0 Group status unchanged";
-    }
-    if (Mode != OMrunning)
-       return CCnotrunning;
-
-    p = xstrdup(ngp->Name);
-    if (!ICDchangegroup(ngp, Rest)) {
-       syslog(L_NOTICE, "%s cant change_group %s to %s", LogName, p, Rest);
-       free(p);
-       return "1 Change failed (probably can't write active?)";
-    }
-    syslog(L_NOTICE, "%s change_group %s to %s", LogName, p, Rest);
-    free(p);
-    return NULL;
-}
-
-
-/*
-**  Change the mode of a newsgroup.
-*/
-static const char *
-CCchgroup(char *av[])
-{
-    NEWSGROUP  *ngp;
-    char       *Rest;
-
-    if ((ngp = NGfind(av[0])) == NULL)
-       return CCnogroup;
-    Rest = av[1];
-    if (Rest[0] != NF_FLAG_ALIAS) {
-       Rest[1] = '\0';
-       if (CTYPE(isupper, Rest[0]))
-           Rest[0] = tolower(Rest[0]);
-    }
-    return CCdochange(ngp, Rest);
-}
-
-
-/*
-**  Cancel a message.
-*/
-const char *
-CCcancel(char *av[])
-{
-    ARTDATA    Data;
-    const char *       p, *msgid;
-
-    Data.Posted = Data.Arrived = Now.time;
-    Data.Expires = 0;
-    Data.Feedsite = "?";
-    if ((p = CCgetid(av[0], &msgid)) != NULL)
-       return p;
-
-    Data.HdrContent[HDR__MESSAGE_ID].Value = (char *)msgid;
-    Data.HdrContent[HDR__MESSAGE_ID].Length = strlen(msgid);
-    if (Mode == OMrunning)
-       ARTcancel(&Data, msgid, true);
-    else {
-       /* If paused, don't try to use the history database since expire may be
-          running */
-       if (Mode == OMpaused)
-           return "1 Server paused";
-       if (ThrottledbyIOError)
-           return "1 Server throttled";
-       /* Possible race condition, but documented in ctlinnd manpage. */
-       InndHisOpen();
-       ARTcancel(&Data, msgid, true);
-       InndHisClose();
-    }
-    if (innconf->logcancelcomm)
-       syslog(L_NOTICE, "%s cancelled %s", LogName, msgid);
-    return NULL;
-}
-
-
-/*
-**  Syntax-check the newsfeeds file.
-*/
-const char *
-CCcheckfile(char *unused[])
-{
-  char         **strings;
-  char         *p;
-  int          i;
-  int          errors;
-  const char * error;
-  SITE         fake;
-  bool         needheaders, needoverview, needpath, needstoredgroup;
-  bool         needreplicdata;
-
-  unused = unused;             /* ARGSUSED */
-  /* Parse all site entries. */
-  strings = SITEreadfile(false);
-  fake.Buffer.size = 0;
-  fake.Buffer.data = NULL;
-  /* save global variables not to be changed */
-  needheaders = NeedHeaders;
-  needoverview = NeedOverview;
-  needpath = NeedPath;
-  needstoredgroup = NeedStoredGroup;
-  needreplicdata = NeedReplicdata;
-  for (errors = 0, i = 0; (p = strings[i]) != NULL; i++) {
-    if ((error = SITEparseone(p, &fake, (char *)NULL, (char *)NULL)) != NULL) {
-      syslog(L_ERROR, "%s bad_newsfeeds %s", MaxLength(p, p), error);
-      errors++;
-    }
-    SITEfree(&fake);
-  }
-  free(strings);
-  /* restore global variables not to be changed */
-  NeedHeaders = needheaders;
-  NeedOverview = needoverview;
-  NeedPath = needpath;
-  NeedStoredGroup = needstoredgroup;
-  NeedReplicdata = needreplicdata;
-
-  if (errors == 0)
-    return NULL;
-
-  buffer_resize(&CCreply, SMBUF);
-  snprintf(CCreply.data, CCreply.size, "1 Found %d errors -- see syslog",
-           errors);
-  return CCreply.data;
-}
-
-
-/*
-**  Drop a site.
-*/
-static const char *
-CCdrop(char *av[])
-{
-    SITE       *sp;
-    NEWSGROUP  *ngp;
-    int                *ip;
-    int                idx;
-    int                i;
-    int                j;
-
-    if ((sp = SITEfind(av[0])) == NULL)
-       return CCnosite;
-
-    SITEdrop(sp);
-
-    /* Loop over all groups, and if the site is in a group, clobber it. */
-    for (idx = sp - Sites, i = nGroups, ngp = Groups; --i >= 0; ngp++) {
-       for (j = ngp->nSites, ip = ngp->Sites; --j >= 0; ip++)
-           if (*ip == idx)
-               *ip = NOSITE;
-       for (j = ngp->nPoison, ip = ngp->Poison; --j >= 0; ip++)
-           if (*ip == idx)
-               *ip = NOSITE;
-    }
-
-       return NULL;
-}
-
-/*
-**  Return info on the feeds for one, or all, sites
-*/
-static const char *
-CCfeedinfo(char *av[])
-{
-    SITE       *sp;
-    char       *p;
-    int                i;
-
-    buffer_set(&CCreply, "0 ", 2);
-    p = av[0];
-    if (*p != '\0') {
-       if ((sp = SITEfind(p)) == NULL)
-           return "1 No such site";
-
-       SITEinfo(&CCreply, sp, true);
-       while ((sp = SITEfindnext(p, sp)) != NULL)
-           SITEinfo(&CCreply, sp, true);
-    }
-    else
-       for (i = nSites, sp = Sites; --i >= 0; sp++)
-           if (sp->Name)
-               SITEinfo(&CCreply, sp, false);
-
-    buffer_append(&CCreply, "", 1);
-    return CCreply.data;
-}
-
-
-static const char *
-CCfilter(char *av[] UNUSED)
-{
-#if defined(DO_TCL)
-    char       *p;
-
-    switch (av[0][0]) {
-    default:
-       return "1 Bad flag";
-    case 'y':
-       if (TCLFilterActive)
-           return "1 tcl filter already enabled";
-       TCLfilter(true);
-       break;
-    case 'n':
-       if (!TCLFilterActive)
-           return "1 tcl filter already disabled";
-       TCLfilter(false);
-       break;
-    }
-    return NULL;
-#else /* defined(DO_TCL) */
-    return "1 TCL filtering support not compiled in";
-#endif /* defined(DO_TCL) */
-}
-
-
-static const char *
-CCperl(char *av[])
-{
-#if defined(DO_PERL)
-    switch (av[0][0]) {
-    default:
-       return "1 Bad flag";
-    case 'y':
-       if (PerlFilterActive)
-           return "1 Perl filter already enabled";
-        else if (!PerlFilter(true))
-            return "1 Perl filter not defined";
-       break;
-    case 'n':
-       if (!PerlFilterActive)
-           return "1 Perl filter already disabled";
-        PerlFilter(false);
-       break;
-    }
-    return NULL;
-#else /* defined(DO_PERL) */
-    return "1 Perl filtering support not compiled in";
-#endif /* defined(DO_PERL) */
-}
-
-
-static const char *
-CCpython(char *av[] UNUSED)
-{
-#if defined(DO_PYTHON)
-    return PYcontrol(av);
-#else /* defined(DO_PYTHON) */
-    return "1 Python filtering support not compiled in";
-#endif /* defined(DO_PYTHON) */
-}
-
-
-/*
-**  Flush all sites or one site.
-*/
-static const char *
-CCflush(char *av[])
-{
-    SITE       *sp;
-    int                i;
-    char       *p;
-
-    p = av[0];
-    if (*p == '\0') {
-       ICDwrite();
-       for (sp = Sites, i = nSites; --i >= 0; sp++)
-           SITEflush(sp, true);
-       syslog(L_NOTICE, "%s flush_all", LogName);
-    }
-    else  {
-       if ((sp = SITEfind(p)) == NULL)
-           return CCnosite;
-       syslog(L_NOTICE, "%s flush", sp->Name);
-       SITEflush(sp, true);
-    }
-    return NULL;
-}
-
-
-/*
-**  Flush the log files.
-*/
-static const char *
-CCflushlogs(char *unused[])
-{
-    unused = unused;           /* ARGSUSED */
-
-    if (Debug)
-       return "1 In debug mode";
-
-    ICDwrite();
-    syslog(L_NOTICE, "%s flushlogs %s", LogName, CCcurrmode());
-    ReopenLog(Log);
-    ReopenLog(Errlog);
-    return NULL;
-}
-
-
-/*
-**  Leave paused or throttled mode.
-*/
-static const char *
-CCgo(char *av[])
-{
-    static char        YES[] = "y";
-    char       *p;
-
-    p = av[0];
-    if (Reservation && strcmp(p, Reservation) == 0) {
-       free(Reservation);
-       Reservation = NULL;
-    }
-    if (RejectReason && strcmp(p, RejectReason) == 0) {
-       free(RejectReason);
-       RejectReason = NULL;
-    }
-
-    if (Mode == OMrunning)
-       return "1 Already running";
-    if (*p && strcmp(p, ModeReason) != 0)
-       return "1 Wrong reason";
-
-#if defined(DO_PERL)
-    PLmode(Mode, OMrunning, p);
-#endif /* defined(DO_PERL) */
-#if defined(DO_PYTHON)
-    PYmode(Mode, OMrunning, p);
-#endif /* defined(DO_PYTHON) */
-    
-    free(ModeReason);
-    ModeReason = NULL;
-    Mode = OMrunning;
-    ThrottledbyIOError = false;
-
-    if (NNRPReason && !innconf->readerswhenstopped) {
-       av[0] = YES;
-       av[1] = p;
-       CCreaders(av);
-    }
-    if (ErrorCount < 0)
-       ErrorCount = IO_ERROR_COUNT;
-    InndHisOpen();
-    syslog(L_NOTICE, "%s running", LogName);
-    if (ICDneedsetup)
-       ICDsetup(true);
-    SCHANwakeup(&Mode);
-    return NULL;
-}
-
-
-/*
-**  Hangup a channel.
-*/
-static const char *
-CChangup(char *av[])
-{
-    CHANNEL    *cp;
-    int                fd;
-    char       *p;
-    int                i;
-
-    /* Parse the argument, a channel number. */
-    for (p = av[0], fd = 0; *p; p++) {
-       if (!CTYPE(isdigit, *p))
-           return "1 Bad channel number";
-       fd = fd * 10 + *p - '0';
-    }
-
-    /* Loop over all channels for the desired one. */
-    for (i = 0; (cp = CHANiter(&i, CTany)) != NULL; )
-       if (cp->fd == fd) {
-           p = CHANname(cp);
-           switch (cp->Type) {
-           default:
-               snprintf(CCreply.data, CCreply.size, "1 Can't close %s", p);
-               return CCreply.data;
-           case CTexploder:
-           case CTprocess:
-           case CTfile:
-           case CTnntp:
-           case CTreject:
-               syslog(L_NOTICE, "%s hangup", p);
-               CHANclose(cp, p);
-               return NULL;
-           }
-       }
-    return "1 Not active";
-}
-
-
-/*
-**  Return our operating mode.
-*/
-static const char *
-CCmode(char *unused[] UNUSED)
-{
-    char       *p;
-    int                i;
-    int                h;
-    char       buff[BUFSIZ];
-#if defined(DO_PERL)
-    char        *stats;
-#endif /* defined(DO_PERL) */
-
-    /* FIXME: We assume here that BUFSIZ is >= 512, and that none of
-     * ModeReason, RejectReason, Reservation, NNRPReason, or the Perl filter
-     * statistics are longer than MAX_REASON_LEN bytes (or actually, the
-     * average of their lengths is <= MAX_REASON_LEN).  If this is not true,
-     * the sprintf's/strcpy's below are likely to overflow buff with somewhat
-     * nasty consequences...
-     */
-
-    p = buff;
-    p += strlen(strcpy(buff, "0 Server "));
-
-    /* Server's mode. */
-    switch (Mode) {
-    default:
-       sprintf(p, "Unknown %d", Mode);
-       p += strlen(p);
-       break;
-    case OMrunning:
-       p += strlen(strcpy(p, "running"));
-       break;
-    case OMpaused:
-       p += strlen(strcpy(p, "paused "));
-       p += strlen(strcpy(p, ModeReason));
-       break;
-    case OMthrottled:
-       p += strlen(strcpy(p, "throttled "));
-       p += strlen(strcpy(p, ModeReason));
-       break;
-    }
-    *p++ = '\n';
-    if (RejectReason) {
-       p += strlen(strcpy(p, "Rejecting "));
-       p += strlen(strcpy(p, RejectReason));
-    }
-    else
-       p += strlen(strcpy(p, "Allowing remote connections"));
-
-    /* Server parameters. */
-    for (i = 0, h = 0; CHANiter(&h, CTnntp) != NULL; )
-       i++;
-    *p++ = '\n';
-    sprintf(p, "Parameters c %ld i %ld (%d) l %ld o %d t %ld H %d T %d X %d %s %s",
-                  innconf->artcutoff, innconf->maxconnections, i,
-                  innconf->maxartsize, MaxOutgoing, (long)TimeOut.tv_sec,
-                  RemoteLimit, RemoteTotal, (int) RemoteTimer,
-                  innconf->xrefslave ? "slave" : "normal",
-                  AnyIncoming ? "any" : "specified");
-    p += strlen(p);
-
-    /* Reservation. */
-    *p++ = '\n';
-    if (Reservation) {
-       sprintf(p, "Reserved %s", Reservation);
-       p += strlen(p);
-    }
-    else
-       p += strlen(strcpy(p, "Not reserved"));
-
-    /* Newsreaders. */
-    *p++ = '\n';
-    p += strlen(strcpy(p, "Readers "));
-    if (innconf->readerswhenstopped)
-       p += strlen(strcpy(p, "independent "));
-    else
-       p += strlen(strcpy(p, "follow "));
-    if (NNRPReason == NULL)
-       p += strlen(strcpy(p, "enabled"));
-    else {
-       sprintf(p, "disabled %s", NNRPReason);
-       p += strlen(p);
-    }
-
-#if defined(DO_TCL)
-    *p++ = '\n';
-    p += strlen(strcpy(p, "Tcl filtering "));
-    if (TCLFilterActive)
-       p += strlen(strcpy(p, "enabled"));
-    else
-       p += strlen(strcpy(p, "disabled"));
-#endif /* defined(DO_TCL) */
-
-#if defined(DO_PERL)
-    *p++ = '\n';
-    p += strlen(strcpy(p, "Perl filtering "));
-    if (PerlFilterActive)
-        p += strlen(strcpy(p, "enabled"));
-    else
-        p += strlen(strcpy(p, "disabled"));
-
-    /* Perl filter status. */
-    stats = PLstats();
-    if (stats != NULL) {
-        *p++ = '\n';
-        p += strlen(strcpy(p, "Perl filter stats: "));
-        p += strlen(strcpy(p, stats));
-        free(stats);
-    }    
-#endif /* defined(DO_PERL) */
-
-#if defined(DO_PYTHON)
-    *p++ = '\n';
-    p += strlen(strcpy(p, "Python filtering "));
-    if (PythonFilterActive)
-        p += strlen(strcpy(p, "enabled"));
-    else
-        p += strlen(strcpy(p, "disabled"));
-#endif /* defined(DO_PYTHON) */
-
-    buffer_set(&CCreply, buff, strlen(buff) + 1);
-    return CCreply.data;
-}
-
-
-/*
-**  Log our operating mode (via syslog).
-*/
-static const char *
-CClogmode(char *unused[])
-{
-    unused = unused;           /* ARGSUSED */
-    syslog(L_NOTICE, "%s servermode %s", LogName, CCcurrmode());
-    return NULL;
-}
-
-
-/*
-**  Name the channels.  ("Name the bats -- simple names.")
-*/
-static const char *
-CCname(char *av[])
-{
-    static char                NL[] = "\n";
-    static char                NIL[] = "\0";
-    char               buff[SMBUF];
-    CHANNEL            *cp;
-    char               *p;
-    int                        count;
-    int                        i;
-
-    p = av[0];
-    if (*p != '\0') {
-       if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
-           return xstrdup(CCnochannel);
-       snprintf(CCreply.data, CCreply.size, "0 %s", CHANname(cp));
-       return CCreply.data;
-    }
-    buffer_set(&CCreply, "0 ", 2);
-    for (count = 0, i = 0; (cp = CHANiter(&i, CTany)) != NULL; ) {
-       if (cp->Type == CTfree)
-           continue;
-       if (++count > 1)
-           buffer_append(&CCreply, NL, 1);
-       p = CHANname(cp);
-       buffer_append(&CCreply, p, strlen(p));
-       switch (cp->Type) {
-       case CTremconn:
-           sprintf(buff, ":remconn::");
-           break;
-       case CTreject:
-           sprintf(buff, ":reject::");
-           break;
-       case CTnntp:
-           sprintf(buff, ":%s:%ld:%s",
-                    cp->State == CScancel ? "cancel" : "nntp",
-                    (long) Now.time - cp->LastActive,
-                    (cp->MaxCnx > 0 && cp->ActiveCnx == 0) ? "paused" : "");
-           break;
-       case CTlocalconn:
-           sprintf(buff, ":localconn::");
-           break;
-       case CTcontrol:
-           sprintf(buff, ":control::");
-           break;
-       case CTfile:
-           sprintf(buff, "::");
-           break;
-       case CTexploder:
-           sprintf(buff, ":exploder::");
-           break;
-       case CTprocess:
-           sprintf(buff, ":");
-           break;
-       default:
-           sprintf(buff, ":unknown::");
-           break;
-       }
-       p = buff;
-       buffer_append(&CCreply, p, strlen(p));
-    }
-    buffer_append(&CCreply, NIL, 1);
-    return CCreply.data;
-}
-
-
-/*
-**  Create a newsgroup.
-*/
-static const char *
-CCnewgroup(char *av[])
-{
-    static char                *TIMES = NULL;
-    static char                WHEN[] = "updating active.times";
-    int                        fd;
-    char               *p;
-    NEWSGROUP          *ngp;
-    char               *Name;
-    char               *Rest;
-    const char *               who;
-    char               *buff;
-    int                        oerrno;
-    size_t              length;
-
-    if (TIMES == NULL)
-       TIMES = concatpath(innconf->pathdb, _PATH_ACTIVETIMES);
-
-    Name = av[0];
-    if (Name[0] == '.' || strspn(Name, "0123456789") == strlen(Name))
-       return "1 Illegal newsgroup name";
-    for (p = Name; *p; p++)
-       if (*p == '.') {
-           if (p[1] == '.' || p[1] == '\0')
-               return "1 Double or trailing period in newsgroup name";
-       }
-       else if (ISWHITE(*p) || *p == ':' || *p == '!' || *p == '/')
-           return "1 Illegal character in newsgroup name";
-
-    Rest = av[1];
-    if (Rest[0] != NF_FLAG_ALIAS) {
-       Rest[1] = '\0';
-       if (CTYPE(isupper, Rest[0]))
-           Rest[0] = tolower(Rest[0]);
-    }
-    if (strlen(Name) + strlen(Rest) > SMBUF - 24)
-       return "1 Name too long";
-
-    if ((ngp = NGfind(Name)) != NULL)
-       return CCdochange(ngp, Rest);
-
-    if (Mode == OMthrottled && ThrottledbyIOError)
-       return "1 server throttled";
-
-    /* Update the log of groups created.  Don't use stdio because SunOS
-     * 4.1 has broken libc which can't handle fd's greater than 127. */
-    if ((fd = open(TIMES, O_WRONLY | O_APPEND | O_CREAT, 0664)) < 0) {
-       oerrno = errno;
-       syslog(L_ERROR, "%s cant open %s %m", LogName, TIMES);
-       IOError(WHEN, oerrno);
-    }
-    else {
-       who = av[2];
-       if (*who == '\0')
-           who = NEWSMASTER;
-
-        length = snprintf(NULL, 0, "%s %ld %s\n", Name, (long) Now.time, who) + 1;
-        buff = xmalloc(length);
-        snprintf(buff, length, "%s %ld %s\n", Name, (long) Now.time, who);
-       if (xwrite(fd, buff, strlen(buff)) < 0) {
-           oerrno = errno;
-           syslog(L_ERROR, "%s cant write %s %m", LogName, TIMES);
-           IOError(WHEN, oerrno);
-       }
-
-       free(buff);
-       
-       if (close(fd) < 0) {
-           oerrno = errno;
-           syslog(L_ERROR, "%s cant close %s %m", LogName, TIMES);
-           IOError(WHEN, oerrno);
-       }
-    }
-
-    /* Update the in-core data. */
-    if (!ICDnewgroup(Name, Rest))
-       return "1 Failed";
-    syslog(L_NOTICE, "%s newgroup %s as %s", LogName, Name, Rest);
-
-    return NULL;
-}
-
-
-/*
-**  Parse and set a boolean flag.
-*/
-static bool
-CCparsebool(char name, bool *bp, char value)
-{
-    switch (value) {
-    default:
-       return false;
-    case 'y':
-       *bp = false;
-       break;
-    case 'n':
-       *bp = true;
-       break;
-    }
-    syslog(L_NOTICE, "%s changed -%c %c", LogName, name, value);
-    return true;
-}
-
-
-/*
-**  Change a running parameter.
-*/
-static const char *
-CCparam(char *av[])
-{
-    static char        BADVAL[] = "1 Bad value";
-    char       *p;
-    int                temp;
-
-    p = av[1];
-    switch (av[0][0]) {
-    default:
-       return "1 Unknown parameter";
-    case 'a':
-       if (!CCparsebool('a', (bool *)&AnyIncoming, *p))
-           return BADVAL;
-       break;
-    case 'c':
-       innconf->artcutoff = atoi(p);
-       syslog(L_NOTICE, "%s changed -c %ld", LogName, innconf->artcutoff);
-       break;
-    case 'i':
-       innconf->maxconnections = atoi(p);
-       syslog(L_NOTICE, "%s changed -i %ld", LogName, innconf->maxconnections);
-       break;
-    case 'l':
-       innconf->maxartsize = atol(p);
-       syslog(L_NOTICE, "%s changed -l %ld", LogName, innconf->maxartsize);
-       break;
-    case 'n':
-       if (!CCparsebool('n', (bool *)&innconf->readerswhenstopped, *p))
-           return BADVAL;
-       break;
-    case 'o':
-       MaxOutgoing = atoi(p);
-       syslog(L_NOTICE, "%s changed -o %d", LogName, MaxOutgoing);
-       break;
-    case 't':
-       TimeOut.tv_sec = atol(p);
-       syslog(L_NOTICE, "%s changed -t %ld", LogName, (long)TimeOut.tv_sec);
-       break;
-    case 'H':
-       RemoteLimit = atoi(p);
-       syslog(L_NOTICE, "%s changed -H %d", LogName, RemoteLimit);
-       break;
-    case 'T':
-        temp = atoi(p);
-       if (temp > REMOTETABLESIZE) {
-           syslog(L_NOTICE, "%s -T must be lower than %d",
-                  LogName, REMOTETABLESIZE+1);
-           temp = REMOTETABLESIZE;
-       }
-       syslog(L_NOTICE, "%s changed -T from %d to %d",
-              LogName, RemoteTotal, temp);
-       RemoteTotal = temp;
-       break;
-    case 'X':
-       RemoteTimer = (time_t) atoi(p);
-       syslog(L_NOTICE, "%s changed -X %d", LogName, (int) RemoteTimer);
-       break;
-    }
-    return NULL;
-}
-
-
-/*
-**  Common code to implement a pause or throttle.
-*/
-const char *
-CCblock(OPERATINGMODE NewMode, char *reason)
-{
-    static char                NO[] = "n";
-    char *             av[2];
-
-    if (*reason == '\0')
-       return CCnoreason;
-
-    if (strlen(reason) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
-       return CCbigreason;
-
-    if (Reservation) {
-       if (strcmp(reason, Reservation) != 0) {
-           snprintf(CCreply.data, CCreply.size, "1 Reserved \"%s\"",
-                     Reservation);
-           return CCreply.data;
-       }
-       free(Reservation);
-       Reservation = NULL;
-    }
-
-#if defined(DO_PERL)
-    PLmode(Mode, NewMode, reason);
-#endif /* defined(DO_PERL) */
-#if defined(DO_PYTHON)
-    PYmode(Mode, NewMode, reason);
-#endif /* defined(DO_PYTHON) */
-
-    ICDwrite();
-    InndHisClose();
-    Mode = NewMode;
-    if (ModeReason)
-       free(ModeReason);
-    ModeReason = xstrdup(reason);
-    if (NNRPReason == NULL && !innconf->readerswhenstopped) {
-       av[0] = NO;
-       av[1] = ModeReason;
-       CCreaders(av);
-    }
-    syslog(L_NOTICE, "%s %s %s",
-       LogName, NewMode == OMpaused ? "paused" : "throttled", reason);
-    return NULL;
-}
-
-
-/*
-**  Enter paused mode.
-*/
-static const char *
-CCpause(char *av[])
-{
-    switch (Mode) {
-    case OMrunning:
-       return CCblock(OMpaused, av[0]);
-    case OMpaused:
-       return "1 Already paused";
-    case OMthrottled:
-       return "1 Already throttled";
-    }
-    return "1 Unknown mode";
-}
-
-
-/*
-**  Allow or disallow newsreaders.
-*/
-static const char *
-CCreaders(char *av[])
-{
-    const char *p;
-
-    switch (av[0][0]) {
-    default:
-       return "1 Bad flag";
-    case 'y':
-       if (NNRPReason == NULL)
-           return "1 Already allowing readers";
-       p = av[1];
-       if (*p && strcmp(p, NNRPReason) != 0)
-           return "1 Wrong reason";
-       free(NNRPReason);
-       NNRPReason = NULL;
-       break;
-    case 'n':
-       if (NNRPReason)
-           return "1 Already not allowing readers";
-       p = av[1];
-       if (*p == '\0')
-           return CCnoreason;
-       if (strlen(p) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
-           return CCbigreason;
-       NNRPReason = xstrdup(p);
-       break;
-    }
-    return NULL;
-}
-
-
-/*
-**  Re-exec ourselves.
-*/
-static const char *
-CCxexec(char *av[])
-{
-    char       *inndstart;
-    char       *p;
-    int                i;
-
-    if (CCargv == NULL)
-       return "1 no argv!";
-    
-    inndstart = concatpath(innconf->pathbin, "inndstart");
-    /* Get the pathname. */
-    p = av[0];
-    if (*p == '\0' || strcmp(p, "innd") == 0 || strcmp(p, "inndstart") == 0)
-       CCargv[0] = inndstart;
-    else
-       return "1 Bad value";
-
-    JustCleanup();
-    syslog(L_NOTICE, "%s execv %s", LogName, CCargv[0]);
-
-    /* Close all fds to protect possible fd leaking accross successive innds. */
-    for (i=3; i<30; i++)
-        close(i);
-
-    execv(CCargv[0], CCargv);
-    syslog(L_FATAL, "%s cant execv %s %m", LogName, CCargv[0]);
-    _exit(1);
-    /* NOTREACHED */
-    return "1 Exit failed";
-}
-
-/*
-**  Reject remote readers.
-*/
-static const char *
-CCreject(char *av[])
-{
-    if (RejectReason)
-       return "1 Already rejecting";
-    if (strlen(av[0]) > MAX_REASON_LEN)        /* MAX_REASON_LEN is as big as is safe */
-       return CCbigreason;
-    RejectReason = xstrdup(av[0]);
-    return NULL;
-}
-
-
-/*
-**  Re-read all in-core data.
-*/
-static const char *
-CCreload(char *av[])
-{
-    static char        BADSCHEMA[] = "1 Can't read schema";
-#if defined(DO_PERL)
-    static char BADPERLRELOAD[] = "1 Failed to define filter_art" ;
-#endif /* defined(DO_PERL) */
-#if defined(DO_PYTHON)
-    static char BADPYRELOAD[] = "1 Failed to reload filter_innd.py" ;
-#endif /* defined(DO_PYTHON) */
-    const char *p;
-    char *path;
-
-    p = av[0];
-    if (*p == '\0' || strcmp(p, "all") == 0) {
-       SITEflushall(false);
-        if (Mode == OMrunning)
-           InndHisClose();
-       RCreadlist();
-       if (Mode == OMrunning)
-           InndHisOpen();
-       ICDwrite();
-       ICDsetup(true);
-       if (!ARTreadschema())
-           return BADSCHEMA;
-#if defined(DO_TCL)
-       TCLreadfilter();
-#endif /* defined(DO_TCL) */
-#if defined(DO_PERL)
-        path = concatpath(innconf->pathfilter, _PATH_PERL_FILTER_INND);
-        PERLreadfilter(path, "filter_art") ;
-        free(path);
-#endif /* defined(DO_PERL) */
-#if defined(DO_PYTHON)
-       syslog(L_NOTICE, "reloading pyfilter");
-        PYreadfilter();
-       syslog(L_NOTICE, "reloaded pyfilter OK");
-#endif /* DO_PYTHON */
-       p = "all";
-    }
-    else if (strcmp(p, "active") == 0 || strcmp(p, "newsfeeds") == 0) {
-       SITEflushall(false);
-       ICDwrite();
-       ICDsetup(true);
-    }
-    else if (strcmp(p, "history") == 0) {
-        if (Mode != OMrunning)
-            return CCnotrunning;
-       InndHisClose();
-       InndHisOpen();
-    }
-    else if (strcmp(p, "incoming.conf") == 0)
-       RCreadlist();
-    else if (strcmp(p, "overview.fmt") == 0) {
-       if (!ARTreadschema())
-           return BADSCHEMA;
-    }
-#if 0 /* we should check almost all innconf parameter, but the code
-         is still incomplete for innd, so just commented out */
-    else if (strcmp(p, "inn.conf") == 0) {
-        struct innconf *saved;
-
-        saved = innconf;
-        innconf = NULL;
-        if (innconf_read(NULL))
-            innconf_free(saved);
-        else {
-            innconf = saved;
-            return "1 Reload of inn.conf failed";
-        }
-       if (innconf->pathhost == NULL) {
-           syslog(L_FATAL, "%s No pathhost set", LogName);
-           exit(1);
-       }   
-       free(Path.Data);
-       Path.Used = strlen(innconf->pathhost) + 1;
-       Path.Data = xmalloc(Path.Used + 1);
-       sprintf(Path.Data, "%s!", innconf->pathhost);
-       if (Pathalias.Used > 0)
-           free(Pathalias.Data);
-       if (innconf->pathalias == NULL) {
-           Pathalias.Used = 0;
-           Pathalias.Data = NULL;
-       } else {
-           Pathalias.Used = strlen(innconf->pathalias) + 1;
-           Pathalias.Data = xmalloc(Pathalias.Used + 1);
-           sprintf(Pathalias.Data, "%s!", innconf->pathalias);
-        }
-        if (Pathcluster.Used > 0)
-            free(Pathcluster.Data);
-        if (innconf->pathcluster == NULL) {
-            Pathcluster.Used = 0;
-            Pathcluster.Data = NULL;
-        } else {
-            Pathcluster.Used = strlen(innconf->pathcluster) + 1;
-            Pathcluster.Data = xmalloc(Pathcluster.Used + 1);
-            sprintf(Pathcluster.Data, "%s!", innconf->pathcluster);
-       }
-    }
-#endif
-#if defined(DO_TCL)
-    else if (strcmp(p, "filter.tcl") == 0) {
-       TCLreadfilter();
-    }
-#endif /* defined(DO_TCL) */
-#if defined(DO_PERL)
-    else if (strcmp(p, "filter.perl") == 0) {
-        path = concatpath(innconf->pathfilter, _PATH_PERL_FILTER_INND);
-        if (!PERLreadfilter(path, "filter_art"))
-            return BADPERLRELOAD;
-    }
-#endif /* defined(DO_PERL) */
-#if defined(DO_PYTHON)
-    else if (strcmp(p, "filter.python") == 0) {
-       if (!PYreadfilter())
-           return BADPYRELOAD;
-    }
-#endif /* defined(DO_PYTHON) */
-    else
-       return "1 Unknown reload type";
-
-    syslog(L_NOTICE, "%s reload %s %s", LogName, p, av[1]);
-    return NULL;
-}
-
-
-/*
-**  Renumber the active file.
-*/
-static const char *
-CCrenumber(char *av[])
-{
-    static char        CANTRENUMBER[] = "1 Failed (see syslog)";
-    char       *p;
-    NEWSGROUP  *ngp;
-
-    if (Mode != OMrunning)
-       return CCnotrunning;
-    if (ICDneedsetup)
-       return "1 Must first reload newsfeeds";
-    p = av[0];
-    if (*p) {
-       if ((ngp = NGfind(p)) == NULL)
-           return CCnogroup;
-       if (!NGrenumber(ngp))
-           return CANTRENUMBER;
-    }
-    else if (!ICDrenumberactive())
-       return CANTRENUMBER;
-    return NULL;
-}
-
-
-/*
-**  Reserve a lock.
-*/
-static const char *
-CCreserve(char *av[])
-{
-    char       *p;
-
-    if (Mode != OMrunning)
-       return CCnotrunning;
-    p = av[0];
-    if (*p) {
-       /* Trying to make a reservation. */
-       if (Reservation)
-           return "1 Already reserved";
-       if (strlen(p) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
-           return CCbigreason;
-       Reservation = xstrdup(p);
-    }
-    else {
-       /* Trying to remove a reservation. */
-       if (Reservation == NULL)
-           return "1 Not reserved";
-       free(Reservation);
-       Reservation = NULL;
-    }
-    return NULL;
-}
-
-
-/*
-**  Remove a newsgroup.
-*/
-static const char *
-CCrmgroup(char *av[])
-{
-    NEWSGROUP  *ngp;
-
-    if ((ngp = NGfind(av[0])) == NULL)
-       return CCnogroup;
-
-    if (Mode == OMthrottled && ThrottledbyIOError)
-       return "1 server throttled";
-
-    /* Update the in-core data. */
-    if (!ICDrmgroup(ngp))
-       return "1 Failed";
-    syslog(L_NOTICE, "%s rmgroup %s", LogName, av[0]);
-    return NULL;
-}
-
-
-/*
-**  Send a command line to an exploder.
-*/
-static const char *
-CCsend(char *av[])
-{
-    SITE               *sp;
-
-    if ((sp = SITEfind(av[0])) == NULL)
-       return CCnosite;
-    if (sp->Type != FTexploder)
-       return CCwrongtype;
-    SITEwrite(sp, av[1]);
-    return NULL;
-}
-
-
-/*
-**  Shut down the system.
-*/
-static const char *
-CCshutdown(char *av[])
-{
-    syslog(L_NOTICE, "%s shutdown %s", LogName, av[0]);
-    CleanupAndExit(0, av[0]);
-    /* NOTREACHED */
-    return "1 Exit failed";
-}
-
-
-/*
-**  Send a signal to a site's feed.
-*/
-static const char *
-CCsignal(char *av[])
-{
-    SITE       *sp;
-    char       *p;
-    int                s;
-    int                oerrno;
-
-    /* Parse the signal. */
-    p = av[0];
-    if (*p == '-')
-       p++;
-    if (strcasecmp(p, "HUP") == 0)
-       s = SIGHUP;
-    else if (strcasecmp(p, "INT") == 0)
-       s = SIGINT;
-    else if (strcasecmp(p, "TERM") == 0)
-       s = SIGTERM;
-    else if ((s = atoi(p)) <= 0)
-       return "1 Invalid signal";
-
-    /* Parse the site. */
-    p = av[1];
-    if ((sp = SITEfind(p)) == NULL)
-       return CCnosite;
-    if (sp->Type != FTchannel && sp->Type != FTexploder)
-       return CCwrongtype;
-    if (sp->Process < 0)
-       return "1 Site has no process";
-
-    /* Do it. */
-    if (kill(sp->pid, s) < 0) {
-       oerrno = errno;
-       syslog(L_ERROR, "%s cant kill %ld %d site %s, %m", LogName, 
-               (long) sp->pid, s, p);
-       snprintf(CCreply.data, CCreply.size, "1 Can't signal process %ld, %s",
-               (long) sp->pid, strerror(oerrno));
-       return CCreply.data;
-    }
-
-    return NULL;
-}
-
-
-/*
-**  Enter throttled mode.
-*/
-static const char *
-CCthrottle(char *av[])
-{
-    char       *p;
-
-    p = av[0];
-    switch (Mode) {
-    case OMpaused:
-       if (*p && strcmp(p, ModeReason) != 0)
-           return "1 Already paused";
-       /* FALLTHROUGH */
-    case OMrunning:
-       return CCblock(OMthrottled, p);
-    case OMthrottled:
-       return "1 Already throttled";
-    }
-    return "1 unknown mode";
-}
-
-/*
-**  Turn on or off performance monitoring
-*/
-static const char *
-CCtimer(char *av[])
-{
-    int                 value;
-    char                *p;
-    
-    if (strcmp(av[0], "off") == 0)
-       value = 0;
-    else {
-       for (p = av[0]; *p; p++) {
-           if (!CTYPE(isdigit, *p))
-               return "1 parameter should be a number or 'off'";
-       }
-       value = atoi(av[0]);
-    }
-    innconf->timer = value;
-    if (innconf->timer)
-        TMRinit(TMR_MAX);
-    else
-       TMRinit(0);
-    return NULL;
-}
-
-/*
-**  Log into filename some history stats
-*/
-static const char *
-CCstathist(char *av[])
-{
-    if (strcmp(av[0], "off") == 0)
-        HISlogclose();
-    else
-        HISlogto(av[0]);
-    return NULL;
-}
-
-/*
-**  Turn innd status creation on or off
-*/
-static const char *
-CCstatus(char *av[])
-{
-    int                 value;
-    char                *p;
-    
-    if (strcmp(av[0], "off") == 0)
-       value = 0;
-    else {
-       for (p = av[0]; *p; p++) {
-           if (!CTYPE(isdigit, *p))
-               return "1 parameter should be a number or 'off'";
-       }
-       value = atoi(av[0]);
-    }
-    innconf->status = value;
-    return NULL;
-}
-
-/*
-**  Add or remove tracing.
-*/
-static const char *
-CCtrace(char *av[])
-{
-    char       *p;
-    bool       Flag;
-    const char *       word;
-    CHANNEL    *cp;
-
-    /* Parse the flag. */
-    p = av[1];
-    switch (p[0]) {
-    default:                   return "1 Bad trace flag";
-    case 'y': case 'Y':                Flag = true;    word = "on";    break;
-    case 'n': case 'N':                Flag = false;   word = "off";   break;
-    }
-
-    /* Parse what's being traced. */
-    p = av[0];
-    switch (*p) {
-    default:
-       return "1 Bad trace item";
-    case 'i': case 'I':
-       Tracing = Flag;
-       syslog(L_NOTICE, "%s trace innd %s", LogName, word);
-       break;
-    case 'n': case 'N':
-       NNRPTracing = Flag;
-       syslog(L_NOTICE, "%s trace nnrpd %s", LogName, word);
-       break;
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-       if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
-           return CCnochannel;
-       CHANtracing(cp, Flag);
-       break;
-    }
-    return NULL;
-}
-
-\f
-
-/*
-**  Split up the text into fields and stuff them in argv.  Return the
-**  number of elements or -1 on error.
-*/
-static int
-CCargsplit(char *p, char *end, char **argv, int size)
-{
-    char               **save;
-
-    for (save = argv, *argv++ = p, size--; p < end; p++)
-       if (*p == SC_SEP) {
-           if (--size <= 0)
-               return -1;
-           *p = '\0';
-           *argv++ = p + 1;
-       }
-    *argv = NULL;
-    return argv - save;
-}
-
-
-/*
-**  Read function.  Read and process the message.
-*/
-static void
-CCreader(CHANNEL *cp)
-{
-    static char                TOOLONG[] = "0 Reply too long for server to send";
-    CCDISPATCH         *dp;
-    const char *       p;
-    char               *q;
-    ICC_MSGLENTYPE     bufflen;
-    ICC_PROTOCOLTYPE   protocol ;
-#if    defined(HAVE_UNIX_DOMAIN_SOCKETS)
-    struct sockaddr_un client;
-#else
-    int                        written;
-#endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
-    int                        i;
-    char                buff[BIG_BUFFER + 2];
-    char                copy[BIG_BUFFER + 2];
-    char               *argv[SC_MAXFIELDS + 2];
-    int                        argc;
-    int                        len;
-    char               *tbuff ;
-
-    if (cp != CCchan) {
-       syslog(L_ERROR, "%s internal CCreader wrong channel 0x%p not 0x%p",
-           LogName, (void *)cp, (void *)CCchan);
-       return;
-    }
-
-#if defined (HAVE_UNIX_DOMAIN_SOCKETS)
-    
-    i = RECVorREAD(CCchan->fd, buff, BIG_BUFFER) ;
-    if (i < 0) {
-       syslog(L_ERROR, "%s cant recv CCreader %m", LogName);
-       return;
-    } else if (i == 0) {
-       syslog(L_ERROR, "%s cant recv CCreader empty", LogName);
-       return;
-    } else if (i < (int)HEADER_SIZE) {
-       syslog(L_ERROR, "%s cant recv CCreader header-length %m", LogName);
-       return;
-    }
-
-    memcpy (&protocol,buff,sizeof (protocol)) ;
-    memcpy (&bufflen,buff + sizeof (protocol),sizeof (bufflen)) ;
-    bufflen = ntohs (bufflen) ;
-    
-    if (i != bufflen) {
-       syslog(L_ERROR, "%s cant recv CCreader short-read %m", LogName);
-       return;
-    }
-
-    i -= HEADER_SIZE ;
-    memmove (buff,buff + HEADER_SIZE,i) ;
-    buff[i] = '\0';
-
-    if (protocol != ICC_PROTOCOL_1) {
-        syslog(L_ERROR, "%s CCreader protocol mismatch", LogName) ;
-        return ;
-    }
-
-#else  /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
-
-    i = RECVorREAD(CCchan->fd, buff, HEADER_SIZE) ;
-    if (i < 0) {
-       syslog(L_ERROR, "%s cant read CCreader header %m", LogName);
-       return;
-    } else if (i == 0) {
-       syslog(L_ERROR, "%s cant read CCreader header empty", LogName);
-       return;
-    } else if (i != HEADER_SIZE) {
-       syslog(L_ERROR, "%s cant read CCreader header-length %m", LogName);
-       return;
-    }
-
-    memcpy (&protocol,buff,sizeof (protocol)) ;
-    memcpy (&bufflen,buff + sizeof (protocol),sizeof (bufflen)) ;
-    bufflen = ntohs (bufflen);
-    if (bufflen < HEADER_SIZE || bufflen > BIG_BUFFER) {
-       syslog(L_ERROR, "%s cant read CCreader bad length", LogName);
-       return;
-    }
-    bufflen -= HEADER_SIZE ;
-    
-    i = RECVorREAD(CCchan->fd, buff, bufflen) ;
-
-    if (i < 0) {
-       syslog(L_ERROR, "%s cant read CCreader buffer %m", LogName);
-       return;
-    } else if (i == 0) {
-       syslog(L_ERROR, "%s cant read CCreader buffer empty", LogName);
-       return;
-    } else if (i != bufflen) {
-       syslog(L_ERROR, "%s cant read CCreader buffer-length %m", LogName);
-       return;
-    }
-
-    buff[i] = '\0';
-
-    if (protocol != ICC_PROTOCOL_1) {
-        syslog(L_ERROR, "%s CCreader protocol mismatch", LogName) ;
-        return ;
-    }
-
-#endif /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
-    
-    /* Copy to a printable buffer, and log. */
-    strcpy(copy, buff);
-    for (p = NULL, q = copy; *q; q++)
-       if (*q == SC_SEP) {
-           *q = ':';
-           if (p == NULL)
-               p = q + 1;
-       }
-    syslog(L_CC_CMD, "%s", p ? p : copy);
-
-    /* Split up the fields, get the command letter. */
-    if ((argc = CCargsplit(buff, &buff[i], argv, ARRAY_SIZE(argv))) < 2
-     || argc == ARRAY_SIZE(argv)) {
-       syslog(L_ERROR, "%s bad_fields CCreader", LogName);
-       return;
-    }
-    p = argv[1];
-    i = *p;
-
-    /* Dispatch to the command function. */
-    for (argc -= 2, dp = CCcommands; dp < ARRAY_END(CCcommands); dp++)
-       if (i == dp->Name) {
-           if (argc != dp->argc)
-               p = "1 Wrong number of parameters";
-           else
-               p = (*dp->Function)(&argv[2]);
-           break;
-       }
-    if (dp == ARRAY_END(CCcommands)) {
-       syslog(L_NOTICE, "%s bad_message %c", LogName, i);
-       p = "1 Bad command";
-    }
-    else if (p == NULL)
-       p = "0 Ok";
-
-    /* Build the reply address and send the reply. */
-    len = strlen(p) + HEADER_SIZE ;
-    tbuff = xmalloc(len + 1);
-    
-    protocol = ICC_PROTOCOL_1 ;
-    memcpy (tbuff,&protocol,sizeof (protocol)) ;
-    tbuff += sizeof (protocol) ;
-    
-    bufflen = htons (len) ;
-    memcpy (tbuff,&bufflen,sizeof (bufflen)) ;
-    tbuff += sizeof (bufflen) ;
-
-    strcpy (tbuff,p) ;
-    tbuff -= HEADER_SIZE ;
-
-#if    defined(HAVE_UNIX_DOMAIN_SOCKETS)
-    memset(&client, 0, sizeof client);
-    client.sun_family = AF_UNIX;
-    strcpy(client.sun_path, argv[0]);
-    if (sendto(CCwriter, tbuff, len, 0,
-           (struct sockaddr *) &client, SUN_LEN(&client)) < 0) {
-       i = errno;
-       syslog(i == ENOENT ? L_NOTICE : L_ERROR,
-           "%s cant sendto CCreader bytes %d %m", LogName, len);
-       if (i == EMSGSIZE)
-           sendto(CCwriter, TOOLONG, strlen(TOOLONG), 0,
-               (struct sockaddr *) &client, SUN_LEN(&client));
-    }
-#else
-    if ((i = open(argv[0], O_WRONLY | O_NDELAY)) < 0)
-       syslog(L_ERROR, "%s cant open %s %m", LogName, argv[0]);
-    else {
-       if ((written = write(i, tbuff, len)) != len)
-           if (written < 0)
-               syslog(L_ERROR, "%s cant write %s %m", LogName, argv[0]);
-           else
-               syslog(L_ERROR, "%s cant write %s", LogName, argv[0]);
-       if (close(i) < 0)
-           syslog(L_ERROR, "%s cant close %s %m", LogName, argv[0]);
-    }
-#endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
-    free (tbuff) ;
-}
-
-
-/*
-**  Called when a write-in-progress is done on the channel.  Shouldn't happen.
-*/
-static void
-CCwritedone(CHANNEL *unused)
-{
-    unused = unused;           /* ARGSUSED */
-    syslog(L_ERROR, "%s internal CCwritedone", LogName);
-}
-
-
-/*
-**  Create the channel.
-*/
-void
-CCsetup(void)
-{
-    int                        i;
-#if    defined(HAVE_UNIX_DOMAIN_SOCKETS)
-    struct sockaddr_un server;
-    int size = 65535;
-#endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
-
-    if (CCpath == NULL)
-       CCpath = concatpath(innconf->pathrun, _PATH_NEWSCONTROL);
-    /* Remove old detritus. */
-    if (unlink(CCpath) < 0 && errno != ENOENT) {
-       syslog(L_FATAL, "%s cant unlink %s %m", LogName, CCpath);
-       exit(1);
-    }
-
-#if    defined(HAVE_UNIX_DOMAIN_SOCKETS)
-    /* Create a socket and name it. */
-    if ((i = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
-       syslog(L_FATAL, "%s cant socket %s %m", LogName, CCpath);
-       exit(1);
-    }
-    memset(&server, 0, sizeof server);
-    server.sun_family = AF_UNIX;
-    strcpy(server.sun_path, CCpath);
-    if (bind(i, (struct sockaddr *) &server, SUN_LEN(&server)) < 0) {
-       syslog(L_FATAL, "%s cant bind %s %m", LogName, CCpath);
-       exit(1);
-    }
-
-    /* Create an unbound socket to reply on. */
-    if ((CCwriter = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
-       syslog(L_FATAL, "%s cant socket unbound %m", LogName);
-       exit(1);
-    }
-
-    /* Increase the buffer size for the Unix domain socket */
-    if (setsockopt(CCwriter, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
-       syslog(L_ERROR, "%s cant setsockopt %m", LogName);
-#else
-    /* Create a named pipe and open it. */
-    if (mkfifo(CCpath, 0666) < 0) {
-       syslog(L_FATAL, "%s cant mkfifo %s %m", LogName, CCpath);
-       exit(1);
-    }
-    if ((i = open(CCpath, O_RDWR)) < 0) {
-       syslog(L_FATAL, "%s cant open %s %m", LogName, CCpath);
-       exit(1);
-    }
-#endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
-
-    CCchan = CHANcreate(i, CTcontrol, CSwaiting, CCreader, CCwritedone);
-    syslog(L_NOTICE, "%s ccsetup %s", LogName, CHANname(CCchan));
-    RCHANadd(CCchan);
-
-    buffer_resize(&CCreply, SMBUF);
-
-    /*
-     *  Catch SIGUSR1 so that we can recreate the control channel when
-     *  needed (i.e. something has deleted our named socket.
-     */
-#if     defined(SIGUSR1)
-    xsignal(SIGUSR1, CCresetup);
-#endif  /* defined(SIGUSR1) */
-}
-
-
-/*
-**  Cleanly shut down the channel.
-*/
-void
-CCclose(void)
-{
-    CHANclose(CCchan, CHANname(CCchan));
-    CCchan = NULL;
-    if (unlink(CCpath) < 0)
-       syslog(L_ERROR, "%s cant unlink %s %m", LogName, CCpath);
-    free(CCpath);
-    CCpath = NULL;
-    free(CCreply.data);
-    CCreply.data = NULL;
-    CCreply.size = 0;
-    CCreply.used = 0;
-    CCreply.left = 0;
-#if    defined(HAVE_UNIX_DOMAIN_SOCKETS)
-    if (close(CCwriter) < 0)
-       syslog(L_ERROR, "%s cant close unbound %m", LogName);
-#endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
-}
-
-
-/*
-**  Restablish the control channel.
-*/
-static RETSIGTYPE
-CCresetup(int unused)
-{
-#ifndef HAVE_SIGACTION
-    xsignal(s, CCresetup);
-#else
-    unused = unused;           /* ARGSUSED */
-#endif
-    CCclose();
-    CCsetup();
-}
-
-
-/*
- * Read a file containing lines of the form "newsgroup lowmark",
- * and reset the low article number for the specified groups.
- */
-static const char *
-CClowmark(char *av[])
-{
-    long lo;
-    char *line, *cp;
-    const char  *ret = NULL;
-    QIOSTATE *qp;
-    NEWSGROUP *ngp;
-
-    if (Mode != OMrunning)
-       return CCnotrunning;
-    if (ICDneedsetup)
-       return "1 Must first reload newsfeeds";
-    if ((qp = QIOopen(av[0])) == NULL) {
-       syslog(L_ERROR, "%s cant open %s %m", LogName, av[0]);
-       return "1 Cannot read input file";
-    }
-    while ((line = QIOread(qp)) != NULL) {
-       if (QIOerror(qp))
-               break;
-       if (QIOtoolong(qp)) {
-           ret = "1 Malformed input line (too long)";
-           break;
-       }
-       while (ISWHITE(*line))
-           line++;
-       for (cp = line; *cp && !ISWHITE(*cp); cp++)
-           ;
-       if (*cp == '\0') {
-           ret = "1 Malformed input line (only one field)";
-           break;
-       }
-       *cp++ = '\0';
-       while (ISWHITE(*cp))
-           cp++;
-       if (strspn(cp, "0123456789") != strlen(cp)) {
-           ret = "1 Malformed input line (non-digit in low mark)";
-           break;
-       }
-       if ((lo = atol(cp)) == 0 && (cp[0] != '0' || cp[1] != '\0')) {
-           ret = "1 Malformed input line (bad low mark)";
-           break;
-       }
-        if ((ngp = NGfind(line)) == NULL) {
-           /* ret = CCnogroup; break; */
-           continue;
-       }
-        if (!NGlowmark(ngp, lo)) {
-           ret = "1 Cannot set low mark - see syslog";
-           break;
-       }
-    }
-    if (ret == NULL && QIOerror(qp)) {
-       syslog(L_ERROR, "%s cant read %s %m", LogName, av[0]);
-       ret = "1 Error reading input file";
-    }
-    QIOclose(qp);
-    ICDwrite();
-    return ret;
-}