1 /* $Id: buffchan.c 6163 2003-01-19 22:56:34Z rra $
3 ** Buffered file exploder for innd.
13 #include "inn/innconf.h"
14 #include "inn/messages.h"
21 ** Hash functions for hashing sitenames.
23 #define SITE_HASH(Name, p, j) \
24 for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++
26 #define SITE_BUCKET(j) &SITEtable[j & (SITE_SIZE - 1)]
30 ** Entry for a single active site.
32 typedef struct _SITE {
48 ** Site hashtable bucket.
50 typedef struct _SITEHASH {
57 /* Global variables. */
59 static const char *Map;
60 static int BufferMode;
61 static int CloseEvery;
62 static int FlushEvery;
63 static int CloseSeconds;
64 static int FlushSeconds;
65 static sig_atomic_t GotInterrupt;
66 static SITEHASH SITEtable[SITE_SIZE];
71 ** Set up the site information. Basically creating empty buckets.
78 for (shp = SITEtable; shp < ARRAY_END(SITEtable); shp++) {
80 shp->Sites = xmalloc(shp->Size * sizeof(SITE));
94 if ((F = sp->F) != NULL) {
95 if (fflush(F) == EOF || ferror(F)
96 || fchmod((int)fileno(F), 0664) < 0
98 syswarn("%s cannot close %s", sp->Name, sp->Filename);
104 ** Close all open sites.
113 for (shp = SITEtable; shp < ARRAY_END(SITEtable); shp++)
114 for (sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
120 ** Open the file for a site.
122 static void SITEopen(SITE *sp)
126 if ((sp->F = xfopena(sp->Filename)) == NULL
127 && ((e = errno) != EACCES || chmod(sp->Filename, 0644) < 0
128 || (sp->F = xfopena(sp->Filename)) == NULL)) {
129 syswarn("%s cannot fopen %s", sp->Name, sp->Filename);
130 if ((sp->F = fopen("/dev/null", "w")) == NULL)
131 /* This really should not happen. */
132 sysdie("%s cannot fopen /dev/null", sp->Name);
134 else if (fchmod((int)fileno(sp->F), 0444) < 0)
135 syswarn("%s cannot fchmod %s", sp->Name, sp->Filename);
137 if (BufferMode != '\0')
138 setbuf(sp->F, sp->Buffer);
140 /* Reset all counters. */
143 sp->LastFlushed = Now.time;
144 sp->LastClosed = Now.time;
150 ** Find a site, possibly create if not found.
153 SITEfind(char *Name, bool CanCreate)
163 /* Look for site in the hash table. */
164 SITE_HASH(Name, p, j);
165 shp = SITE_BUCKET(j);
166 for (c = *Name, sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
167 if (c == sp->Name[0] && strcasecmp(Name, sp->Name) == 0)
172 /* Adding a new site -- grow hash bucket if we need to. */
173 if (shp->Used == shp->Size - 1) {
175 shp->Sites = xrealloc(shp->Sites, shp->Size * sizeof(SITE));
177 sp = &shp->Sites[shp->Used++];
179 /* Fill in the structure for the new site. */
180 sp->Name = xstrdup(Name);
181 snprintf(buff, sizeof(buff), Format, Map ? MAPname(Name) : sp->Name);
182 sp->Filename = xstrdup(buff);
183 if (BufferMode == 'u')
185 else if (BufferMode == 'b')
186 sp->Buffer = xmalloc(BUFSIZ);
194 ** Flush a site -- close and re-open the file.
201 if ((F = sp->F) != NULL) {
202 if (fflush(F) == EOF || ferror(F)
203 || fchmod((int)fileno(F), 0664) < 0
205 syswarn("%s cannot close %s", sp->Name, sp->Filename);
214 ** Flush all open sites.
223 for (shp = SITEtable; shp < ARRAY_END(SITEtable); shp++)
224 for (sp = shp->Sites, i = shp->Used; --i >= 0; sp++)
230 ** Write data to a site.
233 SITEwrite(char *name, char *text, size_t len)
237 sp = SITEfind(name, true);
241 if (fwrite(text, 1, len, sp->F) != len)
242 syswarn("%s cannot write", sp->Name);
244 /* Bump line count; see if time to close or flush. */
245 if (CloseEvery && ++(sp->CloseLines) >= CloseEvery) {
249 if (CloseSeconds && sp->LastClosed + CloseSeconds < Now.time) {
253 if (FlushEvery && ++(sp->FlushLines) >= FlushEvery) {
254 if (fflush(sp->F) == EOF || ferror(sp->F))
255 syswarn("%s cannot flush %s", sp->Name, sp->Filename);
256 sp->LastFlushed = Now.time;
259 else if (FlushSeconds && sp->LastFlushed + FlushSeconds < Now.time) {
260 if (fflush(sp->F) == EOF || ferror(sp->F))
261 syswarn("%s cannot flush %s", sp->Name, sp->Filename);
262 sp->LastFlushed = Now.time;
269 ** Handle a command message.
276 if (*p == 'b' && strncmp(p, "begin", 5) == 0)
280 if (*p == 'f' && strncmp(p, "flush", 5) == 0) {
281 for (p += 5; ISWHITE(*p); p++)
285 else if ((sp = SITEfind(p, false)) != NULL)
288 fprintf(stderr, "buffchan flush %s unknown site\n", p);*/
292 if (*p == 'd' && strncmp(p, "drop", 4) == 0) {
293 for (p += 4; ISWHITE(*p); p++)
297 else if ((sp = SITEfind(p, false)) == NULL)
298 warn("drop %s unknown site", p);
306 if (*p == 'r' && strncmp(p, "readmap", 7) == 0) {
311 /* Other command messages -- ignored. */
312 warn("unknown message %s", p);
317 ** Mark that we got a signal; let two signals kill us.
320 CATCHinterrupt(int s)
328 main(int ac, char *av[])
341 /* First thing, set up our identity. */
342 message_program_name = "buffchan";
345 if (!innconf_read(NULL))
347 ERRLOG = concatpath(innconf->pathlog, _PATH_ERRLOG);
352 GotInterrupt = false;
355 xsignal(SIGHUP, CATCHinterrupt);
356 xsignal(SIGINT, CATCHinterrupt);
357 xsignal(SIGQUIT, CATCHinterrupt);
358 xsignal(SIGPIPE, CATCHinterrupt);
359 xsignal(SIGTERM, CATCHinterrupt);
360 xsignal(SIGALRM, CATCHinterrupt);
363 while ((i = getopt(ac, av, "bc:C:d:f:l:L:m:p:rs:u")) != EOF)
373 CloseEvery = atoi(optarg);
376 CloseSeconds = atoi(optarg);
381 Format =xstrdup("%s");
384 Fields = atoi(optarg);
387 FlushEvery = atoi(optarg);
390 FlushSeconds = atoi(optarg);
397 if ((F = fopen(optarg, "w")) == NULL)
398 sysdie("cannot fopen %s", optarg);
399 fprintf(F, "%ld\n", (long)getpid());
400 if (ferror(F) || fclose(F) == EOF)
401 sysdie("cannot fclose %s", optarg);
415 /* Do some basic set-ups. */
417 freopen(ERRLOG, "a", stderr);
418 if (Format == NULL) {
419 Format = concatpath(innconf->pathoutgoing, "%s");
421 if (Directory && chdir(Directory) < 0)
422 sysdie("cannot chdir to %s", Directory);
426 for (qp = QIOfdopen((int)fileno(stdin)); !GotInterrupt ; ) {
427 if ((line = QIOread(qp)) == NULL) {
429 syswarn("cannot read");
432 if (QIOtoolong(qp)) {
443 if (*line == EXP_CONTROL && *++line != EXP_CONTROL) {
448 /* Skip the right number of leading fields. */
449 for (i = Fields, p = line; *p; p++)
450 if (*p == ' ' && --i <= 0)
453 /* Nothing to write. Probably shouldn't happen. */
456 /* Add a newline, get the length of all leading fields. */
460 if (GetTimeInfo(&Now) < 0) {
461 syswarn("cannot get time");
465 /* Rest of the line is space-separated list of filenames. */
466 for (; *p; p = next) {
467 /* Skip whitespace, get next word. */
470 for (next = p; *next && *next != ' '; next++)
475 SITEwrite(p, line, i);