src/expire.c

/* [<][>]
[^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. getFreeFS
  2. expire_arts
  3. expire

/* $Id: expire.c,v 1.6 2002/03/26 11:18:35 proff Exp $
 * $Copyright$
 */

#include "nglobal.h"
#include "filesystem.h"
#include "history.h"

#include "expire.h"

/* getFreeFS, get
 * ffree, free file nodes in fs
 * files, total file nodes in file system
 * bavail, free blocks avail to non-superuser
 * blocks, total data blocks in file system
 */
static bool getFreeFS (long *ffree, long *files, long *bavail, long *blocks)
/* [<][>][^][v][top][bottom][index][help] */
{
#ifdef HAVE_STATVFS
        struct statvfs fs;
        if (statvfs (".", &fs) < 0)
#else
#ifdef HAVE_STATFS
        struct statfs fs;
        if (statfs (".", &fs) < 0)
#else
#error no statfs type for this file system
#endif
#endif
        {
                loge (("unable to statfs() filesystem holding %s", con->cacheDir));
                return FALSE;
        }
        *ffree = fs.f_ffree;
        *files = fs.f_files;
        *bavail = fs.f_bavail;
        *blocks = fs.f_blocks;
        return TRUE;
}

static time_t ti, OldestArt;

/* 
 * this routine is fast. also does xover db's
 */

static int expire_arts (char *group, int depth)
/* [<][>][^][v][top][bottom][index][help] */
{
        DIR *dir;
        char *p;
        struct dirent *d;
        struct stat st;
        int hi = 0, lo = 0, artnum;
        struct newsgroup *n;
        struct strList *xover = NULL, *l;
#if 0
        bool mark = FALSE;
        int low=1; /* TODO fix me */
#endif
        if (!(dir = opendir (".")))
                return -1;
        settaskinfo("expiring");
        while ((d = readdir (dir)))
        {
                p = d->d_name;
                if (!p || !*p || *p == '.')
                        continue;
#if 0
                if ((*p >= '0' && *p <= '9') && mark)
                {
                        char *p2 = strstr (p, "_xover");
                        if (p2)
                                *p2 = '\0';
                        if ((artnum = strToi (p)))
                        {
                                if (artnum < ((p2) ? low - XOVER_INDEX_SIZE : low))
                                {
                                        if (p2)
                                        {
                                                *p2 = '_';
                                                unlink (p);
                                                Stats->expire_xovers++;
                                        } else
                                        {
                                                unlink (p);
                                                Stats->expire_articles++;
                                        }
                                }
                                continue;
                        }
                }
#endif
                if (lstat (p, &st) == -1)
                        continue;
                if ((*p >= '0' && *p <= '9') && S_ISREG (st.st_mode))
                {
                        time_t t = (st.st_mtime > st.st_atime) ? st.st_mtime : st.st_atime;
                        if (t < OldestArt)
                        {
                                if (strstr (p, "_xover"))
                                        xover = strListAdd(xover, p);
                                else
                                {
                                        if (unlink (p) == 0)
                                                Stats->articlesExpired++;
                                }
                        } else if (!strstr (p, "_xover"))
                        {
                                artnum = strToi(p);
                                if (!lo || artnum < lo)
                                        lo = artnum;
                                if (artnum > hi)
                                        hi = artnum;
                        }
                        continue;
                }
                if (S_ISDIR (st.st_mode))
                {
                        char *newgroup = NULL;
                        if (chdir (p) < 0)
                                continue;
                        if (depth == 1)
                                newgroup = Sstrdup(p);
                        else if (depth > 1)
                        {
                                newgroup = Smalloc (strlen(group) + strlen(p) + 2);
                                sprintf (newgroup, "%s.%s", group, p);
                        }
                        if (!expire_arts (newgroup, depth + 1))
                        {
                                if (newgroup)
                                        free(newgroup);
                                break;
                        }
                        if (newgroup)
                                free(newgroup);
                        if (chdir ("..") < 0)   /* I'm stuck in a klien bottle! */
                                break;
                        rmdir (p);
                }
        }
        closedir (dir);
        if (!d)
        {
                if (depth > 1 && (n = newsgroupFindAddLock(group, NULL, TRUE)))
                {
                        n->hi = hi;
                        n->lo = lo;
                        newsgroupUnlockWrite (n);
                }
                lo -= lo % XOVER_INDEX_SIZE;
                for (l = xover ? xover->head : NULL; l; l = l->next)
                {
                        p = strstr (l->data, "_xover");
                        *p = '\0';
                        if (strToi (l->data) < lo)
                        {
                                *p = '_';
                                if (unlink (l->data) == 0);
                                        Stats->xoversExpired++;
                        }
                }
        }
        if (xover)
                strListFree (xover);
        return (d ? FALSE : TRUE);
}

EXPORT int volatile ExpireDaemonPid;

EXPORT bool expire (bool force)
/* [<][>][^][v][top][bottom][index][help] */
{
    static time_t last_expire;
    long ffree = 0;
    long files = 0, bavail = 0, blocks = 0;
    if (!force && time (NULL) < last_expire + con->expireCheckPeriod)
        return TRUE;
    last_expire = time (NULL);
    if (!force)
        getFreeFS (&ffree, &files, &bavail, &blocks);
    if (blocks < 1)
        blocks = 1;
    if (files < 1)
        files = 1;
    if (ffree < 1)
        ffree = 1;
    if (bavail < 1)
        bavail = 1;

    /* Create Blocks Used and Inodes Used shortcuts */
    #define BU ((blocks - bavail) / (.01 * blocks ))
    #define IU ((files - ffree) / (.01 * files ))
    /* logen (("bu = %2.1f, iu = %2.1f", BU, IU)); */

    if (!(force || BU > con->minBlocksFreePercent || IU > con->minFilesFreePercent))
        return FALSE;
    ti = time (NULL);
    OldestArt = ti - con->maxArtAge;
    if (Task->ti_state == nc_master)
        {
            if (!ExpireDaemonPid)
                {
                    int pid;
                    sigset_t myset;
                    sigemptyset(&myset);
                    sigaddset(&myset, SIGCHLD);
                    sigprocmask (SIG_BLOCK, &myset, NULL);
                    pid = make_vm_proc (nc_expire, -1, "expire");
                    if (pid == 0)
                        {
                            sigprocmask (SIG_UNBLOCK, &myset, NULL);
                            if (chdir (con->cacheDir)==0)
                                {       
                                    time_t maxArtAge = con->maxArtAge;

                                    expire_arts (NULL, 0);
                                    getFreeFS(&ffree, &files, &bavail, &blocks);
                                    while ((BU > con->minBlocksFreePercent ||
                                            IU > con->minFilesFreePercent) &&
                                           maxArtAge > 60)
                                        {
                                            maxArtAge = (maxArtAge * 2) / 3;
                                            OldestArt = time (NULL) - maxArtAge;
                                            expire_arts (NULL, 0);
                                            getFreeFS(&ffree, &files, &bavail, &blocks);
                                        }
                                    logen (("usedblocks = %2.1f%%, usedinodes = %2.1f%%, maxArtAge = %ds, expire ended",
                                            BU, IU, (int)maxArtAge));
                                    retire_vm_proc (0);
                                }
                            else
                                {
                                    loge (("couldn't chdir(\"%s\") for expire", con->cacheDir));
                                    retire_vm_proc (1);
                                }
                            NOTREACHED;
                        }
                    if (pid > 0)
                    {
                        ExpireDaemonPid = pid;
                        sigprocmask (SIG_UNBLOCK, &myset, NULL);
                    }
                }
        }
    else
        {
            if (chdir (con->cacheDir) == 0)
                expire_arts (NULL, 0);
            else
                loge (("couldn't chdir(\"%s\") for expire", con->cacheDir));
        }
    chdir (con->cacheDir);
    hisPrune ();
    return TRUE;
}

/* [<][>][^][v][top][bottom][index][help] */