+++ /dev/null
-/* $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;
-}