"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/activutil.c" (28 Dec 2021, 13756 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. For more information about "activutil.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.11.12_vs_1.12.0.

    1 /*
    2  libutil -- deal with active file
    3 
    4  Written by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
    5  Copyright 2000.
    6  Reused some old code written by Arnt Gulbrandsen <agulbra@troll.no>,
    7  copyright 1995, modified by (amongst others) Cornelius Krasel
    8  <krasel@wpxx02.toxi.uni-wuerzburg.de>, Randolf Skerka
    9  <Randolf.Skerka@gmx.de>, Kent Robotti <robotti@erols.com> and
   10  Markus Enzenberger <enz@cip.physik.uni-muenchen.de>. Copyright
   11  for the modifications 1997-1999.
   12 
   13  Modified and copyright of the modifications by:
   14  2001 - 2010 Matthias Andree <matthias.andree@gmx.de>
   15  2002 Ralf Wildenhues <ralf.wildenhues@gmx.de>
   16 
   17  See file COPYING for restrictions on the use of this software.
   18 */
   19 
   20 #include "leafnode.h"
   21 #include "activutil.h"
   22 #include "ln_log.h"
   23 #include "mastring.h"
   24 
   25 #include <ctype.h>
   26 #include "system.h"
   27 #include <limits.h>
   28 #include <stdio.h>
   29 #include <string.h>
   30 #include <sys/stat.h>
   31 #include <stdlib.h>
   32 #include <string.h>
   33 #include <unistd.h>
   34 
   35 #ifdef CHECKGROUPORDER
   36 #include "ln_log.h"
   37 
   38 #endif /* CHECKGROUPORDER */
   39 size_t activesize;
   40 struct newsgroup *active = NULL;
   41 size_t oldactivesize;
   42 struct newsgroup *oldactive = NULL;
   43 
   44 struct nglist {
   45     struct newsgroup *entry;
   46     struct nglist *next;
   47 };
   48 
   49 /* warning: this function does not do a deep copy: it does not copy
   50  * name or description */
   51 void
   52 newsgroup_copy(struct newsgroup *d, const struct newsgroup *s)
   53 {
   54     d->first = s->first;
   55     d->last  = s->last;
   56     d->age   = s->age;
   57     d->name  = s->name;
   58     d->desc  = s->desc;
   59 }
   60 
   61 int
   62 compactive(const void *a, const void *b)
   63 {
   64     const struct newsgroup *la = (const struct newsgroup *)a;
   65     const struct newsgroup *lb = (const struct newsgroup *)b;
   66 
   67     return strcasecmp(la->name, lb->name);
   68 }
   69 
   70 static struct nglist *newgroup = NULL;
   71 
   72 /*
   73  * insert a group into a list of groups
   74  * if oldactive is set, keep old data of known groups
   75  */
   76 void
   77 insertgroup(const char *name, long unsigned first,
   78         long unsigned last, time_t age)
   79 {
   80     struct nglist *l;
   81     static struct nglist *lold;
   82     struct newsgroup *g, *o;
   83     char *desc = NULL;
   84 
   85     g = findgroup(name);
   86     if (g)
   87     return;
   88 
   89     if (*name == '.' || strstr(name, "..") || name[strlen(name)-1] == '.') {
   90     ln_log(LNLOG_SWARNING, LNLOG_CTOP, "Warning: skipping group \"%s\", "
   91         "invalid name (NULL component).", name);
   92     return;
   93     }
   94 
   95     if (oldactivesize != 0 && oldactive != NULL) {
   96         o = xfindgroup(oldactive, name, oldactivesize);
   97         if (o) {
   98             last = o->last;
   99             first = o->first;
  100             if (o->age) age = o->age;
  101             if (o->desc) desc = critstrdup(o->desc, "insertgroup");
  102         }
  103     }
  104     
  105     g = (struct newsgroup *)critmalloc(sizeof(struct newsgroup),
  106                        "Allocating space for new group");
  107     g->name = critstrdup(name, "insertgroup");
  108     g->first = first;
  109     g->last = last;
  110     g->age = age;
  111     g->desc = desc;
  112     l = (struct nglist *)critmalloc(sizeof(struct nglist),
  113                     "Allocating space for newsgroup list");
  114     l->entry = g;
  115     l->next = NULL;
  116     if (newgroup == NULL)
  117     newgroup = l;
  118     else
  119     lold->next = l;
  120     lold = l;
  121 }
  122 
  123 void
  124 newgroupdesc(const char *groupname, const char *description)
  125 {
  126     struct nglist *l = newgroup;
  127 
  128     while(l) {
  129     if (0 == strcasecmp(groupname, l->entry->name)) {
  130         if (l->entry->desc)
  131         free(l->entry->desc);
  132         l->entry->desc = critstrdup(description, "newgroupdesc");
  133         break;
  134     }
  135     l = l->next;
  136     }
  137 }
  138 
  139 
  140 /*
  141  * change description of newsgroup
  142  */
  143 void
  144 changegroupdesc(const char *groupname, const char *description)
  145 {
  146     struct newsgroup *ng;
  147 
  148     if (groupname && description) {
  149     ng = findgroup(groupname);
  150     if (ng) {
  151         if (ng->desc)
  152         free(ng->desc);
  153         ng->desc = critstrdup(description, "changegroupdesc");
  154     }
  155     }
  156 }
  157 
  158 /*
  159  * merge nglist with active group, then free it
  160  */
  161 void
  162 mergegroups(void)
  163 {
  164     struct nglist *l, *la;
  165     size_t count = 0;
  166 
  167 #ifdef CHECKGROUPORDER
  168     checkgrouporder();
  169 #endif /* CHECKGROUPORDER */
  170     l = newgroup;
  171     while (l) {
  172     count++;
  173     l = l->next;
  174     }
  175 
  176     active = (struct newsgroup *)critrealloc((char *)active,
  177                          (1 + count +
  178                           activesize) *
  179                          sizeof(struct newsgroup),
  180                          "reallocating active");
  181 
  182     l = newgroup;
  183     count = activesize;
  184     while (l) {
  185     la = l;
  186     newsgroup_copy(active + count, l->entry);
  187     l = l->next;
  188     count++;
  189     free(la->entry);
  190     free(la);       /* clean up */
  191     }
  192     newgroup = NULL;
  193     active[count].name = NULL;
  194 
  195     activesize = count;
  196     qsort(active, activesize, sizeof(struct newsgroup), &compactive);
  197     validateactive();
  198 }
  199 
  200 #ifdef CHECKGROUPORDER
  201 void checkgrouporder(void) {
  202     unsigned long i;
  203     int s = 1;
  204 
  205     for (i = 1; i < activesize; i++) {
  206     if (compactive(&active[i-1], &active[i]) >= 0) {
  207         ln_log(LNLOG_SERR, LNLOG_CTOP, "in-core active file misorder at pos. %lu: \"%s\" vs. \"%s\"", i-1, active[i-1].name, active[i].name);
  208         break;
  209         s = 0;
  210     }
  211     }
  212 }
  213 #endif /* CHECKGROUPORDER */
  214 
  215 /*
  216  * finds a group by name
  217  * expects the group list to be sorted in strcasecmp order
  218  * does a binary search, recursively implemented
  219  */
  220 static long
  221 helpfindgroup(struct newsgroup *act, const char *name, long low, long high)
  222 {
  223     int result;
  224     long newp;
  225 
  226     if (low > high)
  227     return -1;
  228     newp = (high - low) / 2 + low;
  229     result = strcasecmp(name, act[newp].name);
  230     if (result == 0)
  231     return newp;
  232     else if (result < 0)
  233     return helpfindgroup(act, name, low, newp - 1);
  234     else
  235     return helpfindgroup(act, name, newp + 1, high);
  236 }
  237 
  238 /*
  239  * find a newsgroup in the active file
  240  */
  241 struct newsgroup *
  242 xfindgroup(struct newsgroup *act, const char *name, unsigned long actsize)
  243 {
  244     long i;
  245 
  246     if (actsize > (unsigned long)LONG_MAX) {
  247     syslog(LOG_CRIT, "xfindgroup: count %lu too large (max. %ld), aborting",
  248         actsize, LONG_MAX);
  249     abort();
  250     }
  251 
  252     i = helpfindgroup(act, name, 0, actsize - 1);
  253     if (i < 0)
  254     return NULL;
  255     else
  256     return (&act[i]);
  257 }
  258 
  259 struct newsgroup *
  260 findgroup(const char *name) {
  261 #ifdef CHECKGROUPORDER
  262     checkgrouporder();
  263 #endif /* CHECKGROUPORDER */
  264     return xfindgroup(active, name, activesize);
  265 }
  266 
  267 /* write active file, returns 0 for success, -1 for failure */
  268 int
  269 writeactive(void)
  270 {
  271     FILE *a;
  272     struct newsgroup *g;
  273     mastr *c = mastr_new(PATH_MAX), *d;
  274     int err;
  275     size_t count = 0;
  276 
  277     mastr_vcat(c, spooldir, "/leaf.node/groupinfo.new", NULL);
  278     (void)unlink(mastr_str(c));
  279     a = fopen(mastr_str(c), "w");
  280     if (!a) {
  281     ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot open new groupinfo file \"%s\": %m", mastr_str(c));
  282     mastr_delete(c);
  283     return -1;
  284     }
  285 
  286     /* count members in array and sort it */
  287     g = active;
  288     err = 0;
  289     if (g) {
  290     while (g->name) {
  291         count++;
  292         g++;
  293     }
  294     qsort(active, count, sizeof(struct newsgroup), &compactive);
  295     validateactive();
  296 
  297     g = active;
  298     while ((err != EOF) && g->name) {
  299         if (strlen(g->name)) {
  300         err = fputs(g->name, a);
  301         if (err == EOF) break;
  302         if (fprintf(a, " %lu %lu %lu ", g->last, g->first,
  303             (unsigned long)g->age) < 0) {
  304             err = EOF;
  305             break;
  306         }
  307         if (err == EOF) break;
  308         err = fputs(g->desc && *(g->desc) ? g->desc : "-x-", a);
  309         if (err == EOF) break;
  310         err = fputs("\n", a);
  311         if (err == EOF) break;
  312         }
  313         g++;
  314     }
  315     }
  316 
  317     if (fflush(a) || fsync(fileno(a)) || fclose(a))
  318         err = EOF;
  319 
  320     if (err == EOF) {
  321     ln_log(LNLOG_SERR, LNLOG_CTOP,
  322         "failed writing new groupinfo file \"%s\": %m", mastr_str(c));
  323     unlink(mastr_str(c));
  324     mastr_delete(c);
  325     return -1;
  326     }
  327 
  328     d = mastr_new(PATH_MAX);
  329     mastr_vcat(d, spooldir, "/leaf.node/groupinfo", NULL);
  330     if (rename(mastr_str(c), mastr_str(d))) {
  331     ln_log(LNLOG_SERR, LNLOG_CTOP, "failed to rename new groupinfo \"%s\" file into proper place \"%s\": %m", mastr_str(c), mastr_str(d));
  332     unlink(mastr_str(c));
  333     mastr_delete(d);
  334     mastr_delete(c);
  335     return -1;
  336     } else {
  337     if (verbose) printf("wrote active file with %lu line%s\n",
  338         (unsigned long)count, PLURAL(count));
  339     syslog(LOG_INFO, "wrote active file with %lu line%s",
  340            (unsigned long)count, PLURAL(count));
  341     }
  342     mastr_delete(d);
  343     mastr_delete(c);
  344     return 0;
  345 }
  346 
  347 /*
  348  * free active list. Written by Lloyd Zusman
  349  */
  350 void
  351 freeactive(struct newsgroup *act)
  352 {
  353     struct newsgroup *g;
  354 
  355     if (act == NULL)
  356     return;
  357 
  358     g = act;
  359     while (g->name) {
  360     free(g->name);
  361     if (g->desc)
  362         free(g->desc);
  363     g++;
  364     }
  365 
  366     free(act);
  367 }
  368 
  369 /*
  370  * read active file into memory
  371  */
  372 void
  373 readactive(void)
  374 {
  375     char *buf;
  376     /*@dependent@*/ char *p, *q, *r;
  377     unsigned long lu;
  378     off_t n;
  379     struct stat st;
  380     FILE *f;
  381     /*@dependent@*/ struct newsgroup *g;
  382     char s[SIZE_s+1];
  383 
  384     if (active) {
  385     freeactive(active);
  386     active = NULL;
  387     }
  388 
  389     xsnprintf(s, SIZE_s, "%s/leaf.node/groupinfo", spooldir);
  390     if ((f = fopen(s, "r")) != NULL) {
  391         if (fstat(fileno(f), &st)) {
  392             syslog(LOG_ERR, "can't stat %s: %m", s);
  393             (void)fclose(f);
  394             return;
  395         }
  396         if (!S_ISREG(st.st_mode)) {
  397             syslog(LOG_ERR, "%s not a regular file", s);
  398             (void)fclose(f);
  399             return;
  400         }
  401         buf = critmalloc(st.st_size + 2, "Reading group info");
  402         n = fread(buf, 1, st.st_size, f);
  403         if ((off_t) n < st.st_size) {
  404             syslog(LOG_ERR,
  405                     "Groupinfo file truncated while reading: %ld < %ld.",
  406                     (long)n, (long)st.st_size);
  407         }
  408         fclose(f);
  409     } else {
  410         int e = errno;
  411         syslog(LOG_ERR, "unable to open %s: %m", s);
  412         if (e == ENOENT)
  413         return;
  414         fprintf(stderr, "unable to open %s: %s, aborting", s, strerror(e));
  415         exit(1);
  416     }
  417 
  418     n = ((off_t) n > st.st_size) ? st.st_size : (off_t) n;
  419     /* to read truncated groupinfo files correctly */
  420     buf[n] = '\n';
  421     buf[n + 1] = '\0';      /* 0-terminate string */
  422 
  423     /* delete spurious 0-bytes */
  424     while ((p = (char *)memchr(buf, '\0', st.st_size)) != NULL)
  425     *p = ' ';       /* \n might be better, but produces more errors */
  426 
  427     /* count lines = newsgroups */
  428     activesize = 0;
  429     p = buf;
  430     while (p && *p && ((q = (char *)memchr(p, '\n', st.st_size)) != NULL)) {
  431     activesize++;
  432     p = q + 1;
  433     }
  434 
  435     active = (struct newsgroup *)critmalloc((1 + activesize) *
  436                         sizeof(struct newsgroup),
  437                         "allocating active");
  438     g = active;
  439 
  440     p = buf;
  441     while (p && *p) {
  442     q = strchr(p, '\n');
  443     if (q) {
  444         *q = '\0';
  445         if (strlen(p) == 0) {
  446         p = q + 1;
  447         continue;   /* skip blank lines */
  448         }
  449     }
  450     r = strchr(p, ' ');
  451     if (!q || !r) {
  452         if (!q && r)
  453         *r = '\0';
  454         else if (q && !r)
  455         *q = '\0';
  456         else if (strlen(p) > 30) {
  457         q = p + 30;
  458         *q = '\0';
  459         }
  460         syslog(LOG_ERR,
  461            "Groupinfo file possibly truncated or damaged: %s", p);
  462         break;
  463     }
  464     *r++ = '\0';
  465     *q++ = '\0';
  466 
  467     g->name = critstrdup(p, "readactive");
  468     if (sscanf(r, "%lu %lu %lu", &g->last, &g->first, &lu) != 3) {
  469         syslog(LOG_ERR,
  470            "Groupinfo file possibly truncated or damaged: %s", p);
  471         break;
  472     }
  473     g->age = lu;
  474     if (g->first == 0)
  475         g->first = 1;   /* pseudoarticle */
  476     if (g->last == 0)
  477         g->last = 1;
  478 
  479     for (n = 0; n < 3; n++) {   /* Skip the numbers */
  480         r = strchr(r, ' ') + 1;
  481     }
  482     if (strcmp(r, "-x-") == 0)
  483         g->desc = NULL;
  484     else
  485         g->desc = critstrdup(r, "readactive");
  486 
  487     p = q;          /* next record */
  488     g++;
  489     }
  490     free(buf);
  491     /* last record, to mark end of array */
  492     g->name = NULL;
  493     g->first = 0;
  494     g->last = 0;
  495     g->age = 0;
  496     g->desc = NULL;
  497 
  498     /* count member in the array - maybe there were some empty lines */
  499     g = active;
  500     activesize = 0;
  501     while (g->name) {
  502     g++;
  503     activesize++;
  504     }
  505 
  506     /* sort the thing, just to be sure */
  507     qsort(active, activesize, sizeof(struct newsgroup), &compactive);
  508     validateactive();
  509 }
  510 
  511 /*
  512  * fake active file if it cannot be retrieved from the server
  513  */
  514 void
  515 fakeactive(void)
  516 {
  517     DIR *d;
  518     struct dirent *de;
  519     DIR *ng;
  520     struct dirent *nga;
  521     long unsigned int i;
  522     long unsigned first, last;
  523     char *p;
  524     char s[SIZE_s+1];
  525     struct stat st;
  526     time_t age;
  527 
  528     killactiveread(); /* force reading active file regardless */
  529     xsnprintf(s, SIZE_s, "%s/interesting.groups", spooldir);
  530     d = opendir(s);
  531     if (!d) {
  532     syslog(LOG_ERR, "cannot open directory %s: %m", s);
  533     return;
  534     }
  535 
  536     while ((de = readdir(d))) {
  537     if (isalnum((unsigned char)*(de->d_name)) &&
  538         chdirgroup(de->d_name, FALSE)) {
  539         /* get first and last article from the directory. This is
  540          * the most secure way to get to that information since the
  541          * .overview files may be corrupt as well
  542          * If the group doesn't exist, just ignore the active entry.
  543          */
  544 
  545         first = ULONG_MAX;
  546         last = 0;
  547 
  548         ng = opendir(".");
  549         while ((nga = readdir(ng)) != NULL) {
  550         if (isdigit((unsigned char)*(nga->d_name))) {
  551             p = NULL;
  552             i = strtoul(nga->d_name, &p, 10);
  553             if (*p == '\0') {
  554             if (i < first)
  555                 first = i;
  556             if (i > last)
  557                 last = i;
  558             }
  559         }
  560         }
  561         if (first > last) {
  562         first = 1;
  563         last = 1;
  564         }
  565         closedir(ng);
  566         if (debugmode)
  567         syslog(LOG_DEBUG, "parsed directory %s: first %lu, last %lu",
  568                de->d_name, first, last);
  569         if (0 == stat(".", &st))
  570         age = st.st_ctime;
  571         else
  572         age = 0;
  573         insertgroup(de->d_name, first, last, age);
  574     }
  575     }
  576     mergegroups();
  577 
  578     closedir(d);
  579 }
  580 
  581 char *
  582 activeread(void)
  583 {
  584     const char *a = "/active.read";
  585     size_t l;
  586     char *t = critmalloc((l = strlen(spooldir)) + strlen(a) + 1, "activeread");
  587     strcpy(t, spooldir); /* RATS: ignore */
  588     strcpy(t + l, a); /* RATS: ignore */
  589     return t;
  590 }
  591 
  592 /* Set a mark that the active file needs to be refetched (as though
  593  * fetchnews -f had been used) next time, by removing active.read file */
  594 int
  595 killactiveread(void)
  596 {
  597     int rc = 0;
  598     char *t = activeread();
  599     if (unlink(t) && errno != ENOENT) {
  600     ln_log(LNLOG_SERR, LNLOG_CTOP,
  601         "cannot delete %s: %m", t);
  602     rc = -1;
  603     }
  604     free(t);
  605     return rc;
  606 }