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