1 /*
2 **  Routines for the control channel.
3 **
4 **  Create a Unix-domain datagram socket that processes on the local server
5 **  send messages to.  The control channel is used only by ctlinnd to tell
6 **  the server to perform special functions.  We use datagrams so that we
7 **  don't need to do an accept() and tie up another descriptor.  recvfrom
8 **  seems to be broken on several systems, so the client passes in the
9 **  socket name.
10 **
11 **  This module completely rips away all pretense of software layering.
12 */
13 
14 #include "portable/system.h"
15 
16 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
17 #    include "portable/socket-unix.h"
18 #endif
19 
20 #include "inn/fdflag.h"
21 #include "inn/innconf.h"
22 #include "inn/inndcomm.h"
23 #include "inn/qio.h"
24 #include "innd.h"
25 #include "innperl.h"
26 
27 /*
28 **  An entry in the dispatch table.  The name, and implementing function,
29 **  of every command we support.
30 */
31 typedef struct _CCDISPATCH {
32     char Name;
33     int argc;
34     const char *(*Function)(char *av[]);
35 } CCDISPATCH;
36 
37 
38 static const char *CCallow(char *av[]);
39 static const char *CCbegin(char *av[]);
40 static const char *CCchgroup(char *av[]);
41 static const char *CCdrop(char *av[]);
42 static const char *CCfeedinfo(char *av[]);
43 static const char *CCflush(char *av[]);
44 static const char *CCflushlogs(char *unused[]);
45 static const char *CCgo(char *av[]);
46 static const char *CChangup(char *av[]);
47 static const char *CCreserve(char *av[]);
48 static const char *CClogmode(char *unused[]);
49 static const char *CCmode(char *unused[]);
50 static const char *CCname(char *av[]);
51 static const char *CCnewgroup(char *av[]);
52 static const char *CCparam(char *av[]);
53 static const char *CCpause(char *av[]);
54 static const char *CCreaders(char *av[]);
55 static const char *CCreject(char *av[]);
56 static const char *CCreload(char *av[]);
57 static const char *CCrenumber(char *av[]);
58 static const char *CCrmgroup(char *av[]);
59 static const char *CCsend(char *av[]);
60 static const char *CCshutdown(char *av[]);
61 static const char *CCsignal(char *av[]);
62 static const char *CCstathist(char *av[]);
63 static const char *CCstatus(char *av[]);
64 static const char *CCthrottle(char *av[]);
65 static const char *CCtimer(char *av[]);
66 static const char *CCtrace(char *av[]);
67 static const char *CCxabort(char *av[]);
68 static const char *CCxexec(char *av[]);
69 static const char *CCperl(char *av[]);
70 static const char *CCpython(char *av[]);
71 static const char *CClowmark(char *av[]);
72 
73 
74 static char *CCpath = NULL;
75 static char **CCargv;
76 static char CCnosite[] = "1 No such site";
77 static char CCwrongtype[] = "1 Wrong site type";
78 static char CCnogroup[] = "1 No such group";
79 static char CCnochannel[] = "1 No such channel";
80 static char CCnoreason[] = "1 Empty reason";
81 static char CCbigreason[] = "1 Reason too long";
82 static char CCnotrunning[] = "1 Must be running";
83 static struct buffer CCreply;
84 static CHANNEL *CCchan;
85 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
86 static int CCwriter;
87 #endif
88 /* clang-format off */
89 static CCDISPATCH CCcommands[] = {{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_PERL,        1, CCperl},
98                                   {SC_PYTHON,      1, CCpython},
99                                   {SC_FLUSH,       1, CCflush},
100                                   {SC_FLUSHLOGS,   0, CCflushlogs},
101                                   {SC_GO,          1, CCgo},
102                                   {SC_HANGUP,      1, CChangup},
103                                   {SC_LOGMODE,     0, CClogmode},
104                                   {SC_MODE,        0, CCmode},
105                                   {SC_NAME,        1, CCname},
106                                   {SC_NEWGROUP,    3, CCnewgroup},
107                                   {SC_PARAM,       2, CCparam},
108                                   {SC_PAUSE,       1, CCpause},
109                                   {SC_READERS,     2, CCreaders},
110                                   {SC_REJECT,      1, CCreject},
111                                   {SC_RENUMBER,    1, CCrenumber},
112                                   {SC_RELOAD,      2, CCreload},
113                                   {SC_RESERVE,     1, CCreserve},
114                                   {SC_RMGROUP,     1, CCrmgroup},
115                                   {SC_SEND,        2, CCsend},
116                                   {SC_SHUTDOWN,    1, CCshutdown},
117                                   {SC_SIGNAL,      2, CCsignal},
118                                   {SC_STATHIST,    1, CCstathist},
119                                   {SC_STATUS,      1, CCstatus},
120                                   {SC_THROTTLE,    1, CCthrottle},
121                                   {SC_TIMER,       1, CCtimer},
122                                   {SC_TRACE,       2, CCtrace},
123                                   {SC_XABORT,      1, CCxabort},
124                                   {SC_LOWMARK,     1, CClowmark},
125                                   {SC_XEXEC,       1, CCxexec}};
126 /* clang-format on */
127 
128 static void CCresetup(int s);
129 
130 
131 void
CCcopyargv(char * av[])132 CCcopyargv(char *av[])
133 {
134     char **v;
135     int i;
136 
137     /* Get the vector size. */
138     for (i = 0; av[i]; i++)
139         continue;
140 
141     /* Get the vector, copy each element. */
142     for (v = CCargv = xmalloc((i + 1) * sizeof(char *)); *av; av++) {
143         /* not to renumber */
144         if (strncmp(*av, "-r", 2) == 0)
145             continue;
146         *v++ = xstrdup(*av);
147     }
148     *v = NULL;
149 }
150 
151 
152 /*
153 **  Return a string representing our operating mode.
154 */
155 static const char *
CCcurrmode(void)156 CCcurrmode(void)
157 {
158     static char buff[32];
159 
160     /* Server's mode. */
161     switch (Mode) {
162     default:
163         snprintf(buff, sizeof(buff), "Unknown %d", Mode);
164         return buff;
165     case OMrunning:
166         return "running";
167     case OMpaused:
168         return "paused";
169     case OMthrottled:
170         return "throttled";
171     }
172 }
173 
174 
175 /*
176 **  Notify systemd of the current operating modes.
177 */
178 static void
CCsdnotify(void)179 CCsdnotify(void)
180 {
181 #ifdef HAVE_SD_NOTIFY
182     int status;
183 
184     buffer_sprintf(&CCreply, "STATUS=Server ");
185 
186     /* Server's mode. */
187     switch (Mode) {
188     default:
189         buffer_append_sprintf(&CCreply, "Unknown %d", Mode);
190         break;
191     case OMrunning:
192         buffer_append_sprintf(&CCreply, "running");
193         break;
194     case OMpaused:
195         buffer_append_sprintf(&CCreply, "paused %s", ModeReason);
196         break;
197     case OMthrottled:
198         buffer_append_sprintf(&CCreply, "throttled %s", ModeReason);
199         break;
200     }
201     if (RejectReason)
202         buffer_append_sprintf(&CCreply, ". Rejecting %s", RejectReason);
203 
204     /* Newsreaders. */
205     buffer_append_sprintf(&CCreply, ". Readers ");
206     if (innconf->readerswhenstopped)
207         buffer_append_sprintf(&CCreply, "independent ");
208     else
209         buffer_append_sprintf(&CCreply, "follow ");
210     if (NNRPReason == NULL)
211         buffer_append_sprintf(&CCreply, "enabled.");
212     else
213         buffer_append_sprintf(&CCreply, "disabled %s.", NNRPReason);
214 
215     buffer_append(&CCreply, "", 1);
216     status = sd_notify(false, CCreply.data);
217     if (status < 0)
218         warn("cannot notify systemd of new status: %s", strerror(-status));
219 #endif
220 }
221 
222 
223 /*
224 **  Add <> around Message-ID if needed.
225 */
226 static const char *
CCgetid(char * p,const char ** store)227 CCgetid(char *p, const char **store)
228 {
229     static char NULLMESGID[] = "1 Empty Message-ID";
230     static struct buffer Save = {0, 0, 0, NULL};
231     int i;
232 
233     if (*p == '\0')
234         return NULLMESGID;
235     if (*p == '<') {
236         if (p[1] == '\0' || p[1] == '>')
237             return NULLMESGID;
238         *store = p;
239         return NULL;
240     }
241 
242     /* Make sure the Message-ID buffer has room. */
243     i = 1 + strlen(p) + 1 + 1;
244     buffer_resize(&Save, i);
245     *store = Save.data;
246     snprintf(Save.data, Save.size, "<%s>", p);
247     return NULL;
248 }
249 
250 
251 /*
252 **  Abort and dump core.
253 */
254 static const char *
CCxabort(char * av[])255 CCxabort(char *av[])
256 {
257     syslog(L_FATAL, "%s abort %s", LogName, av[0]);
258     abort();
259     /* NOTREACHED */
260     syslog(L_FATAL, "%s cant abort %m", LogName);
261     CleanupAndExit(1, av[0]);
262     return "1 Abort failed";
263 }
264 
265 
266 /*
267 **  Do the work needed to add a history entry.
268 */
269 const char *
CCaddhist(char * av[])270 CCaddhist(char *av[])
271 {
272     static char DIGITS[] = "0123456789";
273     ARTDATA Data;
274     const char *p, *msgid;
275     bool ok;
276     TOKEN token;
277 
278     /* You must pass a <message-id> ID, the history API will hash it as it
279      * wants */
280     if ((p = CCgetid(av[0], &msgid)) != NULL)
281         return p;
282 
283     /* If paused, don't try to use the history database since expire may be
284        running */
285     if (Mode == OMpaused)
286         return "1 Server paused";
287 
288     /* If throttled by admin, briefly open the history database. */
289     if (Mode != OMrunning) {
290         if (ThrottledbyIOError)
291             return "1 Server throttled";
292         InndHisOpen();
293     }
294 
295     if (HIScheck(History, msgid)) {
296         if (Mode != OMrunning)
297             InndHisClose();
298         return "1 Duplicate";
299     }
300     if (Mode != OMrunning)
301         InndHisClose();
302     if (strspn(av[1], DIGITS) != strlen(av[1]))
303         return "1 Bad arrival date";
304     Data.Arrived = atol(av[1]);
305     if (strspn(av[2], DIGITS) != strlen(av[2]))
306         return "1 Bad expiration date";
307     Data.Expires = atol(av[2]);
308     if (strspn(av[3], DIGITS) != strlen(av[3]))
309         return "1 Bad posted date";
310     Data.Posted = atol(av[3]);
311 
312     /* Allow empty tokens, but not badly formatted tokens. */
313     if (*av[4] != '\0' && !IsToken(av[4]))
314         return "1 Bad token";
315     token = TextToToken(av[4]);
316     if (Mode == OMrunning)
317         ok = InndHisWrite(msgid, Data.Arrived, Data.Posted, Data.Expires,
318                           &token);
319     else {
320         /* Possible race condition, but documented in ctlinnd man page. */
321         InndHisOpen();
322         ok = InndHisWrite(msgid, Data.Arrived, Data.Posted, Data.Expires,
323                           &token);
324         InndHisClose();
325     }
326     return ok ? NULL : "1 Write failed";
327 }
328 
329 
330 /*
331 **  Do the work to allow foreign connections.
332 */
333 static const char *
CCallow(char * av[])334 CCallow(char *av[])
335 {
336     char *p;
337 
338     if (RejectReason == NULL)
339         return "1 Already allowed";
340     p = av[0];
341     if (*p && strcmp(p, RejectReason) != 0)
342         return "1 Wrong reason";
343     free(RejectReason);
344     RejectReason = NULL;
345     CCsdnotify();
346     return NULL;
347 }
348 
349 
350 /*
351 **  Do the work needed to start feeding a (new) site.
352 */
353 static const char *
CCbegin(char * av[])354 CCbegin(char *av[])
355 {
356     SITE *sp;
357     int i;
358     int length;
359     char *p;
360     const char *p1;
361     char **strings;
362     NEWSGROUP *ngp;
363     const char *error;
364     char *subbed;
365     char *poison;
366 
367     /* If site already exists, drop it. */
368     if (SITEfind(av[0]) != NULL && (p1 = CCdrop(av)) != NULL)
369         return p1;
370 
371     /* Find the named site. */
372     length = strlen(av[0]);
373     for (strings = SITEreadfile(true), i = 0; (p = strings[i]) != NULL; i++)
374         if ((p[length] == NF_FIELD_SEP || p[length] == NF_SUBFIELD_SEP)
375             && strncasecmp(p, av[0], length) == 0) {
376             p = xstrdup(p);
377             break;
378         }
379     if (p == NULL)
380         return CCnosite;
381 
382     if (p[0] == 'M' && p[1] == 'E' && p[2] == NF_FIELD_SEP)
383         sp = &ME;
384     else {
385         /* Get space for the new site entry, and space for it in all
386          * the groups. */
387         for (i = nSites, sp = Sites; --i >= 0; sp++)
388             if (sp->Name == NULL)
389                 break;
390         if (i < 0) {
391             nSites++;
392             Sites = xrealloc(Sites, nSites * sizeof(SITE));
393             sp = &Sites[nSites - 1];
394             sp->Next = sp->Prev = NOSITE;
395             for (i = nGroups, ngp = Groups; --i >= 0; ngp++) {
396                 ngp->Sites = xrealloc(ngp->Sites, nSites * sizeof(int));
397                 ngp->Poison = xrealloc(ngp->Poison, nSites * sizeof(int));
398             }
399         }
400     }
401 
402     /* Parse. */
403     subbed = xmalloc(nGroups);
404     poison = xmalloc(nGroups);
405     error = SITEparseone(p, sp, subbed, poison);
406     free(subbed);
407     free(poison);
408     if (error != NULL) {
409         free((void *) p);
410         syslog(L_ERROR, "%s bad_newsfeeds %s", av[0], error);
411         return "1 Parse error";
412     }
413 
414     if (sp != &ME && (!SITEsetup(sp) || !SITEfunnelpatch()))
415         return "1 Startup error";
416     SITEforward(sp, "begin");
417     return NULL;
418 }
419 
420 
421 /*
422 **  Common code to change a group's flags.
423 */
424 static const char *
CCdochange(NEWSGROUP * ngp,char * Rest)425 CCdochange(NEWSGROUP *ngp, char *Rest)
426 {
427     int length;
428     char *p;
429 
430     if (ngp->Rest[0] == Rest[0]) {
431         length = strlen(Rest);
432         if (ngp->Rest[length] == '\n' && strncmp(ngp->Rest, Rest, length) == 0)
433             return "0 Group status unchanged";
434     }
435     if (Mode != OMrunning)
436         return CCnotrunning;
437 
438     p = xstrdup(ngp->Name);
439     if (!ICDchangegroup(ngp, Rest)) {
440         syslog(L_NOTICE, "%s cant change_group %s to %s", LogName, p, Rest);
441         free(p);
442         return "1 Change failed (probably can't write active?)";
443     }
444     syslog(L_NOTICE, "%s change_group %s to %s", LogName, p, Rest);
445     free(p);
446     return NULL;
447 }
448 
449 
450 /*
451 **  Change the mode of a newsgroup.
452 */
453 static const char *
CCchgroup(char * av[])454 CCchgroup(char *av[])
455 {
456     NEWSGROUP *ngp;
457     char *Rest;
458 
459     if ((ngp = NGfind(av[0])) == NULL)
460         return CCnogroup;
461     Rest = av[1];
462     if (Rest[0] != NF_FLAG_ALIAS) {
463         Rest[1] = '\0';
464         if (isupper((unsigned char) Rest[0]))
465             Rest[0] = tolower((unsigned char) Rest[0]);
466     }
467     return CCdochange(ngp, Rest);
468 }
469 
470 
471 /*
472 **  Cancel a message.
473 */
474 const char *
CCcancel(char * av[])475 CCcancel(char *av[])
476 {
477     ARTDATA Data;
478     const char *p, *msgid;
479 
480     Data.Posted = Data.Arrived = Now.tv_sec;
481     Data.Expires = 0;
482     Data.Feedsite = "?";
483     if ((p = CCgetid(av[0], &msgid)) != NULL)
484         return p;
485 
486     Data.HdrContent[HDR__MESSAGE_ID].Value = (char *) msgid;
487     Data.HdrContent[HDR__MESSAGE_ID].Length = strlen(msgid);
488     if (Mode == OMrunning)
489         ARTcancel(&Data, msgid, true);
490     else {
491         /* If paused, don't try to use the history database since expire may be
492            running */
493         if (Mode == OMpaused)
494             return "1 Server paused";
495         if (ThrottledbyIOError)
496             return "1 Server throttled";
497         /* Possible race condition, but documented in ctlinnd man page. */
498         InndHisOpen();
499         ARTcancel(&Data, msgid, true);
500         InndHisClose();
501     }
502     if (innconf->logcancelcomm)
503         syslog(L_NOTICE, "%s cancelled %s", LogName, msgid);
504     return NULL;
505 }
506 
507 
508 /*
509 **  Syntax-check the newsfeeds file.
510 */
511 const char *
CCcheckfile(char * av[]UNUSED)512 CCcheckfile(char *av[] UNUSED)
513 {
514     char **strings;
515     char *p;
516     int i;
517     int errors;
518     const char *error;
519     SITE fake;
520     bool needheaders, needoverview, needpath, needstoredgroup;
521     bool needreplicdata;
522 
523     /* Parse all site entries. */
524     strings = SITEreadfile(false);
525     fake.Buffer.size = 0;
526     fake.Buffer.data = NULL;
527     /* save global variables not to be changed */
528     needheaders = NeedHeaders;
529     needoverview = NeedOverview;
530     needpath = NeedPath;
531     needstoredgroup = NeedStoredGroup;
532     needreplicdata = NeedReplicdata;
533     for (errors = 0, i = 0; (p = strings[i]) != NULL; i++) {
534         if ((error = SITEparseone(p, &fake, (char *) NULL, (char *) NULL))
535             != NULL) {
536             syslog(L_ERROR, "%s bad_newsfeeds %s", MaxLength(p, p), error);
537             errors++;
538         }
539         SITEfree(&fake);
540     }
541     free(strings);
542     /* restore global variables not to be changed */
543     NeedHeaders = needheaders;
544     NeedOverview = needoverview;
545     NeedPath = needpath;
546     NeedStoredGroup = needstoredgroup;
547     NeedReplicdata = needreplicdata;
548 
549     if (errors == 0)
550         return NULL;
551 
552     buffer_sprintf(&CCreply, "1 Found %d error(s) -- see syslog", errors);
553     return CCreply.data;
554 }
555 
556 
557 /*
558 **  Drop a site.
559 */
560 static const char *
CCdrop(char * av[])561 CCdrop(char *av[])
562 {
563     SITE *sp;
564     NEWSGROUP *ngp;
565     int *ip;
566     int idx;
567     int i;
568     int j;
569 
570     if ((sp = SITEfind(av[0])) == NULL)
571         return CCnosite;
572 
573     SITEdrop(sp);
574 
575     /* Loop over all groups, and if the site is in a group, clobber it. */
576     for (idx = sp - Sites, i = nGroups, ngp = Groups; --i >= 0; ngp++) {
577         for (j = ngp->nSites, ip = ngp->Sites; --j >= 0; ip++)
578             if (*ip == idx)
579                 *ip = NOSITE;
580         for (j = ngp->nPoison, ip = ngp->Poison; --j >= 0; ip++)
581             if (*ip == idx)
582                 *ip = NOSITE;
583     }
584 
585     return NULL;
586 }
587 
588 /*
589 **  Return info on the feeds for one, or all, sites
590 */
591 static const char *
CCfeedinfo(char * av[])592 CCfeedinfo(char *av[])
593 {
594     SITE *sp;
595     char *p;
596     int i;
597 
598     buffer_set(&CCreply, "0 ", 2);
599     p = av[0];
600     if (*p != '\0') {
601         if ((sp = SITEfind(p)) == NULL)
602             return "1 No such site";
603 
604         SITEinfo(&CCreply, sp, true);
605         while ((sp = SITEfindnext(p, sp)) != NULL)
606             SITEinfo(&CCreply, sp, true);
607     } else
608         for (i = nSites, sp = Sites; --i >= 0; sp++)
609             if (sp->Name)
610                 SITEinfo(&CCreply, sp, false);
611 
612     buffer_append(&CCreply, "", 1);
613     return CCreply.data;
614 }
615 
616 
617 static const char *
CCperl(char * av[]UNUSED)618 CCperl(char *av[] UNUSED)
619 {
620 #ifdef DO_PERL
621     switch (av[0][0]) {
622     default:
623         return "1 Bad flag";
624     case 'y':
625         if (PerlFilterActive)
626             return "1 Perl filter already enabled";
627         else if (!PerlFilter(true))
628             return "1 Perl filter not defined";
629         break;
630     case 'n':
631         if (!PerlFilterActive)
632             return "1 Perl filter already disabled";
633         PerlFilter(false);
634         break;
635     }
636     return NULL;
637 #else
638     return "1 Perl filtering support not compiled in";
639 #endif
640 }
641 
642 
643 static const char *
CCpython(char * av[]UNUSED)644 CCpython(char *av[] UNUSED)
645 {
646 #ifdef DO_PYTHON
647     return PYcontrol(av);
648 #else
649     return "1 Python filtering support not compiled in";
650 #endif
651 }
652 
653 
654 /*
655 **  Flush all sites or one site.
656 */
657 static const char *
CCflush(char * av[])658 CCflush(char *av[])
659 {
660     SITE *sp;
661     int i;
662     char *p;
663 
664     p = av[0];
665     if (*p == '\0') {
666         ICDwrite();
667         for (sp = Sites, i = nSites; --i >= 0; sp++)
668             SITEflush(sp, true);
669         syslog(L_NOTICE, "%s flush_all", LogName);
670     } else {
671         if ((sp = SITEfind(p)) == NULL)
672             return CCnosite;
673         syslog(L_NOTICE, "%s flush", sp->Name);
674         SITEflush(sp, true);
675     }
676     return NULL;
677 }
678 
679 
680 /*
681 **  Flush the log files as well as exploder and process channels.
682 */
683 static const char *
CCflushlogs(char * unused[]UNUSED)684 CCflushlogs(char *unused[] UNUSED)
685 {
686     SITE *sp;
687     CHANNEL *cp;
688     int i;
689 
690     if (Debug)
691         return "1 In debug mode";
692 
693     ICDwrite();
694     syslog(L_NOTICE, "%s flushlogs %s", LogName, CCcurrmode());
695     ReopenLog(Log);
696     ReopenLog(Errlog);
697     /* Flush exploder and process channels so that they take into account
698      * the new log files (for instance during log rotation). */
699     for (sp = Sites, i = nSites; --i >= 0; sp++) {
700         if (((cp = sp->Channel) != NULL)
701             && ((cp->Type == CTexploder) || (cp->Type == CTprocess))) {
702             SITEflush(sp, true);
703             syslog(L_NOTICE, "%s flush", sp->Name);
704         }
705     }
706     return NULL;
707 }
708 
709 
710 /*
711 **  Leave paused or throttled mode.
712 */
713 static const char *
CCgo(char * av[])714 CCgo(char *av[])
715 {
716     static char YES[] = "y";
717     char *p;
718 
719     p = av[0];
720     if (Reservation && strcmp(p, Reservation) == 0) {
721         free(Reservation);
722         Reservation = NULL;
723     }
724     if (RejectReason && strcmp(p, RejectReason) == 0) {
725         free(RejectReason);
726         RejectReason = NULL;
727     }
728 
729     if (Mode == OMrunning)
730         return "1 Already running";
731     if (*p && strcmp(p, ModeReason) != 0)
732         return "1 Wrong reason";
733 
734 #if defined(DO_PERL)
735     PLmode(Mode, OMrunning, p);
736 #endif /* defined(DO_PERL) */
737 #if defined(DO_PYTHON)
738     PYmode(Mode, OMrunning, p);
739 #endif /* defined(DO_PYTHON) */
740 
741     free(ModeReason);
742     ModeReason = NULL;
743     Mode = OMrunning;
744     ThrottledbyIOError = false;
745 
746     if (NNRPReason != NULL && !innconf->readerswhenstopped) {
747         av[0] = YES;
748         av[1] = p;
749         CCreaders(av);
750     }
751     if (ErrorCount < 0)
752         ErrorCount = IO_ERROR_COUNT;
753     InndHisOpen();
754     syslog(L_NOTICE, "%s running", LogName);
755     CCsdnotify();
756     if (ICDneedsetup)
757         ICDsetup(true);
758     SCHANwakeup(&Mode);
759     return NULL;
760 }
761 
762 
763 /*
764 **  Hangup a channel.
765 */
766 static const char *
CChangup(char * av[])767 CChangup(char *av[])
768 {
769     CHANNEL *cp;
770     int fd;
771     char *p;
772     int i;
773 
774     /* Parse the argument, a channel number. */
775     for (p = av[0], fd = 0; *p; p++) {
776         if (!isdigit((unsigned char) *p))
777             return "1 Bad channel number";
778         fd = fd * 10 + *p - '0';
779     }
780 
781     /* Loop over all channels for the desired one. */
782     for (i = 0; (cp = CHANiter(&i, CTany)) != NULL;)
783         if (cp->fd == fd) {
784             p = CHANname(cp);
785             switch (cp->Type) {
786             default:
787                 snprintf(CCreply.data, CCreply.size, "1 Can't close %s", p);
788                 return CCreply.data;
789             case CTexploder:
790             case CTprocess:
791             case CTfile:
792             case CTnntp:
793             case CTreject:
794                 syslog(L_NOTICE, "%s hangup", p);
795                 CHANclose(cp, p);
796                 return NULL;
797             }
798         }
799     return "1 Not active";
800 }
801 
802 
803 /*
804 **  Read a file containing lines of the form "newsgroup lowmark" and reset
805 **  the low article number for the specified groups.
806 */
807 static const char *
CClowmark(char * av[])808 CClowmark(char *av[])
809 {
810     long lo;
811     char *line, *cp;
812     const char *ret = NULL;
813     QIOSTATE *qp;
814     NEWSGROUP *ngp;
815 
816     if (Mode != OMrunning)
817         return CCnotrunning;
818     if (ICDneedsetup)
819         return "1 Must first reload newsfeeds";
820     if ((qp = QIOopen(av[0])) == NULL) {
821         syslog(L_ERROR, "%s cant open %s %m", LogName, av[0]);
822         return "1 Cannot read input file";
823     }
824     while ((line = QIOread(qp)) != NULL) {
825         if (QIOerror(qp))
826             break;
827         if (QIOtoolong(qp)) {
828             ret = "1 Malformed input line (too long)";
829             break;
830         }
831         while (ISWHITE(*line))
832             line++;
833         for (cp = line; *cp && !ISWHITE(*cp); cp++)
834             ;
835         if (*cp == '\0') {
836             ret = "1 Malformed input line (only one field)";
837             break;
838         }
839         *cp++ = '\0';
840         while (ISWHITE(*cp))
841             cp++;
842         if (strspn(cp, "0123456789") != strlen(cp)) {
843             ret = "1 Malformed input line (non-digit in low mark)";
844             break;
845         }
846         if ((lo = atol(cp)) == 0 && (cp[0] != '0' || cp[1] != '\0')) {
847             ret = "1 Malformed input line (bad low mark)";
848             break;
849         }
850         if ((ngp = NGfind(line)) == NULL) {
851             /* ret = CCnogroup; break; */
852             continue;
853         }
854         if (!NGlowmark(ngp, lo)) {
855             ret = "1 Cannot set low mark - see syslog";
856             break;
857         }
858     }
859     if (ret == NULL && QIOerror(qp)) {
860         syslog(L_ERROR, "%s cant read %s %m", LogName, av[0]);
861         ret = "1 Error reading input file";
862     }
863     QIOclose(qp);
864     ICDwrite();
865     return ret;
866 }
867 
868 
869 /*
870 **  Return our operating mode.
871 */
872 static const char *
CCmode(char * unused[]UNUSED)873 CCmode(char *unused[] UNUSED)
874 {
875     int count, i;
876 
877 #ifdef DO_PERL
878     char *stats;
879 #endif
880 
881     buffer_sprintf(&CCreply, "0 Server ");
882 
883     /* Server's mode. */
884     switch (Mode) {
885     default:
886         buffer_append_sprintf(&CCreply, "Unknown %d\n", Mode);
887         break;
888     case OMrunning:
889         buffer_append_sprintf(&CCreply, "running\n");
890         break;
891     case OMpaused:
892         buffer_append_sprintf(&CCreply, "paused %s\n", ModeReason);
893         break;
894     case OMthrottled:
895         buffer_append_sprintf(&CCreply, "throttled %s\n", ModeReason);
896         break;
897     }
898     if (RejectReason)
899         buffer_append_sprintf(&CCreply, "Rejecting %s\n", RejectReason);
900     else
901         buffer_append_sprintf(&CCreply, "Allowing remote connections\n");
902 
903     /* Server parameters. */
904     for (count = 0, i = 0; CHANiter(&i, CTnntp) != NULL;)
905         count++;
906     buffer_append_sprintf(&CCreply,
907                           "Parameters c %lu i %lu (%d) l %lu o %d"
908                           " t %ld H %d T %d X %ld %s %s\n",
909                           innconf->artcutoff, innconf->maxconnections, count,
910                           innconf->maxartsize, MaxOutgoing,
911                           (long) TimeOut.tv_sec, RemoteLimit, RemoteTotal,
912                           (long) RemoteTimer,
913                           innconf->xrefslave ? "slave" : "normal",
914                           AnyIncoming ? "any" : "specified");
915 
916     /* Reservation. */
917     if (Reservation)
918         buffer_append_sprintf(&CCreply, "Reserved %s\n", Reservation);
919     else
920         buffer_append_sprintf(&CCreply, "Not reserved\n");
921 
922     /* Newsreaders. */
923     buffer_append_sprintf(&CCreply, "Readers ");
924     if (innconf->readerswhenstopped)
925         buffer_append_sprintf(&CCreply, "independent ");
926     else
927         buffer_append_sprintf(&CCreply, "follow ");
928     if (NNRPReason == NULL)
929         buffer_append_sprintf(&CCreply, "enabled");
930     else
931         buffer_append_sprintf(&CCreply, "disabled %s", NNRPReason);
932 
933 #ifdef DO_PERL
934     buffer_append_sprintf(&CCreply, "\nPerl filtering ");
935     if (PerlFilterActive)
936         buffer_append_sprintf(&CCreply, "enabled");
937     else
938         buffer_append_sprintf(&CCreply, "disabled");
939     stats = PLstats();
940     if (stats != NULL) {
941         buffer_append_sprintf(&CCreply, "\nPerl filter stats: %s", stats);
942         free(stats);
943     }
944 #endif
945 
946 #ifdef DO_PYTHON
947     buffer_append_sprintf(&CCreply, "\nPython filtering ");
948     if (PythonFilterActive)
949         buffer_append_sprintf(&CCreply, "enabled");
950     else
951         buffer_append_sprintf(&CCreply, "disabled");
952 #endif
953 
954     buffer_append(&CCreply, "", 1);
955     return CCreply.data;
956 }
957 
958 
959 /*
960 **  Log our operating mode (via syslog).
961 */
962 static const char *
CClogmode(char * unused[]UNUSED)963 CClogmode(char *unused[] UNUSED)
964 {
965     syslog(L_NOTICE, "%s servermode %s", LogName, CCcurrmode());
966     return NULL;
967 }
968 
969 
970 /*
971 **  Name the channels.  ("Name the bats -- simple names.")
972 */
973 static const char *
CCname(char * av[])974 CCname(char *av[])
975 {
976     CHANNEL *cp;
977     int i, count;
978     const char *mode;
979     char *p;
980 
981     if (av[0][0] != '\0') {
982         for (p = av[0]; *p != '\0'; p++) {
983             if (!isdigit((unsigned char) *p)) {
984                 return "1 Bad channel number";
985             }
986         }
987         cp = CHANfromdescriptor(atoi(av[0]));
988         if (cp == NULL || cp->Type == CTfree) {
989             return xstrdup(CCnochannel);
990         }
991         buffer_sprintf(&CCreply, "0 %s", CHANname(cp));
992         return CCreply.data;
993     }
994     buffer_set(&CCreply, "0 ", 2);
995     for (count = 0, i = 0; (cp = CHANiter(&i, CTany)) != NULL;) {
996         if (cp->Type == CTfree)
997             continue;
998         if (++count > 1)
999             buffer_append(&CCreply, "\n", 1);
1000         buffer_append_sprintf(&CCreply, "%s", CHANname(cp));
1001         switch (cp->Type) {
1002         case CTremconn:
1003             buffer_append_sprintf(&CCreply, ":remconn::");
1004             break;
1005         case CTreject:
1006             buffer_append_sprintf(&CCreply, ":reject::");
1007             break;
1008         case CTnntp:
1009             mode = (cp->MaxCnx > 0 && cp->ActiveCnx == 0) ? "paused" : "";
1010             buffer_append_sprintf(&CCreply, ":%s:%ld:%s",
1011                                   cp->State == CScancel ? "cancel" : "nntp",
1012                                   (long) (Now.tv_sec - cp->LastActive), mode);
1013             break;
1014         case CTlocalconn:
1015             buffer_append_sprintf(&CCreply, ":localconn::");
1016             break;
1017         case CTcontrol:
1018             buffer_append_sprintf(&CCreply, ":control::");
1019             break;
1020         case CTfile:
1021             buffer_append_sprintf(&CCreply, "::");
1022             break;
1023         case CTexploder:
1024             buffer_append_sprintf(&CCreply, ":exploder::");
1025             break;
1026         case CTprocess:
1027             buffer_append_sprintf(&CCreply, ":");
1028             break;
1029         default:
1030             buffer_append_sprintf(&CCreply, ":unknown::");
1031             break;
1032         }
1033     }
1034     buffer_append(&CCreply, "", 1);
1035     return CCreply.data;
1036 }
1037 
1038 
1039 /*
1040 **  Create a newsgroup.
1041 */
1042 static const char *
CCnewgroup(char * av[])1043 CCnewgroup(char *av[])
1044 {
1045     static char *TIMES = NULL;
1046     static char WHEN[] = "updating active.times";
1047     int fd;
1048     char *p;
1049     NEWSGROUP *ngp;
1050     char *Name;
1051     char *Rest;
1052     const char *who;
1053     char *buff = NULL;
1054     int oerrno;
1055 
1056     if (TIMES == NULL)
1057         TIMES = concatpath(innconf->pathdb, INN_PATH_ACTIVETIMES);
1058 
1059     Name = av[0];
1060     if (Name[0] == '.' || strspn(Name, "0123456789") == strlen(Name))
1061         return "1 Illegal newsgroup name";
1062     for (p = Name; *p; p++)
1063         if (*p == '.') {
1064             if (p[1] == '.' || p[1] == '\0')
1065                 return "1 Double or trailing period in newsgroup name";
1066         } else if (ISWHITE(*p) || *p == ':' || *p == '!' || *p == '/')
1067             return "1 Illegal character in newsgroup name";
1068 
1069     Rest = av[1];
1070     if (Rest[0] != NF_FLAG_ALIAS) {
1071         Rest[1] = '\0';
1072         if (isupper((unsigned char) Rest[0]))
1073             Rest[0] = tolower((unsigned char) Rest[0]);
1074     }
1075 
1076     who = av[2];
1077     if (*who == '\0')
1078         who = NEWSMASTER;
1079     if (!is_valid_utf8(who))
1080         return "1 Invalid UTF-8 creator's name";
1081 
1082     if (strlen(Name) + strlen(Rest) > SMBUF - 24)
1083         return "1 Name too long";
1084 
1085     if ((ngp = NGfind(Name)) != NULL)
1086         return CCdochange(ngp, Rest);
1087 
1088     if (Mode == OMthrottled && ThrottledbyIOError)
1089         return "1 server throttled";
1090 
1091     /* Update the log of groups created.  Don't use stdio because SunOS
1092      * 4.1 has broken libc which can't handle fd's greater than 127. */
1093     if ((fd = open(TIMES, O_WRONLY | O_APPEND | O_CREAT, 0664)) < 0) {
1094         oerrno = errno;
1095         syslog(L_ERROR, "%s cant open %s %m", LogName, TIMES);
1096         IOError(WHEN, oerrno);
1097     } else {
1098         xasprintf(&buff, "%s %ld %s\n", Name, (long) Now.tv_sec, who);
1099         if (xwrite(fd, buff, strlen(buff)) < 0) {
1100             oerrno = errno;
1101             syslog(L_ERROR, "%s cant write %s %m", LogName, TIMES);
1102             IOError(WHEN, oerrno);
1103         }
1104 
1105         free(buff);
1106 
1107         if (close(fd) < 0) {
1108             oerrno = errno;
1109             syslog(L_ERROR, "%s cant close %s %m", LogName, TIMES);
1110             IOError(WHEN, oerrno);
1111         }
1112     }
1113 
1114     /* Update the in-core data. */
1115     if (!ICDnewgroup(Name, Rest))
1116         return "1 Failed";
1117     syslog(L_NOTICE, "%s newgroup %s as %s", LogName, Name, Rest);
1118 
1119     return NULL;
1120 }
1121 
1122 
1123 /*
1124 **  Parse and set a boolean flag.
1125 */
1126 static bool
CCparsebool(char name,bool * bp,char value)1127 CCparsebool(char name, bool *bp, char value)
1128 {
1129     switch (value) {
1130     default:
1131         return false;
1132     case 'y':
1133         *bp = false;
1134         break;
1135     case 'n':
1136         *bp = true;
1137         break;
1138     }
1139     syslog(L_NOTICE, "%s changed -%c %c", LogName, name, value);
1140     return true;
1141 }
1142 
1143 
1144 /*
1145 **  Change a running parameter.
1146 */
1147 static const char *
CCparam(char * av[])1148 CCparam(char *av[])
1149 {
1150     static char BADVAL[] = "1 Bad value";
1151     char *p;
1152     int temp;
1153 
1154     p = av[1];
1155     switch (av[0][0]) {
1156     default:
1157         return "1 Unknown parameter";
1158     case 'a':
1159         if (!CCparsebool('a', (bool *) &AnyIncoming, *p))
1160             return BADVAL;
1161         break;
1162     case 'c':
1163         innconf->artcutoff = strtoul(p, NULL, 10);
1164         syslog(L_NOTICE, "%s changed -c %lu", LogName, innconf->artcutoff);
1165         break;
1166     case 'i':
1167         innconf->maxconnections = strtoul(p, NULL, 10);
1168         syslog(L_NOTICE, "%s changed -i %lu", LogName,
1169                innconf->maxconnections);
1170         break;
1171     case 'l':
1172         innconf->maxartsize = strtoul(p, NULL, 10);
1173         syslog(L_NOTICE, "%s changed -l %lu", LogName, innconf->maxartsize);
1174         break;
1175     case 'n':
1176         if (!CCparsebool('n', (bool *) &innconf->readerswhenstopped, *p))
1177             return BADVAL;
1178         CCsdnotify();
1179         break;
1180     case 'o':
1181         MaxOutgoing = atoi(p);
1182         syslog(L_NOTICE, "%s changed -o %d", LogName, MaxOutgoing);
1183         break;
1184     case 't':
1185         TimeOut.tv_sec = atol(p);
1186         syslog(L_NOTICE, "%s changed -t %ld", LogName, (long) TimeOut.tv_sec);
1187         break;
1188     case 'H':
1189         RemoteLimit = atoi(p);
1190         syslog(L_NOTICE, "%s changed -H %d", LogName, RemoteLimit);
1191         break;
1192     case 'T':
1193         temp = atoi(p);
1194         if (temp > REMOTETABLESIZE) {
1195             syslog(L_NOTICE, "%s -T must be lower than %d", LogName,
1196                    REMOTETABLESIZE + 1);
1197             temp = REMOTETABLESIZE;
1198         }
1199         syslog(L_NOTICE, "%s changed -T from %d to %d", LogName, RemoteTotal,
1200                temp);
1201         RemoteTotal = temp;
1202         break;
1203     case 'X':
1204         RemoteTimer = (time_t) atoi(p);
1205         syslog(L_NOTICE, "%s changed -X %d", LogName, (int) RemoteTimer);
1206         break;
1207     }
1208     return NULL;
1209 }
1210 
1211 
1212 /*
1213 **  Common code to implement a pause or throttle.
1214 */
1215 const char *
CCblock(OPERATINGMODE NewMode,char * reason)1216 CCblock(OPERATINGMODE NewMode, char *reason)
1217 {
1218     static char NO[] = "n";
1219     char *av[2];
1220 
1221     if (*reason == '\0')
1222         return CCnoreason;
1223 
1224     if (strlen(reason)
1225         > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe. */
1226         return CCbigreason;
1227 
1228     if (!is_valid_utf8(reason))
1229         return "1 Invalid UTF-8 reason";
1230 
1231     if (Reservation) {
1232         if (strcmp(reason, Reservation) != 0) {
1233             snprintf(CCreply.data, CCreply.size, "1 Reserved \"%s\"",
1234                      Reservation);
1235             return CCreply.data;
1236         }
1237         free(Reservation);
1238         Reservation = NULL;
1239     }
1240 
1241 #ifdef DO_PERL
1242     PLmode(Mode, NewMode, reason);
1243 #endif
1244 #ifdef DO_PYTHON
1245     PYmode(Mode, NewMode, reason);
1246 #endif
1247 
1248     ICDwrite();
1249     InndHisClose();
1250     Mode = NewMode;
1251     if (ModeReason)
1252         free(ModeReason);
1253     ModeReason = xstrdup(reason);
1254     /* Disallow readers when the server is paused or throttled. */
1255     if (NNRPReason == NULL && !innconf->readerswhenstopped) {
1256         av[0] = NO;
1257         av[1] = ModeReason;
1258         CCreaders(av);
1259     }
1260     syslog(L_NOTICE, "%s %s %s", LogName,
1261            NewMode == OMpaused ? "paused" : "throttled", reason);
1262     CCsdnotify();
1263     return NULL;
1264 }
1265 
1266 
1267 /*
1268 **  Enter paused mode.
1269 */
1270 static const char *
CCpause(char * av[])1271 CCpause(char *av[])
1272 {
1273     switch (Mode) {
1274     case OMrunning:
1275         return CCblock(OMpaused, av[0]);
1276     case OMpaused:
1277         return "1 Already paused";
1278     case OMthrottled:
1279         return "1 Already throttled";
1280     case OMshutdown:
1281         return "1 Shutting down";
1282     }
1283     return "1 Unknown mode";
1284 }
1285 
1286 
1287 /*
1288 **  Allow or disallow newsreaders.
1289 */
1290 static const char *
CCreaders(char * av[])1291 CCreaders(char *av[])
1292 {
1293     const char *p;
1294 
1295     switch (av[0][0]) {
1296     default:
1297         return "1 Bad flag";
1298     case 'y':
1299         if (NNRPReason == NULL)
1300             return "1 Already allowing readers";
1301         p = av[1];
1302         if (*p && strcmp(p, NNRPReason) != 0)
1303             return "1 Wrong reason";
1304         free(NNRPReason);
1305         NNRPReason = NULL;
1306         break;
1307     case 'n':
1308         if (NNRPReason)
1309             return "1 Already not allowing readers";
1310         p = av[1];
1311         if (*p == '\0')
1312             return CCnoreason;
1313         if (strlen(p)
1314             > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe. */
1315             return CCbigreason;
1316         if (!is_valid_utf8(p))
1317             return "1 Invalid UTF-8 reason";
1318         NNRPReason = xstrdup(p);
1319         break;
1320     }
1321     CCsdnotify();
1322     return NULL;
1323 }
1324 
1325 
1326 /*
1327 **  Re-exec ourselves.
1328 */
1329 static const char *
CCxexec(char * av[])1330 CCxexec(char *av[])
1331 {
1332     char *innd;
1333     char *p;
1334     int i;
1335     const char *s;
1336     int count;
1337     int status;
1338 
1339     if (CCargv == NULL)
1340         return "1 no argv!";
1341 
1342     innd = concatpath(innconf->pathbin, "innd");
1343     /* Get the pathname. */
1344     p = av[0];
1345     if (*p == '\0' || strcmp(p, "innd") == 0)
1346         CCargv[0] = innd;
1347     else
1348         return "1 Bad value";
1349 
1350 #ifdef DO_PERL
1351     PLmode(Mode, OMshutdown, av[0]);
1352 #endif
1353 #ifdef DO_PYTHON
1354     PYmode(Mode, OMshutdown, av[0]);
1355 #endif
1356     JustCleanup();
1357     syslog(L_NOTICE, "%s execv %s", LogName, CCargv[0]);
1358     status = sd_notify(false, "RELOADING=1");
1359     if (status < 0)
1360         warn("cannot notify systemd of reloading: %s", strerror(-status));
1361 
1362     /* Restore the systemd variables which were backed up in RCsetup()
1363        if socket activation was set. */
1364     s = getenv("INN_BACKUP_LISTEN_FDS");
1365     if (s != NULL) {
1366         if (setenv("LISTEN_FDS", s, true) != 0)
1367             die("setenv failed for LISTEN_FDS");
1368         if (unsetenv("INN_BACKUP_LISTEN_FDS") != 0)
1369             die("unsetenv failed for INN_BACKUP_LISTEN_FDS");
1370     }
1371     s = getenv("INN_BACKUP_LISTEN_PID");
1372     if (s != NULL) {
1373         if (setenv("LISTEN_PID", s, true) != 0)
1374             die("setenv failed for LISTEN_PID");
1375         if (unsetenv("INN_BACKUP_LISTEN_PID") != 0)
1376             die("unsetenv failed for INN_BACKUP_LISTEN_PID");
1377     }
1378 
1379     /* Clear the close-on-exec flag on the socket activation file descriptors.
1380        This is needed because we need to pass them to the new innd process
1381        and sd_listen_fds(), which was called in RCsetup(), set it on all
1382        passed file descriptors. */
1383     s = getenv("INN_SD_LISTEN_FDS_COUNT");
1384     if (s == NULL)
1385         count = 0;
1386     else
1387         count = atoi(s);
1388     for (i = SD_LISTEN_FDS_START; i < SD_LISTEN_FDS_START + count; i++)
1389         fdflag_close_exec(i, false);
1390 
1391     /* Close all fds to protect possible fd leaking across successive innds. */
1392     for (i = SD_LISTEN_FDS_START + count; i < 30; i++)
1393         close(i);
1394 
1395     execv(CCargv[0], CCargv);
1396     syslog(L_FATAL, "%s cant execv %s %m", LogName, CCargv[0]);
1397     _exit(1);
1398     /* NOTREACHED */
1399     return "1 Exit failed";
1400 }
1401 
1402 /*
1403 **  Reject remote readers.
1404 */
1405 static const char *
CCreject(char * av[])1406 CCreject(char *av[])
1407 {
1408     if (RejectReason)
1409         return "1 Already rejecting";
1410     if (strlen(av[0])
1411         > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe. */
1412         return CCbigreason;
1413     if (!is_valid_utf8(av[0]))
1414         return "1 Invalid UTF-8 reason";
1415     RejectReason = xstrdup(av[0]);
1416     CCsdnotify();
1417     return NULL;
1418 }
1419 
1420 
1421 /*
1422 **  Re-read all in-core data.
1423 */
1424 static const char *
CCreload(char * av[])1425 CCreload(char *av[])
1426 {
1427     const char *p;
1428     const char *error;
1429 
1430 #ifdef DO_PERL
1431     static char BADPERLRELOAD[] = "1 Failed to define filter_art";
1432     char *path;
1433 #endif
1434 
1435 #ifdef DO_PYTHON
1436     static char BADPYRELOAD[] = "1 Failed to reload filter_innd.py";
1437 #endif
1438 
1439     p = av[0];
1440     if (*p == '\0' || strcmp(p, "all") == 0) {
1441         /* Check the syntax of the newsfeeds file before reloading. */
1442         if ((error = CCcheckfile(NULL)) != NULL)
1443             return error;
1444         SITEflushall(false);
1445         if (Mode == OMrunning)
1446             InndHisClose();
1447         RCreadlist();
1448         if (Mode == OMrunning)
1449             InndHisOpen();
1450         ICDwrite();
1451         ICDsetup(true);
1452 #ifdef DO_PERL
1453         path = concatpath(innconf->pathfilter, INN_PATH_PERL_FILTER_INND);
1454         PERLreadfilter(path, "filter_art");
1455         free(path);
1456 #endif
1457 #ifdef DO_PYTHON
1458         syslog(L_NOTICE, "reloading pyfilter");
1459         if (PYreadfilter())
1460             syslog(L_NOTICE, "reloaded pyfilter OK");
1461 #endif
1462         p = "all";
1463     } else if (strcmp(p, "active") == 0 || strcmp(p, "newsfeeds") == 0) {
1464         /* Check the syntax of the newsfeeds file before reloading. */
1465         if ((error = CCcheckfile(NULL)) != NULL)
1466             return error;
1467         SITEflushall(false);
1468         ICDwrite();
1469         ICDsetup(true);
1470     } else if (strcmp(p, "history") == 0) {
1471         if (Mode != OMrunning)
1472             return CCnotrunning;
1473         InndHisClose();
1474         InndHisOpen();
1475     } else if (strcmp(p, "incoming.conf") == 0) {
1476         RCreadlist();
1477     }
1478 #if 0 /* We should check almost all innconf parameter, but the code \
1479          is still incomplete for innd, so just commented out. */
1480     else if (strcmp(p, "inn.conf") == 0) {
1481         struct innconf *saved;
1482 
1483         saved = innconf;
1484         innconf = NULL;
1485         if (innconf_read(NULL))
1486             innconf_free(saved);
1487         else {
1488             innconf = saved;
1489             return "1 Reload of inn.conf failed";
1490         }
1491         if (innconf->pathhost == NULL) {
1492             syslog(L_FATAL, "%s No pathhost set", LogName);
1493             exit(1);
1494         }
1495         free(Path.data);
1496         Path.used = strlen(innconf->pathhost) + 1;
1497         Path.data = xmalloc(Path.used + 1);
1498         sprintf(Path.data, "%s!", innconf->pathhost);
1499         if (Pathalias.used > 0)
1500             free(Pathalias.data);
1501         if (innconf->pathalias == NULL) {
1502             Pathalias.used = 0;
1503             Pathalias.data = NULL;
1504         } else {
1505             Pathalias.used = strlen(innconf->pathalias) + 1;
1506             Pathalias.data = xmalloc(Pathalias.used + 1);
1507             sprintf(Pathalias.data, "%s!", innconf->pathalias);
1508         }
1509         if (Pathcluster.used > 0)
1510             free(Pathcluster.data);
1511         if (innconf->pathcluster == NULL) {
1512             Pathcluster.used = 0;
1513             Pathcluster.data = NULL;
1514         } else {
1515             Pathcluster.used = strlen(innconf->pathcluster) + 1;
1516             Pathcluster.data = xmalloc(Pathcluster.used + 1);
1517             sprintf(Pathcluster.data, "%s!", innconf->pathcluster);
1518         }
1519     }
1520 #endif
1521 #ifdef DO_PERL
1522     else if (strcmp(p, "filter.perl") == 0) {
1523         path = concatpath(innconf->pathfilter, INN_PATH_PERL_FILTER_INND);
1524         if (!PERLreadfilter(path, "filter_art")) {
1525             free(path);
1526             return BADPERLRELOAD;
1527         }
1528         free(path);
1529     }
1530 #endif
1531 #ifdef DO_PYTHON
1532     else if (strcmp(p, "filter.python") == 0) {
1533         if (!PYreadfilter())
1534             return BADPYRELOAD;
1535     }
1536 #endif
1537     else
1538         return "1 Unknown reload type";
1539 
1540     syslog(L_NOTICE, "%s reload %s %s", LogName, p, av[1]);
1541     return NULL;
1542 }
1543 
1544 
1545 /*
1546 **  Renumber the active file.
1547 */
1548 static const char *
CCrenumber(char * av[])1549 CCrenumber(char *av[])
1550 {
1551     static char CANTRENUMBER[] = "1 Failed (see syslog)";
1552     char *p;
1553     NEWSGROUP *ngp;
1554 
1555     if (Mode != OMrunning)
1556         return CCnotrunning;
1557     if (ICDneedsetup)
1558         return "1 Must first reload newsfeeds";
1559     p = av[0];
1560     if (*p) {
1561         if ((ngp = NGfind(p)) == NULL)
1562             return CCnogroup;
1563         if (!NGrenumber(ngp))
1564             return CANTRENUMBER;
1565     } else if (!ICDrenumberactive())
1566         return CANTRENUMBER;
1567     return NULL;
1568 }
1569 
1570 
1571 /*
1572 **  Reserve a lock.
1573 */
1574 static const char *
CCreserve(char * av[])1575 CCreserve(char *av[])
1576 {
1577     char *p;
1578 
1579     if (Mode != OMrunning)
1580         return CCnotrunning;
1581     p = av[0];
1582     if (*p) {
1583         /* Trying to make a reservation. */
1584         if (Reservation)
1585             return "1 Already reserved";
1586         if (strlen(p)
1587             > MAX_REASON_LEN) /* MAX_REASON_LEN is as big as is safe. */
1588             return CCbigreason;
1589         if (!is_valid_utf8(p))
1590             return "1 Invalid UTF-8 reason";
1591         Reservation = xstrdup(p);
1592     } else {
1593         /* Trying to remove a reservation. */
1594         if (Reservation == NULL)
1595             return "1 Not reserved";
1596         free(Reservation);
1597         Reservation = NULL;
1598     }
1599     return NULL;
1600 }
1601 
1602 
1603 /*
1604 **  Remove a newsgroup.
1605 */
1606 static const char *
CCrmgroup(char * av[])1607 CCrmgroup(char *av[])
1608 {
1609     NEWSGROUP *ngp;
1610 
1611     if ((ngp = NGfind(av[0])) == NULL)
1612         return CCnogroup;
1613 
1614     if (Mode == OMthrottled && ThrottledbyIOError)
1615         return "1 server throttled";
1616 
1617     /* Update the in-core data. */
1618     if (!ICDrmgroup(ngp))
1619         return "1 Failed";
1620     syslog(L_NOTICE, "%s rmgroup %s", LogName, av[0]);
1621     return NULL;
1622 }
1623 
1624 
1625 /*
1626 **  Send a command line to an exploder.
1627 */
1628 static const char *
CCsend(char * av[])1629 CCsend(char *av[])
1630 {
1631     SITE *sp;
1632 
1633     if ((sp = SITEfind(av[0])) == NULL)
1634         return CCnosite;
1635     if (sp->Type != FTexploder)
1636         return CCwrongtype;
1637     SITEwrite(sp, av[1]);
1638     return NULL;
1639 }
1640 
1641 
1642 /*
1643 **  Shut down the system.
1644 */
1645 static const char *
CCshutdown(char * av[])1646 CCshutdown(char *av[])
1647 {
1648 #ifdef DO_PERL
1649     PLmode(Mode, OMshutdown, av[0]);
1650 #endif
1651 #ifdef DO_PYTHON
1652     PYmode(Mode, OMshutdown, av[0]);
1653 #endif
1654     syslog(L_NOTICE, "%s shutdown %s", LogName, av[0]);
1655     CleanupAndExit(0, av[0]);
1656     /* NOTREACHED */
1657     return "1 Exit failed";
1658 }
1659 
1660 
1661 /*
1662 **  Send a signal to a site's feed.
1663 */
1664 static const char *
CCsignal(char * av[])1665 CCsignal(char *av[])
1666 {
1667     SITE *sp;
1668     char *p;
1669     int s;
1670     int oerrno;
1671 
1672     /* Parse the signal. */
1673     p = av[0];
1674     if (*p == '-')
1675         p++;
1676     if (strcasecmp(p, "HUP") == 0)
1677         s = SIGHUP;
1678     else if (strcasecmp(p, "INT") == 0)
1679         s = SIGINT;
1680     else if (strcasecmp(p, "TERM") == 0)
1681         s = SIGTERM;
1682     else if ((s = atoi(p)) <= 0)
1683         return "1 Invalid signal";
1684 
1685     /* Parse the site. */
1686     p = av[1];
1687     if ((sp = SITEfind(p)) == NULL)
1688         return CCnosite;
1689     if (sp->Type != FTchannel && sp->Type != FTexploder)
1690         return CCwrongtype;
1691     if (sp->Process < 0)
1692         return "1 Site has no process";
1693 
1694     /* Do it. */
1695     if (kill(sp->pid, s) < 0) {
1696         oerrno = errno;
1697         syslog(L_ERROR, "%s cant kill %ld %d site %s, %m", LogName,
1698                (long) sp->pid, s, p);
1699         buffer_sprintf(&CCreply, "1 Can't signal process %ld: %s",
1700                        (long) sp->pid, strerror(oerrno));
1701         return CCreply.data;
1702     }
1703 
1704     return NULL;
1705 }
1706 
1707 
1708 /*
1709 **  Enter throttled mode.
1710 */
1711 static const char *
CCthrottle(char * av[])1712 CCthrottle(char *av[])
1713 {
1714     char *p;
1715 
1716     p = av[0];
1717     switch (Mode) {
1718     case OMpaused:
1719         if (*p && strcmp(p, ModeReason) != 0)
1720             return "1 Already paused";
1721         goto fallthroughOMrunning;
1722     case OMrunning:
1723     fallthroughOMrunning:
1724         return CCblock(OMthrottled, p);
1725     case OMthrottled:
1726         return "1 Already throttled";
1727     case OMshutdown:
1728         return "1 Shutting down";
1729     }
1730     return "1 unknown mode";
1731 }
1732 
1733 /*
1734 **  Turn on or off performance monitoring
1735 */
1736 static const char *
CCtimer(char * av[])1737 CCtimer(char *av[])
1738 {
1739     unsigned long value;
1740     char *p;
1741 
1742     if (strcmp(av[0], "off") == 0)
1743         value = 0;
1744     else {
1745         for (p = av[0]; *p; p++) {
1746             if (!isdigit((unsigned char) *p))
1747                 return "1 parameter should be a number or 'off'";
1748         }
1749         value = strtoul(av[0], NULL, 10);
1750     }
1751     innconf->timer = value;
1752     if (innconf->timer != 0)
1753         TMRinit(TMR_MAX);
1754     else
1755         TMRinit(0);
1756     return NULL;
1757 }
1758 
1759 /*
1760 **  Log into filename some history stats
1761 */
1762 static const char *
CCstathist(char * av[])1763 CCstathist(char *av[])
1764 {
1765     if (strcmp(av[0], "off") == 0)
1766         HISlogclose();
1767     else
1768         HISlogto(av[0]);
1769     return NULL;
1770 }
1771 
1772 /*
1773 **  Turn innd status creation on or off
1774 */
1775 static const char *
CCstatus(char * av[])1776 CCstatus(char *av[])
1777 {
1778     unsigned long value;
1779     char *p;
1780 
1781     if (strcmp(av[0], "off") == 0)
1782         value = 0;
1783     else {
1784         for (p = av[0]; *p; p++) {
1785             if (!isdigit((unsigned char) *p))
1786                 return "1 parameter should be a number or 'off'";
1787         }
1788         value = strtoul(av[0], NULL, 10);
1789     }
1790     innconf->status = value;
1791     return NULL;
1792 }
1793 
1794 /*
1795 **  Add or remove tracing.
1796 */
1797 static const char *
CCtrace(char * av[])1798 CCtrace(char *av[])
1799 {
1800     char *p;
1801     bool Flag;
1802     const char *word;
1803     CHANNEL *cp;
1804 
1805     /* Parse the flag. */
1806     p = av[1];
1807     switch (p[0]) {
1808     default:
1809         return "1 Bad trace flag";
1810     case 'y':
1811     case 'Y':
1812         Flag = true;
1813         word = "on";
1814         break;
1815     case 'n':
1816     case 'N':
1817         Flag = false;
1818         word = "off";
1819         break;
1820     }
1821 
1822     /* Parse what's being traced. */
1823     p = av[0];
1824     switch (*p) {
1825     default:
1826         return "1 Bad trace item";
1827     case 'i':
1828     case 'I':
1829         Tracing = Flag;
1830         syslog(L_NOTICE, "%s trace innd %s", LogName, word);
1831         break;
1832     case 'n':
1833     case 'N':
1834         NNRPTracing = Flag;
1835         syslog(L_NOTICE, "%s trace nnrpd %s", LogName, word);
1836         break;
1837     case '0':
1838     case '1':
1839     case '2':
1840     case '3':
1841     case '4':
1842     case '5':
1843     case '6':
1844     case '7':
1845     case '8':
1846     case '9':
1847         if ((cp = CHANfromdescriptor(atoi(p))) == NULL)
1848             return CCnochannel;
1849         CHANtracing(cp, Flag);
1850         break;
1851     }
1852     return NULL;
1853 }
1854 
1855 
1856 /*
1857 **  Split up the text into fields and stuff them in argv.  Return the
1858 **  number of elements or -1 on error.
1859 */
1860 static int
CCargsplit(char * p,char * end,char ** argv,int size)1861 CCargsplit(char *p, char *end, char **argv, int size)
1862 {
1863     char **save;
1864 
1865     for (save = argv, *argv++ = p, size--; p < end; p++)
1866         if (*p == SC_SEP) {
1867             if (--size <= 0)
1868                 return -1;
1869             *p = '\0';
1870             *argv++ = p + 1;
1871         }
1872     *argv = NULL;
1873     return argv - save;
1874 }
1875 
1876 
1877 /*
1878 **  Read function.  Read and process the message.
1879 */
1880 static void
CCreader(CHANNEL * cp)1881 CCreader(CHANNEL *cp)
1882 {
1883     CCDISPATCH *dp;
1884     const char *p;
1885     ICC_MSGLENTYPE bufflen;
1886     ICC_PROTOCOLTYPE protocol;
1887 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1888     static char TOOLONG[] = "0 Reply too long for server to send";
1889     struct sockaddr_un client;
1890 #else
1891     int written;
1892 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
1893     int i;
1894     char buff[BIG_BUFFER + 2];
1895     char *argv[SC_MAXFIELDS + 2];
1896     int argc;
1897     int len;
1898     char *tbuff;
1899     const char *start;
1900     char *copy;
1901     size_t offset;
1902 
1903     if (cp != CCchan) {
1904         syslog(L_ERROR, "%s internal CCreader wrong channel 0x%p not 0x%p",
1905                LogName, (void *) cp, (void *) CCchan);
1906         return;
1907     }
1908 
1909 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
1910 
1911     i = RECVorREAD(CCchan->fd, buff, BIG_BUFFER);
1912     if (i < 0) {
1913         syslog(L_ERROR, "%s cant recv CCreader %m", LogName);
1914         return;
1915     } else if (i == 0) {
1916         syslog(L_ERROR, "%s cant recv CCreader empty", LogName);
1917         return;
1918     } else if (i < (int) HEADER_SIZE) {
1919         syslog(L_ERROR, "%s cant recv CCreader header-length %m", LogName);
1920         return;
1921     }
1922 
1923     memcpy(&protocol, buff, sizeof(protocol));
1924     memcpy(&bufflen, buff + sizeof(protocol), sizeof(bufflen));
1925     bufflen = ntohs(bufflen);
1926 
1927     if (i != bufflen) {
1928         syslog(L_ERROR, "%s cant recv CCreader short-read %m", LogName);
1929         return;
1930     }
1931 
1932     bufflen -= HEADER_SIZE;
1933     memmove(buff, buff + HEADER_SIZE, bufflen);
1934     buff[bufflen] = '\0';
1935 
1936     if (protocol != ICC_PROTOCOL_1) {
1937         syslog(L_ERROR, "%s CCreader protocol mismatch", LogName);
1938         return;
1939     }
1940 
1941 #else /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
1942 
1943     i = RECVorREAD(CCchan->fd, buff, HEADER_SIZE);
1944     if (i < 0) {
1945         syslog(L_ERROR, "%s cant read CCreader header %m", LogName);
1946         return;
1947     } else if (i == 0) {
1948         syslog(L_ERROR, "%s cant read CCreader header empty", LogName);
1949         return;
1950     } else if (i != HEADER_SIZE) {
1951         syslog(L_ERROR, "%s cant read CCreader header-length %m", LogName);
1952         return;
1953     }
1954 
1955     memcpy(&protocol, buff, sizeof(protocol));
1956     memcpy(&bufflen, buff + sizeof(protocol), sizeof(bufflen));
1957     bufflen = ntohs(bufflen);
1958     if (bufflen < HEADER_SIZE || bufflen > BIG_BUFFER) {
1959         syslog(L_ERROR, "%s cant read CCreader bad length", LogName);
1960         return;
1961     }
1962     bufflen -= HEADER_SIZE;
1963 
1964     i = RECVorREAD(CCchan->fd, buff, bufflen);
1965 
1966     if (i < 0) {
1967         syslog(L_ERROR, "%s cant read CCreader buffer %m", LogName);
1968         return;
1969     } else if (i == 0) {
1970         syslog(L_ERROR, "%s cant read CCreader buffer empty", LogName);
1971         return;
1972     } else if (i != bufflen) {
1973         syslog(L_ERROR, "%s cant read CCreader buffer-length %m", LogName);
1974         return;
1975     }
1976 
1977     buff[i] = '\0';
1978 
1979     if (protocol != ICC_PROTOCOL_1) {
1980         syslog(L_ERROR, "%s CCreader protocol mismatch", LogName);
1981         return;
1982     }
1983 
1984 #endif /* defined (HAVE_UNIX_DOMAIN_SOCKETS) */
1985 
1986     /* Copy to a printable buffer, and log.  We skip the first
1987        SC_SEP-delimited field.  Note that the protocol allows for nuls in the
1988        message, which we'll replace with ? for logging. */
1989     copy = xmalloc(bufflen + 1);
1990     memcpy(copy, buff, bufflen);
1991     copy[bufflen] = '\0';
1992     for (offset = 0, start = NULL; offset < (size_t) bufflen; offset++)
1993         if (copy[offset] == SC_SEP) {
1994             copy[offset] = ':';
1995             if (start == NULL)
1996                 start = copy + offset + 1;
1997         } else if (copy[offset] == '\0') {
1998             copy[offset] = '?';
1999         }
2000     notice("ctlinnd command %s", start != NULL ? start : copy);
2001     free(copy);
2002 
2003     /* Split up the fields, get the command letter. */
2004     if ((argc = CCargsplit(buff, &buff[bufflen], argv, ARRAY_SIZE(argv))) < 2
2005         || argc == ARRAY_SIZE(argv)) {
2006         syslog(L_ERROR, "%s bad_fields CCreader", LogName);
2007         return;
2008     }
2009     p = argv[1];
2010     i = *p;
2011 
2012     /* Dispatch to the command function. */
2013     for (argc -= 2, dp = CCcommands; dp < ARRAY_END(CCcommands); dp++)
2014         if (i == dp->Name) {
2015             if (argc != dp->argc)
2016                 p = "1 Wrong number of parameters";
2017             else
2018                 p = (*dp->Function)(&argv[2]);
2019             break;
2020         }
2021     if (dp == ARRAY_END(CCcommands)) {
2022         syslog(L_NOTICE, "%s bad_message %c", LogName, i);
2023         p = "1 Bad command";
2024     } else if (p == NULL)
2025         p = "0 Ok";
2026 
2027     /* Build the reply address and send the reply. */
2028     len = strlen(p) + HEADER_SIZE;
2029     tbuff = xmalloc(len + 1);
2030 
2031     protocol = ICC_PROTOCOL_1;
2032     memcpy(tbuff, &protocol, sizeof(protocol));
2033     tbuff += sizeof(protocol);
2034 
2035     bufflen = htons(len);
2036     memcpy(tbuff, &bufflen, sizeof(bufflen));
2037     tbuff += sizeof(bufflen);
2038 
2039     strlcpy(tbuff, p, len + 1 - sizeof(protocol) - sizeof(bufflen));
2040     tbuff -= HEADER_SIZE;
2041 
2042 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
2043     memset(&client, 0, sizeof client);
2044     client.sun_family = AF_UNIX;
2045     strlcpy(client.sun_path, argv[0], sizeof(client.sun_path));
2046     if (sendto(CCwriter, tbuff, len, 0, (struct sockaddr *) &client,
2047                SUN_LEN(&client))
2048         < 0) {
2049         i = errno;
2050         syslog(i == ENOENT ? L_NOTICE : L_ERROR,
2051                "%s cant sendto CCreader bytes %d %m", LogName, len);
2052         if (i == EMSGSIZE)
2053             sendto(CCwriter, TOOLONG, strlen(TOOLONG), 0,
2054                    (struct sockaddr *) &client, SUN_LEN(&client));
2055     }
2056 #else
2057     if ((i = open(argv[0], O_WRONLY | O_NDELAY)) < 0)
2058         syslog(L_ERROR, "%s cant open %s %m", LogName, argv[0]);
2059     else {
2060         if ((written = write(i, tbuff, len)) != len) {
2061             if (written < 0)
2062                 syslog(L_ERROR, "%s cant write %s %m", LogName, argv[0]);
2063             else
2064                 syslog(L_ERROR, "%s cant write %s", LogName, argv[0]);
2065         }
2066         if (close(i) < 0)
2067             syslog(L_ERROR, "%s cant close %s %m", LogName, argv[0]);
2068     }
2069 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
2070     free(tbuff);
2071 }
2072 
2073 
2074 /*
2075 **  Called when a write-in-progress is done on the channel.  Shouldn't happen.
2076 */
2077 static void
CCwritedone(CHANNEL * unused UNUSED)2078 CCwritedone(CHANNEL *unused UNUSED)
2079 {
2080     syslog(L_ERROR, "%s internal CCwritedone", LogName);
2081 }
2082 
2083 
2084 /*
2085 **  Create the channel.
2086 */
2087 void
CCsetup(void)2088 CCsetup(void)
2089 {
2090     int i;
2091 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
2092     struct sockaddr_un server;
2093     int size = 65535;
2094 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
2095 
2096     if (CCpath == NULL)
2097         CCpath = concatpath(innconf->pathrun, INN_PATH_NEWSCONTROL);
2098     /* Remove old detritus. */
2099     if (unlink(CCpath) < 0 && errno != ENOENT) {
2100         syslog(L_FATAL, "%s cant unlink %s %m", LogName, CCpath);
2101         exit(1);
2102     }
2103 
2104 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
2105     /* Create a socket and name it. */
2106     if ((i = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
2107         syslog(L_FATAL, "%s cant socket %s %m", LogName, CCpath);
2108         exit(1);
2109     }
2110     memset(&server, 0, sizeof server);
2111     server.sun_family = AF_UNIX;
2112     strlcpy(server.sun_path, CCpath, sizeof(server.sun_path));
2113     if (bind(i, (struct sockaddr *) &server, SUN_LEN(&server)) < 0) {
2114         syslog(L_FATAL, "%s cant bind %s %m", LogName, CCpath);
2115         exit(1);
2116     }
2117 
2118     /* Create an unbound socket to reply on. */
2119     if ((CCwriter = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
2120         syslog(L_FATAL, "%s cant socket unbound %m", LogName);
2121         exit(1);
2122     }
2123 
2124     /* Increase the buffer size for the Unix domain socket */
2125     if (setsockopt(CCwriter, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
2126         syslog(L_ERROR, "%s cant setsockopt %m", LogName);
2127 #else
2128     /* Create a named pipe and open it. */
2129     if (mkfifo(CCpath, 0666) < 0) {
2130         syslog(L_FATAL, "%s cant mkfifo %s %m", LogName, CCpath);
2131         exit(1);
2132     }
2133     if ((i = open(CCpath, O_RDWR)) < 0) {
2134         syslog(L_FATAL, "%s cant open %s %m", LogName, CCpath);
2135         exit(1);
2136     }
2137 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
2138 
2139     CCchan = CHANcreate(i, CTcontrol, CSwaiting, CCreader, CCwritedone);
2140     syslog(L_NOTICE, "%s ccsetup %s", LogName, CHANname(CCchan));
2141     RCHANadd(CCchan);
2142 
2143     buffer_resize(&CCreply, SMBUF);
2144 
2145     /*
2146      *  Catch SIGUSR1 so that we can recreate the control channel when
2147      *  needed (i.e. something has deleted our named socket.
2148      */
2149 #if defined(SIGUSR1)
2150     xsignal(SIGUSR1, CCresetup);
2151 #endif /* defined(SIGUSR1) */
2152 
2153     CCsdnotify();
2154 }
2155 
2156 
2157 /*
2158 **  Cleanly shut down the channel.
2159 */
2160 void
CCclose(void)2161 CCclose(void)
2162 {
2163     CHANclose(CCchan, CHANname(CCchan));
2164     CCchan = NULL;
2165     if (unlink(CCpath) < 0)
2166         syslog(L_ERROR, "%s cant unlink %s %m", LogName, CCpath);
2167     free(CCpath);
2168     CCpath = NULL;
2169     free(CCreply.data);
2170     CCreply.data = NULL;
2171     CCreply.size = 0;
2172     CCreply.used = 0;
2173     CCreply.left = 0;
2174 #if defined(HAVE_UNIX_DOMAIN_SOCKETS)
2175     if (close(CCwriter) < 0)
2176         syslog(L_ERROR, "%s cant close unbound %m", LogName);
2177 #endif /* defined(HAVE_UNIX_DOMAIN_SOCKETS) */
2178 }
2179 
2180 
2181 #ifdef HAVE_SIGACTION
2182 #    define NO_SIGACTION_UNUSED UNUSED
2183 #else
2184 #    define NO_SIGACTION_UNUSED
2185 #endif
2186 
2187 /*
2188 **  Restablish the control channel.
2189 */
2190 static void
CCresetup(int s NO_SIGACTION_UNUSED)2191 CCresetup(int s NO_SIGACTION_UNUSED)
2192 {
2193 #ifndef HAVE_SIGACTION
2194     xsignal(s, CCresetup);
2195 #endif
2196     CCclose();
2197     CCsetup();
2198 }
2199