X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=innd%2Fcc.c;fp=innd%2Fcc.c;h=0000000000000000000000000000000000000000;hb=b7a32e2d73e3ab1add8208d3e157f7269a31ef4d;hp=be171838ef359594eba23c8a3acd808a54f1aba2;hpb=ac902a8299ff4469b356836f431ead31c3377377;p=innduct.git diff --git a/innd/cc.c b/innd/cc.c deleted file mode 100644 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 -#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); - - -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 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; -} - - - -/* -** 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; -}