leafnode  1.12.0
About: Leafnode is a store & forward NNTP proxy for small (dialup) sites.
  Fossies Dox: leafnode-1.12.0.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

texpire.c
Go to the documentation of this file.
1/*
2texpire -- expire old articles
3
4Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
5Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
622646949.
7Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
8and Randolf Skerka <Randolf.Skerka@gmx.de>.
9Copyright of the modifications 1997.
10Modified by Kent Robotti <robotti@erols.com>. Copyright of the
11modifications 1998.
12Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
13Copyright of the modifications 1998.
14Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
15Copyright of the modifications 1998, 1999.
16Modified by Kazushi (Jam) Marukawa <jam@pobox.com>.
17Copyright of the modifications 1998, 1999.
18Modified by Matthias Andree <matthias.andree@gmx.de>.
19Copyright of the modifications 2000 - 2010.
20
21See file COPYING for restrictions on the use of this software.
22*/
23
24#include "leafnode.h"
25#include "ln_log.h"
26
27#ifdef SOCKS
28#include <socks.h>
29#endif
30
31#include <ctype.h>
32#include <limits.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include "system.h"
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <syslog.h>
41#include <unistd.h>
42#include <errno.h>
43#include <signal.h>
44#include "mysigact.h"
45#include "mastring.h"
46
47static time_t default_expire;
48
49int verbose = 0;
50int debug = 0;
51static int repair = 0; /* run expensive checks */
52
53static int use_atime = 1; /* look for atime on articles to expire */
54static int quiet = 0; /* shut up */
55
56static int eflag; /* set to 1 if "mids" file based expiry must not take place */
57
58static const char *const MIDSFILE = "mids";
59
60struct exp {
61 char *xover; /* full xover info */
62 int kill;
63 int exists;
64};
65
66static sigjmp_buf jmpbuffer;
67static int blocksig;
68
69static void
70sig_int(int signo)
71{
72 if (blocksig) return;
73 if (signo == SIGINT || signo == SIGTERM) {
74 siglongjmp(jmpbuffer, 1);
75 }
76}
77
78/* hook for traverseidtree */
79/* writes "mids" file for reliable expiry without counting hard links
80 * to evade local hard link attack DoS */
81static int
82th(const char *mm) {
83 const char *f;
84 char *p, *t;
85 int fd;
86 ssize_t ml;
87 char *m;
88 struct stat st;
89 /*@only@*/ static char *b;
90 static size_t b_siz;
91
92 if (mm == NULL)
93 {
94 b_siz = 0;
95 free(b);
96 return 0;
97 }
98
99 m = critstrdup(mm, "th");
100 f = lookup(m);
101 p = critmalloc(strlen(f) + 6, "th");
102 strcpy(p, f);
103 t = strrchr(p, '/');
104 if (!t) {
105 ln_log(LNLOG_SERR, LNLOG_CTOP, "can't find / - internal error");
106 free(m);
107 free(p);
108 return 1;
109 }
110 strcpy(++t, MIDSFILE);
111
112 fd = open(p, O_WRONLY|O_APPEND|O_CREAT, 0600);
113 if (fd < 0) {
114 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot append to file %s: %m", p);
115 free(p);
116 free(m);
117 return 1;
118 }
119 if (fstat(fd, &st)) {
120 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot fstat fd #%d: %m", fd);
121 free(p);
122 free(m);
123 close(fd);
124 return 1;
125 }
126 /* this file is not portable across endianness, why bother, we're
127 * alone - the spool is locked */
128
129 ml = strlen(m);
130
131 /* resize buffer memory, generously */
132 if (b_siz < ml + 1 + sizeof(ml)) {
133 if (b) free(b);
134 b_siz = ml + 128 + sizeof(ml);
135 b = critmalloc(b_siz, "th");
136 }
137
138 /* make some effort to write the whole record (size + content)
139 * atomically, to avoid corruption when we're interrupted */
140 memcpy(b, &ml, sizeof(ml));
141 for(t = m; *t; t++)
142 if (*t == '/')
143 *t = '@';
144 strcpy(b + sizeof(ml), m);
145 if (write(fd, b, ml + sizeof(ml)) < (ssize_t)(ml + sizeof(ml))) {
146 /* short write -> rollback: truncate file to old size */
147 ftruncate(fd, st.st_size);
148 goto barf;
149 }
150 if (close(fd) < 0) goto barf;
151 free(m);
152 free(p);
153 return 0;
154barf:
155 ln_log(LNLOG_SERR, LNLOG_CTOP, "write error on file %s: %m", p);
156 close(fd);
157 free(m);
158 free(p);
159 return 1;
160}
161
162static void
163dogroup(/*@null@*/ struct newsgroup *g, const char *name, int expdays)
164{
165 char *gdir = NULL;
166 size_t s_gdir;
167 char *p;
168 char *q;
169 DIR *d;
170 struct dirent *de;
171 struct stat st;
172 unsigned long first, last, art, dupli = 0;
173 struct exp *articles;
174 int n;
175 int fd;
176 char *overview; /* xover: read then free */
177
178 int deleted, kept;
179
180 deleted = kept = 0;
181 clearidtree();
182
183 /* eliminate empty groups */
184 if (!chdirgroup(name, FALSE)) {
185 if (g) { g->first = g->last + 1; }
186 return;
187 }
188 if (!agetcwd(&gdir, &s_gdir)) {
189 ln_log(LNLOG_SERR, LNLOG_CGROUP, "getcwd: %m");
190 return;
191 }
192
193 /* find low-water and high-water marks */
194
195 d = opendir(".");
196 if (!d) {
197 ln_log(LNLOG_SERR, LNLOG_CGROUP, "opendir in %s: %m", gdir);
198 free(gdir);
199 return;
200 }
201
202 first = ULONG_MAX;
203 last = 0;
204 while ((de = readdir(d)) != 0) {
205 if (!isdigit((unsigned char)de->d_name[0]) ||
206 stat(de->d_name, &st) || !S_ISREG(st.st_mode))
207 continue;
208 art = strtoul(de->d_name, &p, 10);
209 if (p && !*p) {
210 if (art < first)
211 first = art;
212 if (art > last)
213 last = art;
214 }
215 }
216 closedir(d);
217
218 /* update overview info */
219 getxover();
220 freexover();
221
222 if (last < first) {
223 if (verbose > 1) printf("%s: empty group\n", name);
224 if (g) g->first = g->last + 1;
225 free(gdir);
226 return;
227 }
228
229 if (verbose > 1)
230 printf("%s: low water mark %lu, high water mark %lu\n",
231 name, first, last);
232 if (debugmode)
233 syslog(LOG_DEBUG,
234 "%s: expire %lu, low water mark %lu, high water mark %lu",
235 name, (unsigned long)expdays, first, last);
236
237 /* allocate and clear article array */
238 articles = (struct exp *)critmalloc((last - first + 1) * sizeof(struct exp),
239 "Reading articles to expire");
240 for (art = 0; art <= last - first; art++) {
241 articles[art].xover = NULL;
242 articles[art].kill = 0;
243 articles[art].exists = 0;
244 }
245
246 /* read in overview info, to be purged and written back */
247 overview = NULL;
248
249 if (stat(".overview", &st) == 0) {
250 overview = critmalloc(st.st_size + 1, "Reading article overview info");
251 if ((fd = open(".overview", O_RDONLY)) < 0 ||
252 ((off_t) read(fd, overview, st.st_size) < st.st_size)) {
253 ln_log(LNLOG_SERR, LNLOG_CGROUP, "can't open/read %s/.overview: %m", gdir);
254 *overview = '\0';
255 if (fd > -1)
256 close(fd);
257 } else {
258 close(fd);
259 overview[st.st_size] = '\0'; /* 0-terminate string */
260 }
261
262 p = overview;
263 while (p && *p) {
264 while (p && isspace((unsigned char)*p))
265 p++;
266 art = strtoul(p, NULL, 10);
267 if (art >= first && art <= last && !articles[art - first].xover) {
268 articles[art - first].xover = p;
269 articles[art - first].kill = 1;
270 }
271 p = strchr(p, '\n');
272 if (p) {
273 *p = '\0';
274 if (p[-1] == '\r')
275 p[-1] = '\0';
276 p++;
277 }
278 }
279 }
280
281 /* check the syntax of the .overview info, and delete all illegal stuff */
282 for (art = first; art <= last; art++) {
283 const char *x;
284
285 if (articles[art - first].xover &&
286 !legalxoverline(articles[art - first].xover, &x)) {
287 articles[art - first].xover = NULL;
288 }
289 }
290
291 /* insert articles in tree, and clear 'kill' for new or read articles */
292 d = opendir(".");
293 if (!d) {
294 ln_log(LNLOG_SERR, LNLOG_CGROUP, "opendir in %s: %m", gdir);
295 free(gdir);
296 free(articles);
297 return;
298 }
299 while ((de = readdir(d)) != 0) {
300 art = strtoul(de->d_name, &p, 10);
301 if (p && !*p && art <= last && art >= first) {
302 articles[art - first].exists = 1;
303 /* mark all articles as to-be-deleted and rescue those
304 * which fulfill certain criteria */
305 articles[art - first].kill = 1;
306 /* save file if it is a regular non-empty file
307 * and has no expire time */
308 if (stat(de->d_name, &st) == 0 &&
309 (S_ISREG(st.st_mode)) &&
310 (st.st_size != 0) &&
311 (expdays < 0
312 || (st.st_mtime > expdays)
313 || (use_atime && (st.st_atime > expdays)))) {
314 articles[art - first].kill = 0;
315 p = articles[art - first].xover;
316 for (n = 0; n < 4; n++)
317 if (p && (p = strchr(p + 1, '\t')))
318 p++;
319 q = p ? strchr(p, '\t') : NULL;
320 if (p && q) {
321 *q = '\0';
322 if (findmsgid(p)) { /* another file with same msgid? */
323 /* kill this article and keep the first to have
324 * that message-id */
325 articles[art - first].kill = 1;
327 "%s: removing duplicate article %lu %s",
328 name, art, p);
329 dupli++;
330 } else {
331 int relink = 0;
332 const char *t = lookup(p);
333
334 insertmsgid(p);
335
336 if (repair == 0) {
337 /* fast path, relink only if link is
338 * obviously missing (may not work for
339 * cross-posted articles) */
340 if (st.st_nlink < 2) {
341 relink = 1;
342 }
343 } else {
344 /* slow path, texpire -r => repair/relink
345 * mode, will check if newsgroup file is
346 * same as message.id file linked */
347 struct stat st2;
348 if (stat(t, &st2)
349 || st2.st_dev != st.st_dev
350 || st2.st_ino != st.st_ino) {
351 relink = 1;
352 }
353 }
354
355 if (relink) { /* repair fs damage */
356 if (link(de->d_name, t)
357 /* if EEXIST, link reverse
358 * rename first because it is atomic and
359 * guarantees the file de->d_name is
360 * always present. This file is precious.
361 * If we used unlink and link, a lone
362 * message.id/000 file would be deleted
363 * by expiremsgid()!
364 */
365 && (errno != EEXIST
366 || rename(t, de->d_name)
367 || link(de->d_name, t)))
368 {
370 "%s: relink of %s <-> %s failed: %s (%s)",
371 name, p, de->d_name, strerror(errno), t);
372 } else {
374 "%s: relinked message %s <-> %s", name, p, de->d_name);
375 }
376 }
377 *q = '\t';
378 }
379 } else if (articles[art - first].xover) {
380 /* data structure inconsistency: delete and be rid of it */
381 articles[art - first].kill = 1;
382 } else {
383 /* possibly read the xover line into memory? */
384 }
385 }
386 }
387 }
388 closedir(d);
389
390 /* compute new low-water mark */
391
392 art = first;
393 while (art <= last && articles[art - first].kill)
394 art++;
395 if (g) g->first = art;
396
397 /* remove old postings */
398
399 for (art = first; art <= last; art++) {
400 char artname[40]; /* must hold a decimal long + NUL */ /* RATS: ignore */
401 if (articles[art - first].exists) {
402 if (articles[art - first].kill) {
403 snprintf(artname, sizeof(artname), "%lu", art);
404 if (0 == unlink(artname)) {
405 if (debugmode)
406 syslog(LOG_DEBUG, "deleted article %s/%lu", gdir, art);
407 deleted++;
408 } else if (errno != ENOENT && errno != EEXIST) {
409 /* if file was deleted alredy or it was not a file */
410 /* but a directory, skip error message */
411 kept++;
412 ln_log(LNLOG_SERR, LNLOG_CGROUP, "unlink %s/%lu: %m", gdir, art);
413 } else {
414 /* deleted by someone else */
415 }
416 } else {
417 kept++;
418 }
419 }
420 }
421 free((char *)articles);
422 if (overview)
423 free(overview);
424
425 if (g && last > g->last) /* try to correct insane newsgroup info */
426 g->last = last;
427
428 if (!quiet)
429 printf("%s: %d article%s deleted (%lu duplicate%s), %d kept\n",
430 name, deleted, PLURAL(deleted), dupli, PLURAL(dupli), kept);
431 syslog(LOG_INFO,
432 "%s: %d article%s deleted (%lu duplicate%s), %d kept",
433 name, deleted, PLURAL(deleted), dupli, PLURAL(dupli), kept);
434
435 if (!kept) {
436 if (unlink(".overview") < 0)
437 ln_log(LNLOG_SERR, LNLOG_CGROUP, "unlink %s/.overview: %m", gdir);
438 if (!chdir("..") && (isinteresting(name) == 0)) {
439 /* delete directory and empty parent directories */
440 while (rmdir(gdir) == 0) {
441 if (!agetcwd(&gdir, &s_gdir)) {
442 ln_log(LNLOG_SERR, LNLOG_CGROUP, "getcwd: %m");
443 break;
444 }
445 chdir("..");
446 }
447 }
448 }
449 if (gdir)
450 free(gdir); /* previous loop may have freed *gdir */
451
452 /* write MIDSFILE */
453 if (!eflag)
455
456 clearidtree();
457}
458
459static void
461{
462 struct newsgroup *g;
463 struct stringlist *t, *l = get_grouplist();
464 int expdays;
465
466 if (!l) {
467 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot obtain group list\n");
468 return;
469 }
470
471 for(t = l; t; t = t -> next) {
472 char *x = t->string;
473
474 g = findgroup(x);
475 if ((expdays = lookup_expiredays(x)) >= 0) {
476 if (expdays == 0 || !(expdays = lookup_expire(x)))
477 expdays = default_expire;
478 } else {
479 expdays = -1;
480 if (verbose) {
481 printf("%s: never expires\n", x);
482 }
483 syslog(LOG_INFO, "%s: never expires", x);
484 }
485 dogroup(g, x, expdays);
486 }
487 freelist(l);
488}
489
490static void
491fixupgroup(/*@null@*/ struct newsgroup *g)
492{
493 for (/*nil*/ ; g && g->name; g++) {
494 if (!chdirgroup(g->name, FALSE))
495 g->first = g->last + 1;
496 }
497}
498
499static int
501{
502 int fd;
503 ssize_t l;
504 ssize_t r;
505 char *buf;
506 ssize_t bufsiz = 128;
507 int rc = 0;
508
509 fd = open(MIDSFILE, O_RDONLY);
510 if (fd < 0) {
511 if (errno != ENOENT) {
512 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot open \"%s\" file: %m",
513 MIDSFILE);
514 return 1;
515 }
516 return 0;
517 }
518
519 /* delete file early so we don't barf again and again if the file is
520 * corrupt */
522
523 buf = critmalloc(bufsiz, "readmids");
524
525 while((r = read(fd, &l, sizeof(l))) == (ssize_t)sizeof(l)) {
526 /* length obtained */
527 if (l+1 > bufsiz) {
528 free(buf);
529 bufsiz = l + 1;
530 buf = critmalloc(bufsiz, "readmids");
531 }
532 if ((r = read(fd, buf, l)) < l) {
533 /* short read */
534 rc = -1;
535 break;
536 }
537 buf[l] = '\0';
538 /* sanity check */
539 if (strlen(buf) != (size_t)l) {
540 rc = -1;
541 break;
542 }
543 insertmsgid(buf);
544 }
545 free(buf);
546 (void)close(fd);
547 if (rc)
548 ln_log(LNLOG_SERR, LNLOG_CTOP, "corrupt \"%s\" file", MIDSFILE);
549 if (r < 0) {
550 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot read \"%s\" file: %m", MIDSFILE);
551 rc = -1;
552 }
553 return rc;
554}
555
556/* returns 0 for success */
557static int
559{
560 int n, rc = 0;
561 mastr *s = mastr_new(256);
562
563 for (n = 0; n < 1000; n++) {
564 char buf[4];
565 snprintf(buf, sizeof(buf), "%03d", n); /* safe */
566 mastr_clear(s);
567 mastr_vcat(s, spooldir, "/message.id/", buf, "/", MIDSFILE, NULL);
568 if (log_unlink(mastr_str(s), 1))
569 rc = 1;
570 }
571 mastr_delete(s);
572 return rc;
573}
574
575static void
577{
578 int n, s_len;
579 DIR *d;
580 struct dirent *de;
581 struct stat st;
582 int deleted, kept;
583 const char *t;
584 int nomids = eflag;
585
586 deleted = kept = 0;
587
588 if (verbose)
589 puts("Expiring message.id...");
590
591 for (n = 0; n < 1000; n++) {
592 char s[SIZE_s+1];
593
594 s_len = xsnprintf(s, SIZE_s, "%s/message.id/%03d/", spooldir, n);
595 if (chdir(s)) {
596 if (errno == ENOENT)
597 mkdir(s, 0755); /* file system damage? */
598 if (chdir(s)) {
599 ln_log(LNLOG_SERR, LNLOG_CGROUP, "chdir %s: %m", s);
600 continue;
601 }
602 }
603
604 if (nomids == 0)
605 nomids |= readmids();
606 else
607 unlink(MIDSFILE); /* ignore errors */
608
609 d = opendir(".");
610 if (!d)
611 continue;
612 while ((de = readdir(d)) != 0) {
613 if (stat(de->d_name, &st) == 0 && S_ISREG(st.st_mode)) {
614 int ul = 0;
615 const char *reason = "";
616 if (st.st_nlink < 2) ul = 1, reason = "link count below 2";
617 if (!nomids && !findmsgid(de->d_name)) ul = 1, reason = "not seen in group scan";
618 if (ul) {
619 if (debugmode)
620 ln_log(LNLOG_SDEBUG, LNLOG_CARTICLE, "unlinking %03d/%s, %s",
621 n, de->d_name, reason);
622 if (0 == log_unlink(de->d_name, 1)
623 && de->d_name[0] == '<' /* only count MID files */)
624 deleted++;
625 } else {
626 kept++;
627 /* check hash */
628 t = lookup(de->d_name);
629 if (strncmp(t, s, s_len)) {
630 /* in wrong directory, move to the right one
631 * note however that if the right file is
632 * already present, we'll leave it in place,
633 * because it may have been relinked from a
634 * group directory and we don't want to break
635 * links again
636 */
637 if (link(de->d_name, t) && errno != EEXIST)
639 "rehash: cannot move %s%s to %s: %m",
640 s, de->d_name, t);
641 else {
642 char buf[4];
643 memcpy(buf, t + s_len - 4, 3);
644 buf[3] = '\0';
645
647 "rehashed %s from %03d to %s", de->d_name,
648 n, buf);
649 }
650 log_unlink(de->d_name, 0);
651 }
652 }
653 }
654 }
655 closedir(d);
656 clearidtree();
657 }
658
659 if (verbose)
660 puts("Done.");
661
662 if (!quiet)
663 printf("message.id/: %d article%s deleted, %d kept\n", deleted, PLURAL(deleted), kept);
664 syslog(LOG_INFO, "message.id/: %d article%s deleted, %d kept", deleted, PLURAL(deleted), kept);
665}
666
667
668int
669main(int argc, char **argv)
670{
671 int option;
672 int rc = 1;
673
674 myopenlog("texpire");
675 if (!initvars(argv[0]))
676 exit(1);
677
678 while ((option = getopt(argc, argv, "vfqhr")) != -1) {
679 switch(option) {
680 case 'v':
681 verbose++;
682 quiet = 0;
683 break;
684 case 'f':
685 use_atime = 0;
686 break;
687 case 'r':
688 repair = 1;
689 break;
690 case 'q':
691 quiet = 1;
692 verbose = 0;
693 break;
694 case 'h':
695 rc = 0;
696 /*FALLTHROUGH*/
697 default:
698 if (rc)
699 fprintf(stderr, "texpire: unknown option -%c.\n", optopt);
700 fprintf(stderr, "Usage: texpire {[-v[v[v[v]]]]|-q} [-f]\n"
701 " -q: be quiet (cancels -v)\n"
702 " -v: more verbose (cancels -q, may be repeated)\n"
703 " -f: force expire irrespective of access time\n");
704 exit(rc);
705 }
706 }
707
708 expire = 0;
709 expire_base = NULL;
710
711 if (!readconfig(0)) {
712 fprintf(stderr, "Reading configuration failed, exiting "
713 "(see syslog for more information).\n");
714 exit(2);
715 }
716 freeservers();
717
718 if (verbose || debugmode) {
719 printf("texpire %s: verbosity level %d, debugmode %d, %s\n", version,
721 use_atime ? "check mtime and atime" : "check mtime only");
722 }
723 syslog(LOG_INFO, "texpire %s: use_atime is %d, verbosity level %d, "
724 "debugmode %d", version, use_atime, verbose, debugmode);
725
726 if (try_lock(timeout_lock)) {
727 ln_log(LNLOG_SERR, LNLOG_CTOP, "Cannot obtain lock file, aborting.\n");
728 exit(1);
729 }
730
731 if (cleanmids()) {
732 ln_log(LNLOG_SERR, LNLOG_CTOP, "Cannot weed out MIDS files, aborting.\n");
733 unlink(lockfile);
734 exit(1);
735 }
736
737 readactive();
738 if (!active) {
739 ln_log(LNLOG_SWARNING, LNLOG_CTOP, "Reading active file failed. Trying to build my own.");
740 fakeactive();
741 }
742
743 if (expire == 0) {
744 fprintf(stderr, "%s: no expire time\n", argv[0]);
745 unlink(lockfile);
746 exit(2);
747 }
748
750
751 if (sigsetjmp(jmpbuffer, 1) == 0) {
752 /* if we can't catch either signal, don't care,
753 * it's just more work next time */
754 (void)mysigact(SIGINT, 0, sig_int, 0);
755 (void)mysigact(SIGTERM, 0, sig_int, 0);
756 expiregroup();
758 expiremsgid();
759 } else {
760 blocksig = 1;
762 "caught interrupt/termination signal, aborting gracefully.");
763 }
764 if (writeactive())
765 ln_log(LNLOG_SERR, LNLOG_CTOP, "error writing groupinfo.");
767 unlink(lockfile);
768 freeservers();
769 freexover();
770 freeconfig();
771 th(NULL);
772 return 0;
773}
void readactive(void)
Definition: activutil.c:373
void fakeactive(void)
Definition: activutil.c:515
void freeactive(struct newsgroup *act)
Definition: activutil.c:351
struct newsgroup * findgroup(const char *name)
Definition: activutil.c:260
int writeactive(void)
Definition: activutil.c:269
struct newsgroup * active
Definition: activutil.c:40
int agetcwd(char **buf, size_t *size)
Definition: agetcwd.c:10
time_t expire
Definition: configutil.c:58
unsigned long timeout_lock
Definition: configutil.c:92
void freeservers(void)
Definition: configutil.c:695
int debugmode
Definition: configutil.c:67
time_t lookup_expire(char *group)
Definition: configutil.c:735
int lookup_expiredays(char *group)
Definition: configutil.c:743
int readconfig(int logtostderr)
Definition: configutil.c:208
void freeconfig(void)
Definition: configutil.c:722
struct expire_entry * expire_base
Definition: configutil.c:60
char * critstrdup(const char *source, const char *message)
Definition: critmem.c:92
char * critmalloc(size_t size, const char *message)
Definition: critmem.c:61
static void barf(const char *func, size_t size, const char *message)
Definition: critmem.c:47
struct stringlist * get_grouplist(void)
Definition: grouplist.c:113
#define PLURAL(no)
Definition: leafnode.h:35
void freexover(void)
Definition: xoverutil.c:396
const char * spooldir
int log_unlink(const char *f, int ignore_enoent)
Definition: log_unlink.c:13
void freelist(struct stringlist *list)
Definition: miscutil.c:626
void myopenlog(const char *ident)
Definition: syslog.c:24
int initvars(char *progname)
Definition: miscutil.c:143
#define SIZE_s
Definition: leafnode.h:287
int findmsgid(const char *msgid)
Definition: miscutil.c:695
int legalxoverline(const char *xover, const char **errmsg)
Definition: xoverutil.c:206
int traverseidtree(tmihook h)
Definition: miscutil.c:747
const char * lookup(const char *msgid)
Definition: miscutil.c:306
void clearidtree(void)
Definition: miscutil.c:729
const char * version
#define snprintf
Definition: leafnode.h:74
#define FALSE
Definition: leafnode.h:32
const char * lockfile
void insertmsgid(const char *msgid)
Definition: miscutil.c:662
int isinteresting(const char *groupname)
Definition: miscutil.c:271
int xsnprintf(char *str, size_t n, const char *format,...)
Definition: miscutil.c:798
int try_lock(unsigned long)
Definition: lockfile.c:200
int chdirgroup(const char *group, int creatdir)
Definition: miscutil.c:446
int getxover(void)
Definition: xoverutil.c:415
void ln_log(int sev, int ctx, const char *format,...)
Definition: ln_log.c:103
#define LNLOG_SERR
Definition: ln_log.h:13
#define LNLOG_SWARNING
Definition: ln_log.h:14
#define LNLOG_SINFO
Definition: ln_log.h:16
#define LNLOG_CTOP
Definition: ln_log.h:22
#define LNLOG_CGROUP
Definition: ln_log.h:24
#define LNLOG_CARTICLE
Definition: ln_log.h:25
#define LNLOG_SNOTICE
Definition: ln_log.h:15
#define LNLOG_SDEBUG
Definition: ln_log.h:17
void mastr_delete(mastr *m)
Definition: mastring.c:223
mastr * mastr_new(size_t size)
Definition: mastring.c:62
int mastr_vcat(mastr *m,...)
Definition: mastring.c:147
void mastr_clear(mastr *m)
Definition: mastring.c:138
#define mastr_str(m)
Definition: mastring.h:57
const char * name
Definition: miscutil.c:126
int mysigact(int sig, int flags, void(*func)(int), int blockthis)
Definition: mysigact.c:9
Definition: texpire.c:60
int exists
Definition: texpire.c:63
int kill
Definition: texpire.c:62
char * xover
Definition: texpire.c:61
Definition: mastring.h:28
unsigned long first
Definition: leafnode.h:122
char * name
Definition: leafnode.h:124
unsigned long last
Definition: leafnode.h:123
struct stringlist * next
Definition: leafnode.h:166
char string[1]
Definition: leafnode.h:167
#define dirent
Definition: system.h:21
int verbose
Definition: texpire.c:49
static void sig_int(int signo)
Definition: texpire.c:70
static int cleanmids(void)
Definition: texpire.c:558
static const char *const MIDSFILE
Definition: texpire.c:58
static int readmids(void)
Definition: texpire.c:500
int main(int argc, char **argv)
Definition: texpire.c:669
static void expiregroup(void)
Definition: texpire.c:460
static int quiet
Definition: texpire.c:54
static time_t default_expire
Definition: texpire.c:47
static void dogroup(struct newsgroup *g, const char *name, int expdays)
Definition: texpire.c:163
static sigjmp_buf jmpbuffer
Definition: texpire.c:66
static int repair
Definition: texpire.c:51
static void fixupgroup(struct newsgroup *g)
Definition: texpire.c:491
static int eflag
Definition: texpire.c:56
int debug
Definition: texpire.c:50
static void expiremsgid(void)
Definition: texpire.c:576
static int use_atime
Definition: texpire.c:53
static int th(const char *mm)
Definition: texpire.c:82
static int blocksig
Definition: texpire.c:67
static int rc
Definition: xsnprintf.c:11