1 /* $Id: cc.c 7748 2008-04-06 13:49:56Z iulius $
3 ** Routines for the control channel.
5 ** Create a Unix-domain datagram socket that processes on the local server
6 ** send messages to. The control channel is used only by ctlinnd to tell
7 ** the server to perform special functions. We use datagrams so that we
8 ** don't need to do an accept() and tie up another descriptor. recvfrom
9 ** seems to be broken on several systems, so the client passes in the
12 ** This module completely rips away all pretense of software layering.
18 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
22 #include "inn/innconf.h"
29 ** An entry in the dispatch table. The name, and implementing function,
30 ** of every command we support.
32 typedef struct _CCDISPATCH {
35 const char * (*Function)(char *av[]);
39 static const char * CCallow(char *av[]);
40 static const char * CCbegin(char *av[]);
41 static const char * CCchgroup(char *av[]);
42 static const char * CCdrop(char *av[]);
43 static const char * CCfeedinfo(char *av[]);
44 static const char * CCflush(char *av[]);
45 static const char * CCflushlogs(char *unused[]);
46 static const char * CCgo(char *av[]);
47 static const char * CChangup(char *av[]);
48 static const char * CCreserve(char *av[]);
49 static const char * CClogmode(char *unused[]);
50 static const char * CCmode(char *unused[]);
51 static const char * CCname(char *av[]);
52 static const char * CCnewgroup(char *av[]);
53 static const char * CCparam(char *av[]);
54 static const char * CCpause(char *av[]);
55 static const char * CCreaders(char *av[]);
56 static const char * CCreject(char *av[]);
57 static const char * CCreload(char *av[]);
58 static const char * CCrenumber(char *av[]);
59 static const char * CCrmgroup(char *av[]);
60 static const char * CCsend(char *av[]);
61 static const char * CCshutdown(char *av[]);
62 static const char * CCsignal(char *av[]);
63 static const char * CCstathist(char *av[]);
64 static const char * CCstatus(char *av[]);
65 static const char * CCthrottle(char *av[]);
66 static const char * CCtimer(char *av[]);
67 static const char * CCtrace(char *av[]);
68 static const char * CCxabort(char *av[]);
69 static const char * CCxexec(char *av[]);
70 static const char * CCfilter(char *av[]);
71 static const char * CCperl(char *av[]);
72 static const char * CCpython(char *av[]);
73 static const char * CClowmark(char *av[]);
76 static char *CCpath = NULL;
78 static char CCnosite[] = "1 No such site";
79 static char CCwrongtype[] = "1 Wrong site type";
80 static char CCnogroup[] = "1 No such group";
81 static char CCnochannel[] = "1 No such channel";
82 static char CCnoreason[] = "1 Empty reason";
83 static char CCbigreason[] = "1 Reason too long";
84 static char CCnotrunning[] = "1 Must be running";
85 static struct buffer CCreply;
86 static CHANNEL *CCchan;
88 static CCDISPATCH CCcommands[] = {
89 { SC_ADDHIST, 5, CCaddhist },
90 { SC_ALLOW, 1, CCallow },
91 { SC_BEGIN, 1, CCbegin },
92 { SC_CANCEL, 1, CCcancel },
93 { SC_CHANGEGROUP, 2, CCchgroup },
94 { SC_CHECKFILE, 0, CCcheckfile },
95 { SC_DROP, 1, CCdrop },
96 { SC_FEEDINFO, 1, CCfeedinfo },
97 { SC_FILTER, 1, CCfilter },
98 { SC_PERL, 1, CCperl },
99 { SC_PYTHON, 1, CCpython },
100 { SC_FLUSH, 1, CCflush },
101 { SC_FLUSHLOGS, 0, CCflushlogs },
103 { SC_HANGUP, 1, CChangup },
104 { SC_LOGMODE, 0, CClogmode },
105 { SC_MODE, 0, CCmode },
106 { SC_NAME, 1, CCname },
107 { SC_NEWGROUP, 3, CCnewgroup },
108 { SC_PARAM, 2, CCparam },
109 { SC_PAUSE, 1, CCpause },
110 { SC_READERS, 2, CCreaders },
111 { SC_REJECT, 1, CCreject },
112 { SC_RENUMBER, 1, CCrenumber },
113 { SC_RELOAD, 2, CCreload },
114 { SC_RESERVE, 1, CCreserve },
115 { SC_RMGROUP, 1, CCrmgroup },
116 { SC_SEND, 2, CCsend },
117 { SC_SHUTDOWN, 1, CCshutdown },
118 { SC_SIGNAL, 2, CCsignal },
119 { SC_STATHIST, 1, CCstathist },
120 { SC_STATUS, 1, CCstatus },
121 { SC_THROTTLE, 1, CCthrottle },
122 { SC_TIMER, 1, CCtimer },
123 { SC_TRACE, 2, CCtrace },
124 { SC_XABORT, 1, CCxabort },
125 { SC_LOWMARK, 1, CClowmark },
126 { SC_XEXEC, 1, CCxexec }
129 static RETSIGTYPE CCresetup(int unused);
133 CCcopyargv(char *av[])
138 /* Get the vector size. */
139 for (i = 0; av[i]; i++)
142 /* Get the vector, copy each element. */
143 for (v = CCargv = xmalloc((i + 1) * sizeof(char *)); *av; av++) {
144 /* not to renumber */
145 if (strncmp(*av, "-r", 2) == 0)
154 ** Return a string representing our operating mode.
159 static char buff[32];
164 snprintf(buff, sizeof(buff), "Unknown %d", Mode);
177 ** Add <> around Message-ID if needed.
180 CCgetid(char *p, const char **store)
182 static char NULLMESGID[] = "1 Empty Message-ID";
183 static struct buffer Save = { 0, 0, 0, NULL };
189 if (p[1] == '\0' || p[1] == '>')
195 /* Make sure the Message-ID buffer has room. */
196 i = 1 + strlen(p) + 1 + 1;
197 buffer_resize(&Save, i);
199 snprintf(Save.data, Save.size, "<%s>", p);
205 ** Abort and dump core.
210 syslog(L_FATAL, "%s abort %s", LogName, av[0]);
212 syslog(L_FATAL, "%s cant abort %m", LogName);
213 CleanupAndExit(1, av[0]);
219 ** Do the work needed to add a history entry.
222 CCaddhist(char *av[])
224 static char DIGITS[] = "0123456789";
226 const char * p, *msgid;
230 /* You must pass a <message-id> ID, the history API will hash it as it
232 if ((p = CCgetid(av[0], &msgid)) != NULL)
235 /* If paused, don't try to use the history database since expire may be
237 if (Mode == OMpaused)
238 return "1 Server paused";
240 /* If throttled by admin, briefly open the history database. */
241 if (Mode != OMrunning) {
242 if (ThrottledbyIOError)
243 return "1 Server throttled";
247 if (HIScheck(History, msgid)) {
248 if (Mode != OMrunning) InndHisClose();
249 return "1 Duplicate";
251 if (Mode != OMrunning) InndHisClose();
252 if (strspn(av[1], DIGITS) != strlen(av[1]))
253 return "1 Bad arrival date";
254 Data.Arrived = atol(av[1]);
255 if (strspn(av[2], DIGITS) != strlen(av[2]))
256 return "1 Bad expiration date";
257 Data.Expires = atol(av[2]);
258 if (strspn(av[3], DIGITS) != strlen(av[3]))
259 return "1 Bad posted date";
260 Data.Posted = atol(av[3]);
262 token = TextToToken(av[4]);
263 if (Mode == OMrunning)
264 ok = InndHisWrite(msgid, Data.Arrived, Data.Posted,
265 Data.Expires, &token);
267 /* Possible race condition, but documented in ctlinnd manpage. */
269 ok = InndHisWrite(msgid, Data.Arrived, Data.Posted,
270 Data.Expires, &token);
273 return ok ? NULL : "1 Write failed";
278 ** Do the work to allow foreign connectiosn.
285 if (RejectReason == NULL)
286 return "1 Already allowed";
288 if (*p && strcmp(p, RejectReason) != 0)
289 return "1 Wrong reason";
297 ** Do the work needed to start feeding a (new) site.
313 /* If site already exists, drop it. */
314 if (SITEfind(av[0]) != NULL && (p1 = CCdrop(av)) != NULL)
317 /* Find the named site. */
318 length = strlen(av[0]);
319 for (strings = SITEreadfile(true), i = 0; (p = strings[i]) != NULL; i++)
320 if ((p[length] == NF_FIELD_SEP || p[length] == NF_SUBFIELD_SEP)
321 && strncasecmp(p, av[0], length) == 0) {
328 if (p[0] == 'M' && p[1] == 'E' && p[2] == NF_FIELD_SEP)
331 /* Get space for the new site entry, and space for it in all
333 for (i = nSites, sp = Sites; --i >= 0; sp++)
334 if (sp->Name == NULL)
338 Sites = xrealloc(Sites, nSites * sizeof(SITE));
339 sp = &Sites[nSites - 1];
340 sp->Next = sp->Prev = NOSITE;
341 for (i = nGroups, ngp = Groups; --i >= 0; ngp++) {
342 ngp->Sites = xrealloc(ngp->Sites, nSites * sizeof(int));
343 ngp->Poison = xrealloc(ngp->Poison, nSites * sizeof(int));
349 subbed = xmalloc(nGroups);
350 poison = xmalloc(nGroups);
351 error = SITEparseone(p, sp, subbed, poison);
356 syslog(L_ERROR, "%s bad_newsfeeds %s", av[0], error);
357 return "1 Parse error";
360 if (sp != &ME && (!SITEsetup(sp) || !SITEfunnelpatch()))
361 return "1 Startup error";
362 SITEforward(sp, "begin");
368 ** Common code to change a group's flags.
371 CCdochange(NEWSGROUP *ngp, char *Rest)
376 if (ngp->Rest[0] == Rest[0]) {
377 length = strlen(Rest);
378 if (ngp->Rest[length] == '\n' && strncmp(ngp->Rest, Rest, length) == 0)
379 return "0 Group status unchanged";
381 if (Mode != OMrunning)
384 p = xstrdup(ngp->Name);
385 if (!ICDchangegroup(ngp, Rest)) {
386 syslog(L_NOTICE, "%s cant change_group %s to %s", LogName, p, Rest);
388 return "1 Change failed (probably can't write active?)";
390 syslog(L_NOTICE, "%s change_group %s to %s", LogName, p, Rest);
397 ** Change the mode of a newsgroup.
400 CCchgroup(char *av[])
405 if ((ngp = NGfind(av[0])) == NULL)
408 if (Rest[0] != NF_FLAG_ALIAS) {
410 if (CTYPE(isupper, Rest[0]))
411 Rest[0] = tolower(Rest[0]);
413 return CCdochange(ngp, Rest);
424 const char * p, *msgid;
426 Data.Posted = Data.Arrived = Now.time;
429 if ((p = CCgetid(av[0], &msgid)) != NULL)
432 Data.HdrContent[HDR__MESSAGE_ID].Value = (char *)msgid;
433 Data.HdrContent[HDR__MESSAGE_ID].Length = strlen(msgid);
434 if (Mode == OMrunning)
435 ARTcancel(&Data, msgid, true);
437 /* If paused, don't try to use the history database since expire may be
439 if (Mode == OMpaused)
440 return "1 Server paused";
441 if (ThrottledbyIOError)
442 return "1 Server throttled";
443 /* Possible race condition, but documented in ctlinnd manpage. */
445 ARTcancel(&Data, msgid, true);
448 if (innconf->logcancelcomm)
449 syslog(L_NOTICE, "%s cancelled %s", LogName, msgid);
455 ** Syntax-check the newsfeeds file.
458 CCcheckfile(char *unused[])
466 bool needheaders, needoverview, needpath, needstoredgroup;
469 unused = unused; /* ARGSUSED */
470 /* Parse all site entries. */
471 strings = SITEreadfile(false);
472 fake.Buffer.size = 0;
473 fake.Buffer.data = NULL;
474 /* save global variables not to be changed */
475 needheaders = NeedHeaders;
476 needoverview = NeedOverview;
478 needstoredgroup = NeedStoredGroup;
479 needreplicdata = NeedReplicdata;
480 for (errors = 0, i = 0; (p = strings[i]) != NULL; i++) {
481 if ((error = SITEparseone(p, &fake, (char *)NULL, (char *)NULL)) != NULL) {
482 syslog(L_ERROR, "%s bad_newsfeeds %s", MaxLength(p, p), error);
488 /* restore global variables not to be changed */
489 NeedHeaders = needheaders;
490 NeedOverview = needoverview;
492 NeedStoredGroup = needstoredgroup;
493 NeedReplicdata = needreplicdata;
498 buffer_resize(&CCreply, SMBUF);
499 snprintf(CCreply.data, CCreply.size, "1 Found %d errors -- see syslog",
518 if ((sp = SITEfind(av[0])) == NULL)
523 /* Loop over all groups, and if the site is in a group, clobber it. */
524 for (idx = sp - Sites, i = nGroups, ngp = Groups; --i >= 0; ngp++) {
525 for (j = ngp->nSites, ip = ngp->Sites; --j >= 0; ip++)
528 for (j = ngp->nPoison, ip = ngp->Poison; --j >= 0; ip++)
537 ** Return info on the feeds for one, or all, sites
540 CCfeedinfo(char *av[])
546 buffer_set(&CCreply, "0 ", 2);
549 if ((sp = SITEfind(p)) == NULL)
550 return "1 No such site";
552 SITEinfo(&CCreply, sp, true);
553 while ((sp = SITEfindnext(p, sp)) != NULL)
554 SITEinfo(&CCreply, sp, true);
557 for (i = nSites, sp = Sites; --i >= 0; sp++)
559 SITEinfo(&CCreply, sp, false);
561 buffer_append(&CCreply, "", 1);
567 CCfilter(char *av[] UNUSED)
577 return "1 tcl filter already enabled";
581 if (!TCLFilterActive)
582 return "1 tcl filter already disabled";
587 #else /* defined(DO_TCL) */
588 return "1 TCL filtering support not compiled in";
589 #endif /* defined(DO_TCL) */
601 if (PerlFilterActive)
602 return "1 Perl filter already enabled";
603 else if (!PerlFilter(true))
604 return "1 Perl filter not defined";
607 if (!PerlFilterActive)
608 return "1 Perl filter already disabled";
613 #else /* defined(DO_PERL) */
614 return "1 Perl filtering support not compiled in";
615 #endif /* defined(DO_PERL) */
620 CCpython(char *av[] UNUSED)
622 #if defined(DO_PYTHON)
623 return PYcontrol(av);
624 #else /* defined(DO_PYTHON) */
625 return "1 Python filtering support not compiled in";
626 #endif /* defined(DO_PYTHON) */
631 ** Flush all sites or one site.
643 for (sp = Sites, i = nSites; --i >= 0; sp++)
645 syslog(L_NOTICE, "%s flush_all", LogName);
648 if ((sp = SITEfind(p)) == NULL)
650 syslog(L_NOTICE, "%s flush", sp->Name);
658 ** Flush the log files.
661 CCflushlogs(char *unused[])
663 unused = unused; /* ARGSUSED */
666 return "1 In debug mode";
669 syslog(L_NOTICE, "%s flushlogs %s", LogName, CCcurrmode());
677 ** Leave paused or throttled mode.
682 static char YES[] = "y";
686 if (Reservation && strcmp(p, Reservation) == 0) {
690 if (RejectReason && strcmp(p, RejectReason) == 0) {
695 if (Mode == OMrunning)
696 return "1 Already running";
697 if (*p && strcmp(p, ModeReason) != 0)
698 return "1 Wrong reason";
701 PLmode(Mode, OMrunning, p);
702 #endif /* defined(DO_PERL) */
703 #if defined(DO_PYTHON)
704 PYmode(Mode, OMrunning, p);
705 #endif /* defined(DO_PYTHON) */
710 ThrottledbyIOError = false;
712 if (NNRPReason && !innconf->readerswhenstopped) {
718 ErrorCount = IO_ERROR_COUNT;
720 syslog(L_NOTICE, "%s running", LogName);
739 /* Parse the argument, a channel number. */
740 for (p = av[0], fd = 0; *p; p++) {
741 if (!CTYPE(isdigit, *p))
742 return "1 Bad channel number";
743 fd = fd * 10 + *p - '0';
746 /* Loop over all channels for the desired one. */
747 for (i = 0; (cp = CHANiter(&i, CTany)) != NULL; )
752 snprintf(CCreply.data, CCreply.size, "1 Can't close %s", p);
759 syslog(L_NOTICE, "%s hangup", p);
764 return "1 Not active";
769 ** Return our operating mode.
772 CCmode(char *unused[] UNUSED)
780 #endif /* defined(DO_PERL) */
782 /* FIXME: We assume here that BUFSIZ is >= 512, and that none of
783 * ModeReason, RejectReason, Reservation, NNRPReason, or the Perl filter
784 * statistics are longer than MAX_REASON_LEN bytes (or actually, the
785 * average of their lengths is <= MAX_REASON_LEN). If this is not true,
786 * the sprintf's/strcpy's below are likely to overflow buff with somewhat
787 * nasty consequences...
791 p += strlen(strcpy(buff, "0 Server "));
796 sprintf(p, "Unknown %d", Mode);
800 p += strlen(strcpy(p, "running"));
803 p += strlen(strcpy(p, "paused "));
804 p += strlen(strcpy(p, ModeReason));
807 p += strlen(strcpy(p, "throttled "));
808 p += strlen(strcpy(p, ModeReason));
813 p += strlen(strcpy(p, "Rejecting "));
814 p += strlen(strcpy(p, RejectReason));
817 p += strlen(strcpy(p, "Allowing remote connections"));
819 /* Server parameters. */
820 for (i = 0, h = 0; CHANiter(&h, CTnntp) != NULL; )
823 sprintf(p, "Parameters c %ld i %ld (%d) l %ld o %d t %ld H %d T %d X %d %s %s",
824 innconf->artcutoff, innconf->maxconnections, i,
825 innconf->maxartsize, MaxOutgoing, (long)TimeOut.tv_sec,
826 RemoteLimit, RemoteTotal, (int) RemoteTimer,
827 innconf->xrefslave ? "slave" : "normal",
828 AnyIncoming ? "any" : "specified");
834 sprintf(p, "Reserved %s", Reservation);
838 p += strlen(strcpy(p, "Not reserved"));
842 p += strlen(strcpy(p, "Readers "));
843 if (innconf->readerswhenstopped)
844 p += strlen(strcpy(p, "independent "));
846 p += strlen(strcpy(p, "follow "));
847 if (NNRPReason == NULL)
848 p += strlen(strcpy(p, "enabled"));
850 sprintf(p, "disabled %s", NNRPReason);
856 p += strlen(strcpy(p, "Tcl filtering "));
858 p += strlen(strcpy(p, "enabled"));
860 p += strlen(strcpy(p, "disabled"));
861 #endif /* defined(DO_TCL) */
865 p += strlen(strcpy(p, "Perl filtering "));
866 if (PerlFilterActive)
867 p += strlen(strcpy(p, "enabled"));
869 p += strlen(strcpy(p, "disabled"));
871 /* Perl filter status. */
875 p += strlen(strcpy(p, "Perl filter stats: "));
876 p += strlen(strcpy(p, stats));
879 #endif /* defined(DO_PERL) */
881 #if defined(DO_PYTHON)
883 p += strlen(strcpy(p, "Python filtering "));
884 if (PythonFilterActive)
885 p += strlen(strcpy(p, "enabled"));
887 p += strlen(strcpy(p, "disabled"));
888 #endif /* defined(DO_PYTHON) */
890 buffer_set(&CCreply, buff, strlen(buff) + 1);
896 ** Log our operating mode (via syslog).
899 CClogmode(char *unused[])
901 unused = unused; /* ARGSUSED */
902 syslog(L_NOTICE, "%s servermode %s", LogName, CCcurrmode());
908 ** Name the channels. ("Name the bats -- simple names.")
913 static char NL[] = "\n";
914 static char NIL[] = "\0";
923 if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
924 return xstrdup(CCnochannel);
925 snprintf(CCreply.data, CCreply.size, "0 %s", CHANname(cp));
928 buffer_set(&CCreply, "0 ", 2);
929 for (count = 0, i = 0; (cp = CHANiter(&i, CTany)) != NULL; ) {
930 if (cp->Type == CTfree)
933 buffer_append(&CCreply, NL, 1);
935 buffer_append(&CCreply, p, strlen(p));
938 sprintf(buff, ":remconn::");
941 sprintf(buff, ":reject::");
944 sprintf(buff, ":%s:%ld:%s",
945 cp->State == CScancel ? "cancel" : "nntp",
946 (long) Now.time - cp->LastActive,
947 (cp->MaxCnx > 0 && cp->ActiveCnx == 0) ? "paused" : "");
950 sprintf(buff, ":localconn::");
953 sprintf(buff, ":control::");
959 sprintf(buff, ":exploder::");
965 sprintf(buff, ":unknown::");
969 buffer_append(&CCreply, p, strlen(p));
971 buffer_append(&CCreply, NIL, 1);
977 ** Create a newsgroup.
980 CCnewgroup(char *av[])
982 static char *TIMES = NULL;
983 static char WHEN[] = "updating active.times";
995 TIMES = concatpath(innconf->pathdb, _PATH_ACTIVETIMES);
998 if (Name[0] == '.' || strspn(Name, "0123456789") == strlen(Name))
999 return "1 Illegal newsgroup name";
1000 for (p = Name; *p; p++)
1002 if (p[1] == '.' || p[1] == '\0')
1003 return "1 Double or trailing period in newsgroup name";
1005 else if (ISWHITE(*p) || *p == ':' || *p == '!' || *p == '/')
1006 return "1 Illegal character in newsgroup name";
1009 if (Rest[0] != NF_FLAG_ALIAS) {
1011 if (CTYPE(isupper, Rest[0]))
1012 Rest[0] = tolower(Rest[0]);
1014 if (strlen(Name) + strlen(Rest) > SMBUF - 24)
1015 return "1 Name too long";
1017 if ((ngp = NGfind(Name)) != NULL)
1018 return CCdochange(ngp, Rest);
1020 if (Mode == OMthrottled && ThrottledbyIOError)
1021 return "1 server throttled";
1023 /* Update the log of groups created. Don't use stdio because SunOS
1024 * 4.1 has broken libc which can't handle fd's greater than 127. */
1025 if ((fd = open(TIMES, O_WRONLY | O_APPEND | O_CREAT, 0664)) < 0) {
1027 syslog(L_ERROR, "%s cant open %s %m", LogName, TIMES);
1028 IOError(WHEN, oerrno);
1035 length = snprintf(NULL, 0, "%s %ld %s\n", Name, (long) Now.time, who) + 1;
1036 buff = xmalloc(length);
1037 snprintf(buff, length, "%s %ld %s\n", Name, (long) Now.time, who);
1038 if (xwrite(fd, buff, strlen(buff)) < 0) {
1040 syslog(L_ERROR, "%s cant write %s %m", LogName, TIMES);
1041 IOError(WHEN, oerrno);
1046 if (close(fd) < 0) {
1048 syslog(L_ERROR, "%s cant close %s %m", LogName, TIMES);
1049 IOError(WHEN, oerrno);
1053 /* Update the in-core data. */
1054 if (!ICDnewgroup(Name, Rest))
1056 syslog(L_NOTICE, "%s newgroup %s as %s", LogName, Name, Rest);
1063 ** Parse and set a boolean flag.
1066 CCparsebool(char name, bool *bp, char value)
1078 syslog(L_NOTICE, "%s changed -%c %c", LogName, name, value);
1084 ** Change a running parameter.
1089 static char BADVAL[] = "1 Bad value";
1096 return "1 Unknown parameter";
1098 if (!CCparsebool('a', (bool *)&AnyIncoming, *p))
1102 innconf->artcutoff = atoi(p);
1103 syslog(L_NOTICE, "%s changed -c %ld", LogName, innconf->artcutoff);
1106 innconf->maxconnections = atoi(p);
1107 syslog(L_NOTICE, "%s changed -i %ld", LogName, innconf->maxconnections);
1110 innconf->maxartsize = atol(p);
1111 syslog(L_NOTICE, "%s changed -l %ld", LogName, innconf->maxartsize);
1114 if (!CCparsebool('n', (bool *)&innconf->readerswhenstopped, *p))
1118 MaxOutgoing = atoi(p);
1119 syslog(L_NOTICE, "%s changed -o %d", LogName, MaxOutgoing);
1122 TimeOut.tv_sec = atol(p);
1123 syslog(L_NOTICE, "%s changed -t %ld", LogName, (long)TimeOut.tv_sec);
1126 RemoteLimit = atoi(p);
1127 syslog(L_NOTICE, "%s changed -H %d", LogName, RemoteLimit);
1131 if (temp > REMOTETABLESIZE) {
1132 syslog(L_NOTICE, "%s -T must be lower than %d",
1133 LogName, REMOTETABLESIZE+1);
1134 temp = REMOTETABLESIZE;
1136 syslog(L_NOTICE, "%s changed -T from %d to %d",
1137 LogName, RemoteTotal, temp);
1141 RemoteTimer = (time_t) atoi(p);
1142 syslog(L_NOTICE, "%s changed -X %d", LogName, (int) RemoteTimer);
1150 ** Common code to implement a pause or throttle.
1153 CCblock(OPERATINGMODE NewMode, char *reason)
1155 static char NO[] = "n";
1158 if (*reason == '\0')
1161 if (strlen(reason) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
1165 if (strcmp(reason, Reservation) != 0) {
1166 snprintf(CCreply.data, CCreply.size, "1 Reserved \"%s\"",
1168 return CCreply.data;
1174 #if defined(DO_PERL)
1175 PLmode(Mode, NewMode, reason);
1176 #endif /* defined(DO_PERL) */
1177 #if defined(DO_PYTHON)
1178 PYmode(Mode, NewMode, reason);
1179 #endif /* defined(DO_PYTHON) */
1186 ModeReason = xstrdup(reason);
1187 if (NNRPReason == NULL && !innconf->readerswhenstopped) {
1192 syslog(L_NOTICE, "%s %s %s",
1193 LogName, NewMode == OMpaused ? "paused" : "throttled", reason);
1199 ** Enter paused mode.
1206 return CCblock(OMpaused, av[0]);
1208 return "1 Already paused";
1210 return "1 Already throttled";
1212 return "1 Unknown mode";
1217 ** Allow or disallow newsreaders.
1220 CCreaders(char *av[])
1226 return "1 Bad flag";
1228 if (NNRPReason == NULL)
1229 return "1 Already allowing readers";
1231 if (*p && strcmp(p, NNRPReason) != 0)
1232 return "1 Wrong reason";
1238 return "1 Already not allowing readers";
1242 if (strlen(p) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
1244 NNRPReason = xstrdup(p);
1252 ** Re-exec ourselves.
1262 return "1 no argv!";
1264 inndstart = concatpath(innconf->pathbin, "inndstart");
1265 /* Get the pathname. */
1267 if (*p == '\0' || strcmp(p, "innd") == 0 || strcmp(p, "inndstart") == 0)
1268 CCargv[0] = inndstart;
1270 return "1 Bad value";
1273 syslog(L_NOTICE, "%s execv %s", LogName, CCargv[0]);
1275 /* Close all fds to protect possible fd leaking accross successive innds. */
1276 for (i=3; i<30; i++)
1279 execv(CCargv[0], CCargv);
1280 syslog(L_FATAL, "%s cant execv %s %m", LogName, CCargv[0]);
1283 return "1 Exit failed";
1287 ** Reject remote readers.
1290 CCreject(char *av[])
1293 return "1 Already rejecting";
1294 if (strlen(av[0]) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
1296 RejectReason = xstrdup(av[0]);
1302 ** Re-read all in-core data.
1305 CCreload(char *av[])
1307 static char BADSCHEMA[] = "1 Can't read schema";
1308 #if defined(DO_PERL)
1309 static char BADPERLRELOAD[] = "1 Failed to define filter_art" ;
1310 #endif /* defined(DO_PERL) */
1311 #if defined(DO_PYTHON)
1312 static char BADPYRELOAD[] = "1 Failed to reload filter_innd.py" ;
1313 #endif /* defined(DO_PYTHON) */
1318 if (*p == '\0' || strcmp(p, "all") == 0) {
1319 SITEflushall(false);
1320 if (Mode == OMrunning)
1323 if (Mode == OMrunning)
1327 if (!ARTreadschema())
1331 #endif /* defined(DO_TCL) */
1332 #if defined(DO_PERL)
1333 path = concatpath(innconf->pathfilter, _PATH_PERL_FILTER_INND);
1334 PERLreadfilter(path, "filter_art") ;
1336 #endif /* defined(DO_PERL) */
1337 #if defined(DO_PYTHON)
1338 syslog(L_NOTICE, "reloading pyfilter");
1340 syslog(L_NOTICE, "reloaded pyfilter OK");
1341 #endif /* DO_PYTHON */
1344 else if (strcmp(p, "active") == 0 || strcmp(p, "newsfeeds") == 0) {
1345 SITEflushall(false);
1349 else if (strcmp(p, "history") == 0) {
1350 if (Mode != OMrunning)
1351 return CCnotrunning;
1355 else if (strcmp(p, "incoming.conf") == 0)
1357 else if (strcmp(p, "overview.fmt") == 0) {
1358 if (!ARTreadschema())
1361 #if 0 /* we should check almost all innconf parameter, but the code
1362 is still incomplete for innd, so just commented out */
1363 else if (strcmp(p, "inn.conf") == 0) {
1364 struct innconf *saved;
1368 if (innconf_read(NULL))
1369 innconf_free(saved);
1372 return "1 Reload of inn.conf failed";
1374 if (innconf->pathhost == NULL) {
1375 syslog(L_FATAL, "%s No pathhost set", LogName);
1379 Path.Used = strlen(innconf->pathhost) + 1;
1380 Path.Data = xmalloc(Path.Used + 1);
1381 sprintf(Path.Data, "%s!", innconf->pathhost);
1382 if (Pathalias.Used > 0)
1383 free(Pathalias.Data);
1384 if (innconf->pathalias == NULL) {
1386 Pathalias.Data = NULL;
1388 Pathalias.Used = strlen(innconf->pathalias) + 1;
1389 Pathalias.Data = xmalloc(Pathalias.Used + 1);
1390 sprintf(Pathalias.Data, "%s!", innconf->pathalias);
1392 if (Pathcluster.Used > 0)
1393 free(Pathcluster.Data);
1394 if (innconf->pathcluster == NULL) {
1395 Pathcluster.Used = 0;
1396 Pathcluster.Data = NULL;
1398 Pathcluster.Used = strlen(innconf->pathcluster) + 1;
1399 Pathcluster.Data = xmalloc(Pathcluster.Used + 1);
1400 sprintf(Pathcluster.Data, "%s!", innconf->pathcluster);
1405 else if (strcmp(p, "filter.tcl") == 0) {
1408 #endif /* defined(DO_TCL) */
1409 #if defined(DO_PERL)
1410 else if (strcmp(p, "filter.perl") == 0) {
1411 path = concatpath(innconf->pathfilter, _PATH_PERL_FILTER_INND);
1412 if (!PERLreadfilter(path, "filter_art"))
1413 return BADPERLRELOAD;
1415 #endif /* defined(DO_PERL) */
1416 #if defined(DO_PYTHON)
1417 else if (strcmp(p, "filter.python") == 0) {
1418 if (!PYreadfilter())
1421 #endif /* defined(DO_PYTHON) */
1423 return "1 Unknown reload type";
1425 syslog(L_NOTICE, "%s reload %s %s", LogName, p, av[1]);
1431 ** Renumber the active file.
1434 CCrenumber(char *av[])
1436 static char CANTRENUMBER[] = "1 Failed (see syslog)";
1440 if (Mode != OMrunning)
1441 return CCnotrunning;
1443 return "1 Must first reload newsfeeds";
1446 if ((ngp = NGfind(p)) == NULL)
1448 if (!NGrenumber(ngp))
1449 return CANTRENUMBER;
1451 else if (!ICDrenumberactive())
1452 return CANTRENUMBER;
1461 CCreserve(char *av[])
1465 if (Mode != OMrunning)
1466 return CCnotrunning;
1469 /* Trying to make a reservation. */
1471 return "1 Already reserved";
1472 if (strlen(p) > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe */
1474 Reservation = xstrdup(p);
1477 /* Trying to remove a reservation. */
1478 if (Reservation == NULL)
1479 return "1 Not reserved";
1488 ** Remove a newsgroup.
1491 CCrmgroup(char *av[])
1495 if ((ngp = NGfind(av[0])) == NULL)
1498 if (Mode == OMthrottled && ThrottledbyIOError)
1499 return "1 server throttled";
1501 /* Update the in-core data. */
1502 if (!ICDrmgroup(ngp))
1504 syslog(L_NOTICE, "%s rmgroup %s", LogName, av[0]);
1510 ** Send a command line to an exploder.
1517 if ((sp = SITEfind(av[0])) == NULL)
1519 if (sp->Type != FTexploder)
1521 SITEwrite(sp, av[1]);
1527 ** Shut down the system.
1530 CCshutdown(char *av[])
1532 syslog(L_NOTICE, "%s shutdown %s", LogName, av[0]);
1533 CleanupAndExit(0, av[0]);
1535 return "1 Exit failed";
1540 ** Send a signal to a site's feed.
1543 CCsignal(char *av[])
1550 /* Parse the signal. */
1554 if (strcasecmp(p, "HUP") == 0)
1556 else if (strcasecmp(p, "INT") == 0)
1558 else if (strcasecmp(p, "TERM") == 0)
1560 else if ((s = atoi(p)) <= 0)
1561 return "1 Invalid signal";
1563 /* Parse the site. */
1565 if ((sp = SITEfind(p)) == NULL)
1567 if (sp->Type != FTchannel && sp->Type != FTexploder)
1569 if (sp->Process < 0)
1570 return "1 Site has no process";
1573 if (kill(sp->pid, s) < 0) {
1575 syslog(L_ERROR, "%s cant kill %ld %d site %s, %m", LogName,
1576 (long) sp->pid, s, p);
1577 snprintf(CCreply.data, CCreply.size, "1 Can't signal process %ld, %s",
1578 (long) sp->pid, strerror(oerrno));
1579 return CCreply.data;
1587 ** Enter throttled mode.
1590 CCthrottle(char *av[])
1597 if (*p && strcmp(p, ModeReason) != 0)
1598 return "1 Already paused";
1601 return CCblock(OMthrottled, p);
1603 return "1 Already throttled";
1605 return "1 unknown mode";
1609 ** Turn on or off performance monitoring
1617 if (strcmp(av[0], "off") == 0)
1620 for (p = av[0]; *p; p++) {
1621 if (!CTYPE(isdigit, *p))
1622 return "1 parameter should be a number or 'off'";
1624 value = atoi(av[0]);
1626 innconf->timer = value;
1635 ** Log into filename some history stats
1638 CCstathist(char *av[])
1640 if (strcmp(av[0], "off") == 0)
1648 ** Turn innd status creation on or off
1651 CCstatus(char *av[])
1656 if (strcmp(av[0], "off") == 0)
1659 for (p = av[0]; *p; p++) {
1660 if (!CTYPE(isdigit, *p))
1661 return "1 parameter should be a number or 'off'";
1663 value = atoi(av[0]);
1665 innconf->status = value;
1670 ** Add or remove tracing.
1680 /* Parse the flag. */
1683 default: return "1 Bad trace flag";
1684 case 'y': case 'Y': Flag = true; word = "on"; break;
1685 case 'n': case 'N': Flag = false; word = "off"; break;
1688 /* Parse what's being traced. */
1692 return "1 Bad trace item";
1695 syslog(L_NOTICE, "%s trace innd %s", LogName, word);
1699 syslog(L_NOTICE, "%s trace nnrpd %s", LogName, word);
1701 case '0': case '1': case '2': case '3': case '4':
1702 case '5': case '6': case '7': case '8': case '9':
1703 if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
1705 CHANtracing(cp, Flag);
1714 ** Split up the text into fields and stuff them in argv. Return the
1715 ** number of elements or -1 on error.
1718 CCargsplit(char *p, char *end, char **argv, int size)
1722 for (save = argv, *argv++ = p, size--; p < end; p++)
1735 ** Read function. Read and process the message.
1738 CCreader(CHANNEL *cp)
1740 static char TOOLONG[] = "0 Reply too long for server to send";
1744 ICC_MSGLENTYPE bufflen;
1745 ICC_PROTOCOLTYPE protocol ;
1746 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1747 struct sockaddr_un client;
1750 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
1752 char buff[BIG_BUFFER + 2];
1753 char copy[BIG_BUFFER + 2];
1754 char *argv[SC_MAXFIELDS + 2];
1760 syslog(L_ERROR, "%s internal CCreader wrong channel 0x%p not 0x%p",
1761 LogName, (void *)cp, (void *)CCchan);
1765 #if defined (HAVE_UNIX_DOMAIN_SOCKETS)
1767 i = RECVorREAD(CCchan->fd, buff, BIG_BUFFER) ;
1769 syslog(L_ERROR, "%s cant recv CCreader %m", LogName);
1771 } else if (i == 0) {
1772 syslog(L_ERROR, "%s cant recv CCreader empty", LogName);
1774 } else if (i < (int)HEADER_SIZE) {
1775 syslog(L_ERROR, "%s cant recv CCreader header-length %m", LogName);
1779 memcpy (&protocol,buff,sizeof (protocol)) ;
1780 memcpy (&bufflen,buff + sizeof (protocol),sizeof (bufflen)) ;
1781 bufflen = ntohs (bufflen) ;
1784 syslog(L_ERROR, "%s cant recv CCreader short-read %m", LogName);
1789 memmove (buff,buff + HEADER_SIZE,i) ;
1792 if (protocol != ICC_PROTOCOL_1) {
1793 syslog(L_ERROR, "%s CCreader protocol mismatch", LogName) ;
1797 #else /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
1799 i = RECVorREAD(CCchan->fd, buff, HEADER_SIZE) ;
1801 syslog(L_ERROR, "%s cant read CCreader header %m", LogName);
1803 } else if (i == 0) {
1804 syslog(L_ERROR, "%s cant read CCreader header empty", LogName);
1806 } else if (i != HEADER_SIZE) {
1807 syslog(L_ERROR, "%s cant read CCreader header-length %m", LogName);
1811 memcpy (&protocol,buff,sizeof (protocol)) ;
1812 memcpy (&bufflen,buff + sizeof (protocol),sizeof (bufflen)) ;
1813 bufflen = ntohs (bufflen);
1814 if (bufflen < HEADER_SIZE || bufflen > BIG_BUFFER) {
1815 syslog(L_ERROR, "%s cant read CCreader bad length", LogName);
1818 bufflen -= HEADER_SIZE ;
1820 i = RECVorREAD(CCchan->fd, buff, bufflen) ;
1823 syslog(L_ERROR, "%s cant read CCreader buffer %m", LogName);
1825 } else if (i == 0) {
1826 syslog(L_ERROR, "%s cant read CCreader buffer empty", LogName);
1828 } else if (i != bufflen) {
1829 syslog(L_ERROR, "%s cant read CCreader buffer-length %m", LogName);
1835 if (protocol != ICC_PROTOCOL_1) {
1836 syslog(L_ERROR, "%s CCreader protocol mismatch", LogName) ;
1840 #endif /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
1842 /* Copy to a printable buffer, and log. */
1844 for (p = NULL, q = copy; *q; q++)
1850 syslog(L_CC_CMD, "%s", p ? p : copy);
1852 /* Split up the fields, get the command letter. */
1853 if ((argc = CCargsplit(buff, &buff[i], argv, ARRAY_SIZE(argv))) < 2
1854 || argc == ARRAY_SIZE(argv)) {
1855 syslog(L_ERROR, "%s bad_fields CCreader", LogName);
1861 /* Dispatch to the command function. */
1862 for (argc -= 2, dp = CCcommands; dp < ARRAY_END(CCcommands); dp++)
1863 if (i == dp->Name) {
1864 if (argc != dp->argc)
1865 p = "1 Wrong number of parameters";
1867 p = (*dp->Function)(&argv[2]);
1870 if (dp == ARRAY_END(CCcommands)) {
1871 syslog(L_NOTICE, "%s bad_message %c", LogName, i);
1872 p = "1 Bad command";
1877 /* Build the reply address and send the reply. */
1878 len = strlen(p) + HEADER_SIZE ;
1879 tbuff = xmalloc(len + 1);
1881 protocol = ICC_PROTOCOL_1 ;
1882 memcpy (tbuff,&protocol,sizeof (protocol)) ;
1883 tbuff += sizeof (protocol) ;
1885 bufflen = htons (len) ;
1886 memcpy (tbuff,&bufflen,sizeof (bufflen)) ;
1887 tbuff += sizeof (bufflen) ;
1890 tbuff -= HEADER_SIZE ;
1892 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1893 memset(&client, 0, sizeof client);
1894 client.sun_family = AF_UNIX;
1895 strcpy(client.sun_path, argv[0]);
1896 if (sendto(CCwriter, tbuff, len, 0,
1897 (struct sockaddr *) &client, SUN_LEN(&client)) < 0) {
1899 syslog(i == ENOENT ? L_NOTICE : L_ERROR,
1900 "%s cant sendto CCreader bytes %d %m", LogName, len);
1902 sendto(CCwriter, TOOLONG, strlen(TOOLONG), 0,
1903 (struct sockaddr *) &client, SUN_LEN(&client));
1906 if ((i = open(argv[0], O_WRONLY | O_NDELAY)) < 0)
1907 syslog(L_ERROR, "%s cant open %s %m", LogName, argv[0]);
1909 if ((written = write(i, tbuff, len)) != len)
1911 syslog(L_ERROR, "%s cant write %s %m", LogName, argv[0]);
1913 syslog(L_ERROR, "%s cant write %s", LogName, argv[0]);
1915 syslog(L_ERROR, "%s cant close %s %m", LogName, argv[0]);
1917 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
1923 ** Called when a write-in-progress is done on the channel. Shouldn't happen.
1926 CCwritedone(CHANNEL *unused)
1928 unused = unused; /* ARGSUSED */
1929 syslog(L_ERROR, "%s internal CCwritedone", LogName);
1934 ** Create the channel.
1940 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1941 struct sockaddr_un server;
1943 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
1946 CCpath = concatpath(innconf->pathrun, _PATH_NEWSCONTROL);
1947 /* Remove old detritus. */
1948 if (unlink(CCpath) < 0 && errno != ENOENT) {
1949 syslog(L_FATAL, "%s cant unlink %s %m", LogName, CCpath);
1953 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1954 /* Create a socket and name it. */
1955 if ((i = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
1956 syslog(L_FATAL, "%s cant socket %s %m", LogName, CCpath);
1959 memset(&server, 0, sizeof server);
1960 server.sun_family = AF_UNIX;
1961 strcpy(server.sun_path, CCpath);
1962 if (bind(i, (struct sockaddr *) &server, SUN_LEN(&server)) < 0) {
1963 syslog(L_FATAL, "%s cant bind %s %m", LogName, CCpath);
1967 /* Create an unbound socket to reply on. */
1968 if ((CCwriter = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
1969 syslog(L_FATAL, "%s cant socket unbound %m", LogName);
1973 /* Increase the buffer size for the Unix domain socket */
1974 if (setsockopt(CCwriter, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
1975 syslog(L_ERROR, "%s cant setsockopt %m", LogName);
1977 /* Create a named pipe and open it. */
1978 if (mkfifo(CCpath, 0666) < 0) {
1979 syslog(L_FATAL, "%s cant mkfifo %s %m", LogName, CCpath);
1982 if ((i = open(CCpath, O_RDWR)) < 0) {
1983 syslog(L_FATAL, "%s cant open %s %m", LogName, CCpath);
1986 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
1988 CCchan = CHANcreate(i, CTcontrol, CSwaiting, CCreader, CCwritedone);
1989 syslog(L_NOTICE, "%s ccsetup %s", LogName, CHANname(CCchan));
1992 buffer_resize(&CCreply, SMBUF);
1995 * Catch SIGUSR1 so that we can recreate the control channel when
1996 * needed (i.e. something has deleted our named socket.
1998 #if defined(SIGUSR1)
1999 xsignal(SIGUSR1, CCresetup);
2000 #endif /* defined(SIGUSR1) */
2005 ** Cleanly shut down the channel.
2010 CHANclose(CCchan, CHANname(CCchan));
2012 if (unlink(CCpath) < 0)
2013 syslog(L_ERROR, "%s cant unlink %s %m", LogName, CCpath);
2017 CCreply.data = NULL;
2021 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
2022 if (close(CCwriter) < 0)
2023 syslog(L_ERROR, "%s cant close unbound %m", LogName);
2024 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
2029 ** Restablish the control channel.
2032 CCresetup(int unused)
2034 #ifndef HAVE_SIGACTION
2035 xsignal(s, CCresetup);
2037 unused = unused; /* ARGSUSED */
2045 * Read a file containing lines of the form "newsgroup lowmark",
2046 * and reset the low article number for the specified groups.
2049 CClowmark(char *av[])
2053 const char *ret = NULL;
2057 if (Mode != OMrunning)
2058 return CCnotrunning;
2060 return "1 Must first reload newsfeeds";
2061 if ((qp = QIOopen(av[0])) == NULL) {
2062 syslog(L_ERROR, "%s cant open %s %m", LogName, av[0]);
2063 return "1 Cannot read input file";
2065 while ((line = QIOread(qp)) != NULL) {
2068 if (QIOtoolong(qp)) {
2069 ret = "1 Malformed input line (too long)";
2072 while (ISWHITE(*line))
2074 for (cp = line; *cp && !ISWHITE(*cp); cp++)
2077 ret = "1 Malformed input line (only one field)";
2081 while (ISWHITE(*cp))
2083 if (strspn(cp, "0123456789") != strlen(cp)) {
2084 ret = "1 Malformed input line (non-digit in low mark)";
2087 if ((lo = atol(cp)) == 0 && (cp[0] != '0' || cp[1] != '\0')) {
2088 ret = "1 Malformed input line (bad low mark)";
2091 if ((ngp = NGfind(line)) == NULL) {
2092 /* ret = CCnogroup; break; */
2095 if (!NGlowmark(ngp, lo)) {
2096 ret = "1 Cannot set low mark - see syslog";
2100 if (ret == NULL && QIOerror(qp)) {
2101 syslog(L_ERROR, "%s cant read %s %m", LogName, av[0]);
2102 ret = "1 Error reading input file";