1 /* $Id: site.c 7748 2008-04-06 13:49:56Z iulius $
3 ** Routines to implement site-feeding. Mainly working with channels to
4 ** do buffering and determine what to send.
10 #include "inn/innconf.h"
15 static int SITEhead = NOSITE;
16 static int SITEtail = NOSITE;
17 static char SITEshell[] = _PATH_SH;
21 ** Called when input is ready to read. Shouldn't happen.
24 SITEreader(CHANNEL *cp)
26 syslog(L_ERROR, "%s internal SITEreader: %s", LogName, CHANname(cp));
31 ** Called when write is done. No-op.
34 SITEwritedone(CHANNEL *cp UNUSED)
40 ** Make a site start spooling.
43 SITEspool(SITE *sp, CHANNEL *cp)
46 char buff[SPOOLNAMEBUFF];
50 i = open(name, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
51 if (i < 0 && errno == EISDIR) {
52 FileGlue(buff, sp->SpoolName, '/', "togo");
54 i = open(buff, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
58 syslog(L_ERROR, "%s cant open %s %m", sp->Name, name);
59 IOError("site batch file", i);
65 /* syslog(L_ERROR, "DEBUG ERROR SITEspool trashed:%d %s:%d", cp->fd, sp->Name, i);
66 CPU-eating bug, killed by kre. */
74 sp->Channel = CHANcreate(i, CTfile, CSwriting, SITEreader, SITEwritedone);
75 if (sp->Channel == NULL) {
76 syslog(L_ERROR, "%s cant channel %m", sp->Name);
80 WCHANset(sp->Channel, "", 0);
87 ** Delete a site from the file writing list. Can be called even if
88 ** site is not on the list.
93 if (sp->Next != NOSITE || sp->Prev != NOSITE
94 || (SITEhead != NOSITE && sp == &Sites[SITEhead]))
97 if (sp->Next != NOSITE)
98 Sites[sp->Next].Prev = sp->Prev;
99 else if (SITEtail != NOSITE && sp == &Sites[SITEtail])
102 if (sp->Prev != NOSITE)
103 Sites[sp->Prev].Next = sp->Next;
104 else if (SITEhead != NOSITE && sp == &Sites[SITEhead])
107 sp->Next = sp->Prev = NOSITE;
112 ** Find the oldest "file feed" site and buffer it.
115 SITEbufferoldest(void)
121 /* Get the oldest user of a file. */
122 if (SITEtail == NOSITE) {
123 syslog(L_ERROR, "%s internal no oldest site found", LogName);
128 sp = &Sites[SITEtail];
132 syslog(L_ERROR, "%s internal oldest (%s) was buffered", LogName,
137 if (sp->Type != FTfile) {
138 syslog(L_ERROR, "%s internal oldest (%s) not FTfile", LogName,
143 /* Write out what we can. */
144 WCHANflush(sp->Channel);
146 /* Get a buffer for the site. */
152 bp->size = sp->Flushpoint;
153 bp->data = xmalloc(bp->size);
156 bp->size = sp->Flushpoint;
157 bp->data = xrealloc(bp->data, bp->size);
160 /* If there's any unwritten data, copy it. */
161 out = &sp->Channel->Out;
163 buffer_set(bp, &out->data[out->used], out->left);
167 /* Now close the original channel. */
168 CHANclose(sp->Channel, sp->Name);
173 * * Bilge Site's Channel out buffer.
176 SITECHANbilge(SITE *sp)
180 char buff[SPOOLNAMEBUFF];
183 name = sp->SpoolName;
184 fd = open(name, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
185 if (fd < 0 && errno == EISDIR) {
186 FileGlue(buff, sp->SpoolName, '/', "togo");
188 fd = open(buff, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
192 syslog(L_ERROR, "%s cant open %s %m", sp->Name, name);
193 IOError("site batch file",oerrno);
197 while (sp->Channel->Out.left > 0) {
198 i = write(fd, &sp->Channel->Out.data[sp->Channel->Out.used],
199 sp->Channel->Out.left);
201 syslog(L_ERROR,"%s cant spool count %lu", CHANname(sp->Channel),
202 (unsigned long) sp->Channel->Out.left);
206 sp->Channel->Out.left -= i;
207 sp->Channel->Out.used += i;
210 free(sp->Channel->Out.data);
211 sp->Channel->Out.data = xmalloc(SMBUF);
212 sp->Channel->Out.size = SMBUF;
213 sp->Channel->Out.left = 0;
214 sp->Channel->Out.used = 0;
219 ** Check if we need to write out the site's buffer. If we're buffered
220 ** or the feed is backed up, this gets a bit complicated.
223 SITEflushcheck(SITE *sp, struct buffer *bp)
228 /* If we're buffered, and we hit the flushpoint, do an LRU. */
230 if (bp->left < sp->Flushpoint)
232 while (SITEcount >= MaxOutgoing)
234 if (!SITEsetup(sp) || sp->Buffered) {
235 syslog(L_ERROR, "%s cant unbuffer %m", sp->Name);
238 WCHANsetfrombuffer(sp->Channel, bp);
239 WCHANadd(sp->Channel);
240 /* Reset buffer; data has been moved. */
241 buffer_set(bp, "", 0);
247 /* Handle buffering. */
250 if (i < sp->StopWriting)
252 if ((sp->StartWriting == 0 || i > sp->StartWriting)
253 && !CHANsleeping(cp)) {
254 if (sp->Type == FTchannel) { /* channel feed, try the write */
258 j = write(cp->fd, &bp->data[bp->used], bp->left);
263 /* Since we just had a successful write, we need to clear the
264 * channel's error counts. - dave@jetcafe.org */
266 cp->BlockedWrites = 0;
269 /* reset Used, Left on bp, keep channel buffer size from
271 bp->used = bp->left = 0;
280 cp->LastActive = Now.time;
282 /* If we're a channel that's getting big, see if we need to spool. */
283 if (sp->Type == FTfile || sp->StartSpooling == 0 || i < sp->StartSpooling)
286 syslog(L_ERROR, "%s spooling %d bytes", sp->Name, i);
287 if(!SITECHANbilge(sp)) {
288 syslog(L_ERROR, "%s overflow %d bytes", sp->Name, i);
295 ** Send a control line to an exploder.
298 SITEwrite(SITE *sp, const char *text)
300 static char PREFIX[] = { EXP_CONTROL, '\0' };
306 if (sp->Channel == NULL)
308 sp->Channel->LastActive = Now.time;
309 bp = &sp->Channel->Out;
311 buffer_append(bp, PREFIX, strlen(PREFIX));
312 buffer_append(bp, text, strlen(text));
313 buffer_append(bp, "\n", 1);
314 if (sp->Channel != NULL)
315 WCHANadd(sp->Channel);
320 ** Send the desired data about an article down a channel.
323 SITEwritefromflags(SITE *sp, ARTDATA *Data)
325 HDRCONTENT *hc = Data->HdrContent;
326 static char ITEMSEP[] = " ";
327 static char NL[] = "\n";
338 /* This should not happen, but if we tried to spool and failed,
339 * e.g., because of a bad F param for this site, we can get
340 * into this state. We already logged a message so give up. */
341 if (sp->Channel == NULL)
343 bp = &sp->Channel->Out;
345 for (Dirty = false, p = sp->FileFlags; *p; p++) {
348 syslog(L_ERROR, "%s internal SITEwritefromflags %c", sp->Name, *p);
352 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
353 buffer_append(bp, Data->Bytes + sizeof("Bytes: ") - 1,
359 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
360 buffer_append(bp, Data->TokenText, sizeof(TOKEN) * 2 + 2);
364 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
365 buffer_append(bp, "[", 1);
366 buffer_append(bp, HashToText(*(Data->Hash)), sizeof(HASH)*2);
367 buffer_append(bp, "]", 1);
369 case FEED_HDR_DISTRIB:
371 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
372 buffer_append(bp, HDR(HDR__DISTRIBUTION),
373 HDR_LEN(HDR__DISTRIBUTION));
375 case FEED_HDR_NEWSGROUP:
377 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
378 buffer_append(bp, HDR(HDR__NEWSGROUPS), HDR_LEN(HDR__NEWSGROUPS));
382 buffer_append(bp, NL, strlen(NL));
383 buffer_append(bp, Data->Headers.data, Data->Headers.left);
387 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
388 buffer_append(bp, Data->Overview.data, Data->Overview.left);
392 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
393 if (!Data->Hassamepath || Data->AddAlias || Pathcluster.used) {
394 if (Pathcluster.used)
395 buffer_append(bp, Pathcluster.data, Pathcluster.used);
396 buffer_append(bp, Path.data, Path.used);
398 buffer_append(bp, Pathalias.data, Pathalias.used);
400 if (Data->Hassamecluster)
401 buffer_append(bp, HDR(HDR__PATH) + Pathcluster.used,
402 HDR_LEN(HDR__PATH) - Pathcluster.used);
404 buffer_append(bp, HDR(HDR__PATH), HDR_LEN(HDR__PATH));
408 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
409 buffer_append(bp, Data->Replic, Data->ReplicLength);
411 case FEED_STOREDGROUP:
413 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
414 buffer_append(bp, Data->Newsgroups.List[0],
415 Data->StoredGroupLength);
417 case FEED_TIMERECEIVED:
419 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
420 snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Arrived);
421 buffer_append(bp, pbuff, strlen(pbuff));
423 case FEED_TIMEPOSTED:
425 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
426 snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Posted);
427 buffer_append(bp, pbuff, strlen(pbuff));
429 case FEED_TIMEEXPIRED:
431 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
432 snprintf(pbuff, sizeof(pbuff), "%ld", (long) Data->Expires);
433 buffer_append(bp, pbuff, strlen(pbuff));
437 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
438 buffer_append(bp, HDR(HDR__MESSAGE_ID), HDR_LEN(HDR__MESSAGE_ID));
441 if (sp->FNLnames.data) {
442 /* Funnel; write names of our sites that got it. */
444 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
445 buffer_append(bp, sp->FNLnames.data, sp->FNLnames.used);
448 /* Not funnel; write names of all sites that got it. */
449 for (spx = Sites, i = nSites; --i >= 0; spx++)
452 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
453 buffer_append(bp, spx->Name, spx->NameLength);
460 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
462 buffer_append(bp, sp->ng->Name, sp->ng->NameLength);
464 buffer_append(bp, "?", 1);
468 buffer_append(bp, ITEMSEP, strlen(ITEMSEP));
469 buffer_append(bp, Data->Feedsite, Data->FeedsiteLength);
475 buffer_append(bp, "\n", 1);
476 SITEflushcheck(sp, bp);
482 ** Send one article to a site.
485 SITEsend(SITE *sp, ARTDATA *Data)
491 char * argv[MAX_BUILTIN_ARGV];
495 syslog(L_ERROR, "%s internal SITEsend type %d", sp->Name, sp->Type);
500 syslog(L_ERROR, "%s funnel_send", sp->Name);
505 SITEwritefromflags(sp, Data);
508 /* Set up the argument vector. */
509 if (sp->FNLwantsnames) {
510 i = strlen(sp->Param) + sp->FNLnames.used;
511 if (i + (sizeof(TOKEN) * 2) + 3 >= sizeof buff) {
512 syslog(L_ERROR, "%s toolong need %lu for %s",
513 sp->Name, (unsigned long) (i + (sizeof(TOKEN) * 2) + 3),
517 temp = xmalloc(i + 1);
518 p = strchr(sp->Param, '*');
520 strlcpy(temp, sp->Param, i + 1);
521 strlcat(temp, sp->FNLnames.data, i + 1);
522 strlcat(temp, &p[1], i + 1);
524 snprintf(buff, sizeof(buff), temp, Data->TokenText);
528 snprintf(buff, sizeof(buff), sp->Param, Data->TokenText);
530 if (NeedShell(buff, (const char **)argv, (const char **)ARRAY_END(argv))) {
532 argv[1] = (char *) "-c";
537 /* Start the process. */
538 i = Spawn(sp->Nice, 0, (int)fileno(Errlog), (int)fileno(Errlog), argv);
547 ** The channel was sleeping because we had to spool our output to
548 ** a file. Flush and restart.
551 SITEspoolwake(CHANNEL *cp)
556 ip = (int *) cp->Argument;
560 if (sp->Channel != cp) {
561 syslog(L_ERROR, "%s internal SITEspoolwake %s got %d, not %d",
562 LogName, sp->Name, cp->fd, sp->Channel->fd);
565 syslog(L_NOTICE, "%s spoolwake", sp->Name);
571 ** Start up a process for a channel, or a spool to a file if we can't.
572 ** Create a channel for the site to talk to.
575 SITEstartprocess(SITE *sp)
578 char *argv[MAX_BUILTIN_ARGV];
584 /* Create a socketpair. */
585 if (socketpair(PF_UNIX, SOCK_STREAM, 0, pan) < 0) {
586 syslog(L_ERROR, "%s cant socketpair %m", sp->Name);
592 syslog(L_ERROR, "%s cant pipe %m", sp->Name);
596 close_on_exec(pan[PIPE_WRITE], true);
598 /* Set up the argument vector. */
599 process = xstrdup(sp->Param);
600 if (NeedShell(process, (const char **)argv, (const char **)ARRAY_END(argv))) {
602 argv[1] = (char *) "-c";
608 i = Spawn(sp->Nice, pan[PIPE_READ], (int)fileno(Errlog),
609 (int)fileno(Errlog), argv);
612 sp->Spooling = false;
613 sp->Process = PROCwatch(i, sp - Sites);
614 close(pan[PIPE_READ]);
615 sp->Channel = CHANcreate(pan[PIPE_WRITE],
616 sp->Type == FTchannel ? CTprocess : CTexploder,
617 CSwriting, SITEreader, SITEwritedone);
622 /* Error. Switch to spooling. */
623 syslog(L_ERROR, "%s cant spawn spooling %m", sp->Name);
624 close(pan[PIPE_WRITE]);
625 close(pan[PIPE_READ]);
627 if (!SITEspool(sp, (CHANNEL *)NULL))
630 /* We'll try to restart the channel later. */
631 syslog(L_ERROR, "%s cant spawn spooling %m", sp->Name);
632 ip = xmalloc(sizeof(int));
634 SCHANadd(sp->Channel, Now.time + innconf->chanretrytime, NULL,
641 ** Set up a site for internal buffering.
652 buffer_resize(bp, sp->Flushpoint);
653 buffer_set(bp, "", 0);
654 syslog(L_NOTICE, "%s buffered", sp->Name);
659 ** Link a site at the head of the "currently writing to a file" list
662 SITEmovetohead(SITE *sp)
664 if ((SITEhead == NOSITE) && ((sp->Next != NOSITE) || (sp->Prev != NOSITE)))
667 if ((sp->Next = SITEhead) != NOSITE)
668 Sites[SITEhead].Prev = sp - Sites;
671 SITEhead = sp - Sites;
672 if (SITEtail == NOSITE)
673 SITEtail = sp - Sites;
680 ** Set up a site's feed. This means opening a file or channel if needed.
690 syslog(L_ERROR, "%s internal SITEsetup %d",
696 /* Nothing to do here. */
699 if (SITEcount >= MaxOutgoing)
702 sp->Buffered = false;
703 fd = open(sp->Param, O_APPEND | O_CREAT | O_WRONLY, BATCHFILE_MODE);
705 if (errno == EMFILE) {
706 syslog(L_ERROR, "%s cant open %s %m", sp->Name, sp->Param);
711 syslog(L_NOTICE, "%s cant open %s %m", sp->Name, sp->Param);
712 IOError("site file", oerrno);
716 sp->Channel = CHANcreate(fd, CTfile, CSwriting,
717 SITEreader, SITEwritedone);
718 syslog(L_NOTICE, "%s opened %s", sp->Name, CHANname(sp->Channel));
719 WCHANset(sp->Channel, "", 0);
724 if (!SITEstartprocess(sp))
726 syslog(L_NOTICE, "%s spawned %s", sp->Name, CHANname(sp->Channel));
727 WCHANset(sp->Channel, "", 0);
728 WCHANadd(sp->Channel);
736 ** A site's channel process died; restart it.
739 SITEprocdied(SITE *sp, int process, PROCESS *pp)
741 syslog(pp->Status ? L_ERROR : L_NOTICE, "%s exit %d elapsed %ld pid %ld",
742 sp->Name ? sp->Name : "?", pp->Status,
743 (long) (pp->Collected - pp->Started), (long) pp->Pid);
744 if (sp->Process != process || sp->Name == NULL)
745 /* We already started a new process for this channel
746 * or this site has been dropped. */
748 if (sp->Channel != NULL)
749 CHANclose(sp->Channel, CHANname(sp->Channel));
750 sp->Working = SITEsetup(sp);
752 syslog(L_ERROR, "%s cant restart %m", sp->Name);
755 syslog(L_NOTICE, "%s restarted", sp->Name);
759 ** A channel is about to be closed; see if any site cares.
762 SITEchanclose(CHANNEL *cp)
768 for (i = nSites, sp = Sites; --i >= 0; sp++)
769 if (sp->Channel == cp) {
770 /* Found the site that has this channel. Start that
771 * site spooling, copy any data that might be pending,
772 * and arrange to retry later. */
773 if (!SITEspool(sp, (CHANNEL *)NULL)) {
774 syslog(L_ERROR, "%s loss %lu bytes", sp->Name,
775 (unsigned long) cp->Out.left);
778 WCHANsetfrombuffer(sp->Channel, &cp->Out);
779 WCHANadd(sp->Channel);
780 ip = xmalloc(sizeof(int));
782 SCHANadd(sp->Channel, Now.time + innconf->chanretrytime, NULL,
790 ** Flush any pending data waiting to be sent.
793 SITEflush(SITE *sp, const bool Restart)
799 if (sp->Name == NULL)
803 SITEforward(sp, "flush");
807 syslog(L_ERROR, "%s internal SITEflush %d", sp->Name, sp->Type);
813 /* Nothing to do here. */
818 /* If spooling, close the file right now -- documented behavior. */
819 if (sp->Spooling && (cp = sp->Channel) != NULL) {
821 CHANclose(cp, CHANname(cp));
827 /* We must ensure we have a file open for this site, so if
828 * we're buffered we HACK and pretend we have no sites
833 if (!SITEsetup(sp) || sp->Buffered)
834 syslog(L_ERROR, "%s cant unbuffer to flush", sp->Name);
836 buffer_swap(&sp->Buffer, &sp->Channel->Out);
842 /* We're only dealing with files and channels now. */
843 if ((cp = sp->Channel) != NULL)
846 /* Restart the site, copy any pending data. */
849 syslog(L_ERROR, "%s cant restart %m", sp->Name);
850 else if (cp != NULL) {
852 /* SITEsetup had to buffer us; save any residue. */
855 buffer_set(&sp->Buffer, &out->data[out->used], out->left);
858 WCHANsetfrombuffer(sp->Channel, &cp->Out);
861 else if (cp != NULL && cp->Out.left) {
862 if (sp->Type == FTfile || sp->Spooling) {
863 /* Can't flush a file? Hopeless. */
864 syslog(L_ERROR, "%s dataloss %lu", sp->Name,
865 (unsigned long) cp->Out.left);
868 /* Must be a working channel; spool and retry. */
869 syslog(L_ERROR, "%s spooling %lu bytes", sp->Name,
870 (unsigned long) cp->Out.left);
871 if (SITEspool(sp, cp))
872 SITEflush(sp, false);
876 /* Close the old channel if it was open. */
878 /* Make sure we have no dangling pointers to it. */
881 CHANclose(cp, sp->Name);
882 if (sp->Type == FTfile)
892 SITEflushall(const bool Restart)
897 for (i = nSites, sp = Sites; --i >= 0; sp++)
899 SITEflush(sp, Restart);
904 ** Run down the site's pattern list and see if it wants the specified
908 SITEwantsgroup(SITE *sp, char *name)
917 for (argv = ME.Patterns; (pat = *argv++) != NULL; ) {
918 subvalue = *pat != SUB_NEGATE && *pat != SUB_POISON;
921 if ((match != subvalue) && uwildmat(name, pat))
925 for (argv = sp->Patterns; (pat = *argv++) != NULL; ) {
926 subvalue = *pat != SUB_NEGATE && *pat != SUB_POISON;
929 if ((match != subvalue) && uwildmat(name, pat))
937 ** Run down the site's pattern list and see if specified newsgroup is
938 ** considered poison.
941 SITEpoisongroup(SITE *sp, char *name)
950 for (argv = ME.Patterns; (pat = *argv++) != NULL; ) {
951 poisonvalue = *pat == SUB_POISON;
952 if (*pat == SUB_NEGATE || *pat == SUB_POISON)
954 if (uwildmat(name, pat))
958 for (argv = sp->Patterns; (pat = *argv++) != NULL; ) {
959 poisonvalue = *pat == SUB_POISON;
960 if (*pat == SUB_NEGATE || *pat == SUB_POISON)
962 if (uwildmat(name, pat))
973 SITEfind(const char *p)
978 for (i = nSites, sp = Sites; --i >= 0; sp++)
979 if (sp->Name && strcasecmp(p, sp->Name) == 0)
986 ** Find the next site that matches this site.
989 SITEfindnext(const char *p, SITE *sp)
993 for (sp++, end = &Sites[nSites]; sp < end; sp++)
994 if (sp->Name && strcasecmp(p, sp->Name) == 0)
1001 ** Close a site down.
1007 HASHFEEDLIST *hf, *hn;
1012 CHANclose(sp->Channel, CHANname(sp->Channel));
1019 if (sp->Process > 0) {
1020 /* Kill the backpointer so PROCdied won't call us. */
1021 PROCunwatch(sp->Process);
1028 if (sp->Originator) {
1029 free(sp->Originator);
1030 sp->Originator = NULL;
1036 if (sp->SpoolName) {
1037 free(sp->SpoolName);
1038 sp->SpoolName = NULL;
1042 sp->Patterns = NULL;
1044 if (sp->Exclusions) {
1045 free(sp->Exclusions);
1046 sp->Exclusions = NULL;
1048 if (sp->Distributions) {
1049 free(sp->Distributions);
1050 sp->Distributions = NULL;
1052 if (sp->Buffer.data) {
1053 free(sp->Buffer.data);
1054 sp->Buffer.data = NULL;
1055 sp->Buffer.size = 0;
1057 if (sp->FNLnames.data) {
1058 free(sp->FNLnames.data);
1059 sp->FNLnames.data = NULL;
1060 sp->FNLnames.size = 0;
1062 if (sp->HashFeedList) {
1063 for (hf = sp->HashFeedList; hf; hf = hn) {
1067 sp->HashFeedList = NULL;
1070 /* If this site was a master, find a new one. */
1072 for (new = NOSITE, s = Sites, i = nSites; --i >= 0; s++)
1073 if (&Sites[s->Master] == sp) {
1074 if (new == NOSITE) {
1082 sp->IsMaster = false;
1088 ** If a site is an exploder or funnels into one, forward a command
1092 SITEforward(SITE *sp, const char *text)
1098 if (fsp->Funnel != NOSITE)
1099 fsp = &Sites[fsp->Funnel];
1100 if (sp->Name == NULL || fsp->Name == NULL)
1102 if (fsp->Type == FTexploder) {
1103 strlcpy(buff, text, sizeof(buff));
1104 if (fsp != sp && fsp->FNLwantsnames) {
1105 strlcat(buff, " ", sizeof(buff));
1106 strlcat(buff, sp->Name, sizeof(buff));
1108 SITEwrite(fsp, buff);
1119 SITEforward(sp, "drop");
1120 SITEflush(sp, false);
1126 ** Append info about the current state of the site to the buffer
1129 SITEinfo(struct buffer *bp, SITE *sp, const bool Verbose)
1131 static char FREESITE[] = "<<No name>>\n\n";
1137 if (sp->Name == NULL) {
1138 buffer_append(bp, FREESITE, strlen(FREESITE));
1143 snprintf(buff, sizeof(buff), "%s%s:\t", sp->Name,
1144 sp->IsMaster ? "(*)" : "");
1147 if (sp->Type == FTfunnel) {
1148 sp = &Sites[sp->Funnel];
1149 sprintf(p, "funnel -> %s: ", sp->Name);
1155 sprintf(p, "unknown feed type %d", sp->Type);
1160 p += strlen(strcpy(p, "file"));
1162 sprintf(p, " buffered(%lu)", (unsigned long) sp->Buffer.left);
1165 else if ((cp = sp->Channel) == NULL)
1166 p += strlen(strcpy(p, " no channel?"));
1168 sprintf(p, " open fd=%d, in mem %lu", cp->fd,
1169 (unsigned long) cp->Out.left);
1174 p += strlen(strcpy(p, "channel"));
1177 p += strlen(strcpy(p, "exploder"));
1179 if (sp->Process >= 0) {
1180 sprintf(p, " pid=%ld", (long) sp->pid);
1184 p += strlen(strcpy(p, " spooling"));
1185 if ((cp = sp->Channel) == NULL)
1186 p += strlen(strcpy(p, " no channel?"));
1188 sprintf(p, " fd=%d, in mem %lu", cp->fd,
1189 (unsigned long) cp->Out.left);
1194 p += strlen(strcpy(p, "recursive funnel"));
1197 p += strlen(strcpy(p, "log only"));
1200 p += strlen(strcpy(p, "program"));
1201 if (sp->FNLwantsnames)
1202 p += strlen(strcpy(p, " with names"));
1208 if (sp->Buffered && sp->Flushpoint) {
1209 sprintf(p, "%sFlush @ %ld", sep, sp->Flushpoint);
1213 if (sp->StartWriting || sp->StopWriting) {
1214 sprintf(p, "%sWrite [%ld..%ld]", sep,
1215 sp->StopWriting, sp->StartWriting);
1219 if (sp->StartSpooling) {
1220 sprintf(p, "%sSpool @ %ld", sep, sp->StartSpooling);
1226 if (sp->Spooling && sp->SpoolName) {
1227 sprintf(p, "\tSpooling to \"%s\"\n", sp->SpoolName);
1230 if ((cp = sp->Channel) != NULL) {
1231 sprintf(p, "\tChannel created %.12s",
1232 ctime(&cp->Started) + 4);
1234 sprintf(p, ", last active %.12s\n",
1235 ctime(&cp->LastActive) + 4);
1237 if (cp->Waketime > Now.time) {
1238 sprintf(p, "\tSleeping until %.12s\n",
1239 ctime(&cp->Waketime) + 4);
1245 buffer_append(bp, buff, p - buff);