"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/texpire.c" (28 Dec 2021, 18738 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2 texpire -- expire old articles
    3 
    4 Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
    5 Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
    6 22646949.
    7 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
    8 and Randolf Skerka <Randolf.Skerka@gmx.de>.
    9 Copyright of the modifications 1997.
   10 Modified by Kent Robotti <robotti@erols.com>. Copyright of the
   11 modifications 1998.
   12 Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
   13 Copyright of the modifications 1998.
   14 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
   15 Copyright of the modifications 1998, 1999.
   16 Modified by Kazushi (Jam) Marukawa <jam@pobox.com>.
   17 Copyright of the modifications 1998, 1999.
   18 Modified by Matthias Andree <matthias.andree@gmx.de>.
   19 Copyright of the modifications 2000 - 2010.
   20 
   21 See 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 
   47 static time_t default_expire;
   48 
   49 int verbose = 0;
   50 int debug = 0;
   51 static int repair = 0;          /* run expensive checks */
   52 
   53 static int use_atime = 1;       /* look for atime on articles to expire */
   54 static int quiet = 0;           /* shut up */
   55 
   56 static int eflag;           /* set to 1 if "mids" file based expiry must not take place */
   57 
   58 static const char *const MIDSFILE = "mids";
   59 
   60 struct exp {
   61     char *xover;    /* full xover info */
   62     int kill;
   63     int exists;
   64 };
   65 
   66 static sigjmp_buf jmpbuffer;
   67 static int blocksig;
   68 
   69 static void
   70 sig_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 */
   81 static int
   82 th(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;
  154 barf:
  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 
  162 static void
  163 dogroup(/*@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;
  326             ln_log(LNLOG_SINFO, LNLOG_CARTICLE,
  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                 {
  369                     ln_log(LNLOG_SERR, LNLOG_CGROUP,
  370                        "%s: relink of %s <-> %s failed: %s (%s)",
  371                        name, p, de->d_name, strerror(errno), t);
  372                 } else {
  373                 ln_log(LNLOG_SINFO, LNLOG_CARTICLE,
  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)
  454     eflag |= traverseidtree(th);
  455 
  456     clearidtree();
  457 }
  458 
  459 static void
  460 expiregroup(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 
  490 static void
  491 fixupgroup(/*@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 
  499 static int
  500 readmids(void)
  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 */
  521     log_unlink(MIDSFILE, 0);
  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 */
  557 static int
  558 cleanmids(void)
  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 
  575 static void
  576 expiremsgid(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)
  638                 ln_log(LNLOG_SERR, LNLOG_CARTICLE,
  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 
  646                 ln_log(LNLOG_SINFO, LNLOG_CARTICLE,
  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 
  668 int
  669 main(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,
  720         verbose, debugmode,
  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 
  749     default_expire = expire;
  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();
  757     fixupgroup(active);
  758     expiremsgid();
  759     } else {
  760     blocksig = 1;
  761     ln_log(LNLOG_SNOTICE, LNLOG_CTOP,
  762         "caught interrupt/termination signal, aborting gracefully.");
  763     }
  764     if (writeactive())
  765     ln_log(LNLOG_SERR, LNLOG_CTOP, "error writing groupinfo.");
  766     freeactive(active);
  767     unlink(lockfile);
  768     freeservers();
  769     freexover();
  770     freeconfig();
  771     th(NULL);
  772     return 0;
  773 }