nfdump/bin/expire.c

817 lines
23 KiB
C

/*
* Copyright (c) 2016, Peter Haag
* Copyright (c) 2014, Peter Haag
* Copyright (c) 2009, Peter Haag
* Copyright (c) 2004-2008, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $Author: haag $
*
* $Id: expire.c 39 2009-11-25 08:11:15Z haag $
*
* $LastChangedRevision: 39 $
*
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#ifdef HAVE_FTS_H
# include <fts.h>
#else
# include "fts_compat.h"
#define fts_children fts_children_compat
#define fts_close fts_close_compat
#define fts_open fts_open_compat
#define fts_read fts_read_compat
#define fts_set fts_set_compat
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include "util.h"
#include "bookkeeper.h"
#include "nfstatfile.h"
#include "expire.h"
static uint32_t timeout = 0;
static void PrepareDirLists(channel_t *channel);
static int compare(const FTSENT **f1, const FTSENT **f2);
#if 0
#define unlink unlink_debug
static int unlink_debug (const char *path) {
printf("Unlink %s\n", path);
return 0;
} // End of unlink_debug
#endif
static void IntHandler(int signal) {
switch (signal) {
case SIGALRM:
timeout = 1;
break;
case SIGHUP:
case SIGINT:
case SIGTERM:
timeout = 1;
break;
break;
case SIGCHLD:
default:
// ignore everything we don't know
break;
}
} /* End of IntHandler */
static void SetupSignalHandler(void) {
struct sigaction act;
memset((void *)&act,0,sizeof(struct sigaction));
act.sa_handler = IntHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGALRM, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
} // End of SetupSignalHandler
uint64_t ParseSizeDef(char *s, uint64_t *value) {
char *p;
uint64_t fac;
int dot;
dot = 0;
*value = 0;
p = s;
while ( *p ) {
if ( *p < '0' || *p > '9' ) {
if ( *p == '.' ) {
if ( dot ) {
break;
} else {
dot = 1;
}
} else
break;
}
p++;
}
if ( p == s ) {
fprintf(stderr, "Missing number in '%s'\n", s);
return 0;
}
fac = 0;
switch (*p) {
case '\0':
case 'b':
case 'B':
fac = 1;
break;
case 'k':
case 'K':
fac = 1024;
break;
case 'm':
case 'M':
fac = 1024 * 1024;
break;
case 'g':
case 'G':
fac = 1024 * 1024 * 1024;
break;
case 't':
case 'T':
fac = 1024LL * 1024LL * 1024LL * 1024LL;
break;
default:
fprintf(stderr, "Garbage character(s) '%s' in '%s'\n", p, s);
return 0;
}
if ( *p ) {
char *r = p++;
// skip optional 'B' for Bytes in KB etc.
if ( fac != 1 && (*p == 'B' || *p == 'b') ) p++;
if ( *p ) {
// extra garbage after factor
fprintf(stderr, "Garbage character(s) '%s''in '%s'\n", p, s);
return 0;
}
*r = '\0';
}
// *value = strtoll(s, NULL, 10) * fac;
*value = (uint64_t)(atof(s) * (double)fac);
return 1;
} // End of ParseSizeDef
/*
* Accepted time scales
*
* w week
* d day
* H hour
* M minute
*/
uint64_t ParseTimeDef(char *s, uint64_t *value) {
char *p;
uint64_t weeks, days, hours, minutes;
*value = 0;
weeks=days=hours=minutes=0;
p = s;
while ( *p ) {
char *q = p;
while ( *p ) {
if ( *p < '0' || *p > '9' ) {
break;
}
p++;
}
if ( p == q ) {
fprintf(stderr, "Missing number before '%s'\n", q);
return 0;
}
switch (*p) {
case 'w':
*p++ = '\0';
if ( weeks ) {
fprintf(stderr, "Ambiguous weeks %sw\n", q);
return 0;
}
weeks = strtoll(q, NULL, 10);
break;
case 'd':
*p++ = '\0';
if ( days ) {
fprintf(stderr, "Ambiguous days %sD\n", q);
return 0;
}
days = strtoll(q, NULL, 10);
break;
case '\0': // without any units, assume hours
case 'H':
if ( *p ) *p++ = '\0';
if ( hours ) {
fprintf(stderr, "Ambiguous hours %sH\n", q);
return 0;
}
hours = strtoll(q, NULL, 10);
break;
case 'M':
*p++ = '\0';
if ( minutes ) {
fprintf(stderr, "Ambiguous minutes %sM\n", q);
return 0;
}
minutes = strtoll(q, NULL, 10);
break;
default:
fprintf(stderr, "Unknown time unit '%s'\n", q);
return 0;
}
}
*value = minutes*60 + hours*3600 + days*24*3600 + weeks*7*24*3600;
return 1;
} // End of ParseTimeDef
static int compare(const FTSENT **f1, const FTSENT **f2) {
return strcmp( (*f1)->fts_name, (*f2)->fts_name);
} // End of compare
void RescanDir(char *dir, dirstat_t *dirstat) {
FTS *fts;
FTSENT *ftsent;
char *const path[] = { dir, NULL };
char first_timestring[16], last_timestring[16];
dirstat->filesize = dirstat->numfiles = 0;
dirstat->first = 0;
dirstat->last = 0;
strncpy(first_timestring, "999999999999", 15);
strncpy(last_timestring, "000000000000", 15);
fts = fts_open(path, FTS_LOGICAL, compare);
if ( !fts ) {
LogError( "fts_open() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
return;
}
while ( (ftsent = fts_read(fts)) != NULL) {
if ( ftsent->fts_info == FTS_F && ftsent->fts_namelen == 19 ) {
// nfcapd.200604301200 strlen = 19
if ( strncmp(ftsent->fts_name, "nfcapd.", 7) == 0 ) {
char *s, *p = &(ftsent->fts_name[7]);
// make sure, we have only digits
s = p;
while ( *s ) {
if ( *s < '0' || *s > '9' )
break;
s++;
}
// otherwise skip
if ( *s )
continue;
if ( strcmp(p, first_timestring) < 0 ) {
first_timestring[0] = '\0';
strncat(first_timestring, p, 15);
}
if ( strcmp(p, last_timestring) > 0 ) {
last_timestring[0] = '\0';
strncat(last_timestring, p, 15);
}
dirstat->filesize += 512 * ftsent->fts_statp->st_blocks;
dirstat->numfiles++;
}
} else {
switch (ftsent->fts_info) {
case FTS_D:
// skip all '.' entries as well as hidden directories
if ( ftsent->fts_level > 0 && ftsent->fts_name[0] == '.' )
fts_set(fts, ftsent, FTS_SKIP);
// any valid dirctory need to start with a digit ( %Y -> year )
if ( ftsent->fts_level > 0 && !isdigit(ftsent->fts_name[0]) )
fts_set(fts, ftsent, FTS_SKIP);
break;
case FTS_DP:
break;
}
}
}
fts_close(fts);
// no files means do rebuild next time, otherwise the stat record may not be accurate
if ( dirstat->numfiles == 0 ) {
dirstat->first = dirstat->last = time(NULL);
dirstat->status = FORCE_REBUILD;
} else {
dirstat->first = ISO2UNIX(first_timestring);
dirstat->last = ISO2UNIX(last_timestring);
dirstat->status = STATFILE_OK;
}
} // End of RescanDir
void ExpireDir(char *dir, dirstat_t *dirstat, uint64_t maxsize, uint64_t maxlife, uint32_t runtime ) {
FTS *fts;
FTSENT *ftsent;
uint64_t sizelimit, num_expired;
int done, size_done, lifetime_done, dir_files;
char *const path[] = { dir, NULL };
char *expire_timelimit = NULL;
time_t now = time(NULL);
dir_files = 0;
if ( dirstat->low_water == 0 )
dirstat->low_water = 95;
if ( runtime ) {
SetupSignalHandler();
alarm(runtime);
}
if ( maxlife ) {
// build an appropriate string for comparing
time_t t_expire = now - maxlife;
time_t t_watermark = now - (time_t)((maxlife * dirstat->low_water)/100);
// printf("Expire files before %s", ctime(&t_expire));
expire_timelimit = strdup(UNIX2ISO(t_watermark));
// printf("down to %s", ctime(&t_watermark));
// printf("Diff: %i\n", t_watermark - t_expire );
if ( dirstat->last < t_expire && (isatty(STDIN_FILENO) ) ) {
// this means all files will get expired - are you sure ?
char *s, s1[32], s2[32];
time_t t;
struct tm *when;
t = t_expire;
when = localtime(&t);
strftime(s1, 31, "%Y-%m-%d %H:%M:%S", when);
s1[31] = '\0';
t = dirstat->last;
when = localtime(&t);
strftime(s2, 31, "%Y-%m-%d %H:%M:%S", when);
s2[31] = '\0';
printf("Your max lifetime of %s will expire all file before %s\n", ScaleTime(maxlife), s1);
printf("Your latest files are dated %s. This means all files will be deleted!\n", s2);
printf("Are you sure? yes/[no] ");
s = fgets(s1, 31, stdin);
s1[31] = '\0';
if ( s && strncasecmp(s1, "yes\n", 31) == 0 ) {
printf("Ok - you've beeen warned!\n");
} else {
printf("Expire canceled!\n");
return;
}
}
}
done = 0;
size_done = maxsize == 0 || dirstat->filesize < maxsize;
lifetime_done = maxlife == 0 || ( now - dirstat->first ) < maxlife;
sizelimit = (dirstat->low_water * maxsize)/100;
num_expired = 0;
fts = fts_open(path, FTS_LOGICAL, compare);
while ( !done && ((ftsent = fts_read(fts)) != NULL) ) {
if ( ftsent->fts_info == FTS_F ) {
dir_files++; // count files in directories
if ( ftsent->fts_namelen == 19 && strncmp(ftsent->fts_name, "nfcapd.", 7) == 0 ) {
// nfcapd.200604301200 strlen = 19
char *s, *p = &(ftsent->fts_name[7]);
// process only nfcapd. files
// make sure it's really an nfcapd. file and we have
// only digits in the rest of the file name
s = p;
while ( *s ) {
if ( *s < '0' || *s > '9' )
break;
s++;
}
// otherwise skip
if ( *s )
continue;
// expire size-wise if needed
if ( !size_done ) {
if ( dirstat->filesize > sizelimit ) {
if ( unlink(ftsent->fts_path) == 0 ) {
dirstat->filesize -= 512 * ftsent->fts_statp->st_blocks;
num_expired++;
dir_files--;
} else {
LogError( "unlink() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
continue; // next file if file was unlinked
} else {
dirstat->first = ISO2UNIX(p); // time of first file not expired
size_done = 1;
}
}
// expire time-wise if needed
// this part of the code is executed only when size-wise is fullfilled
if ( !lifetime_done ) {
if ( expire_timelimit && strcmp(p, expire_timelimit) < 0 ) {
if ( unlink(ftsent->fts_path) == 0 ) {
dirstat->filesize -= 512 * ftsent->fts_statp->st_blocks;
num_expired++;
dir_files--;
} else {
LogError( "unlink() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
lifetime_done = 0;
} else {
dirstat->first = ISO2UNIX(p); // time of first file not expired
lifetime_done = 1;
}
}
done = (size_done && lifetime_done) || timeout;
}
} else {
switch (ftsent->fts_info) {
case FTS_D:
// set pre-order flag
dir_files = 0;
// skip all '.' entries as well as hidden directories
if ( ftsent->fts_level > 0 && ftsent->fts_name[0] == '.' )
fts_set(fts, ftsent, FTS_SKIP);
// any valid directory needs to start with a digit ( %Y -> year )
if ( ftsent->fts_level > 0 && !isdigit(ftsent->fts_name[0]) )
fts_set(fts, ftsent, FTS_SKIP);
break;
case FTS_DP:
// do not delete base data directory ( level == 0 )
if ( dir_files == 0 && ftsent->fts_level > 0 ) {
// directory is empty and can be deleted
// printf("Will remove directory %s\n", ftsent->fts_path);
if ( rmdir(ftsent->fts_path) != 0 ) {
LogError( "rmdir() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
}
break;
}
}
}
fts_close(fts);
if ( !done ) {
// all files expired and limits not reached
// this may be possible, when files get time-wise expired and
// the time limit is shorter than the latest file
dirstat->first = dirstat->last;
}
if ( runtime )
alarm(0);
if ( timeout ) {
LogError( "Maximum execution time reached! Interrupt expire.\n");
}
if ( num_expired > dirstat->numfiles ) {
LogError( "Error updating stat record: Number of files inconsistent!\n");
LogError( "Will automatically rebuild this directory next time\n");
dirstat->numfiles = 0;
dirstat->status = FORCE_REBUILD;
} else {
dirstat->numfiles -= num_expired;
}
if ( dirstat->numfiles == 0 ) {
dirstat->first = dirstat->last = time(NULL);
dirstat->status = FORCE_REBUILD;
}
free(expire_timelimit);
} // End of ExpireDir
static void PrepareDirLists(channel_t *channel) {
channel_t *current_channel = channel;
while ( current_channel ) {
char *const path[] = { current_channel->datadir, NULL };
current_channel->fts = fts_open(path, FTS_LOGICAL|FTS_NOCHDIR, compare);
if ( !current_channel->fts ) {
LogError( "fts_open() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
continue;
}
// get first entry
current_channel->ftsent = fts_read(current_channel->fts);
if ( current_channel->ftsent )
// use fts_number as the number of files already seen in this directory.
current_channel->ftsent->fts_number = 0;
while ( current_channel->ftsent ) {
/*
FTSENT *ftsent = current_channel->ftsent;
char *finfo;
switch (ftsent->fts_info) {
case FTS_ERR:
case FTS_NS:
LogError( "fts_read() %s error in %s line %d: %s\n",
current_channel->ftsent->fts_path, __FILE__, __LINE__, strerror(current_channel->ftsent->fts_errno) );
break;
case FTS_D:
finfo = "DIR pre ";
break;
case FTS_DP:
finfo = "DIR post";
break;
case FTS_F:
finfo = "FILE ";
break;
default:
finfo = "<undef> ";
}
printf("%u %i %s %s %s\n", ftsent->fts_info, ftsent->fts_level, finfo, ftsent->fts_path, ftsent->fts_name);
*/
if ( current_channel->ftsent->fts_info == FTS_ERR ||
current_channel->ftsent->fts_info == FTS_NS) {
LogError( "fts_read() %s error in %s line %d: %s\n",
current_channel->ftsent->fts_path, __FILE__, __LINE__, strerror(current_channel->ftsent->fts_errno) );
continue;
}
if ( current_channel->ftsent->fts_info != FTS_F ) {
current_channel->ftsent = fts_read(current_channel->fts);
continue;
}
// it's now FTS_F
current_channel->ftsent->fts_number++;
// if ftsent points to first valid file, break
if ( current_channel->ftsent->fts_namelen == 19 && strncmp(current_channel->ftsent->fts_name, "nfcapd.", 7) == 0 )
break;
// otherwise loop
current_channel->ftsent = fts_read(current_channel->fts);
}
current_channel = current_channel->next;
}
} // End of PrepareDirLists
void ExpireProfile(channel_t *channel, dirstat_t *current_stat, uint64_t maxsize, uint64_t maxlife, uint32_t runtime ) {
int size_done, lifetime_done, done;
char *expire_timelimit = "";
time_t now = time(NULL);
uint64_t sizelimit, num_expired;
if ( !channel )
return;
done = 0;
SetupSignalHandler();
if ( maxlife ) {
// time_t t_expire = now - maxlife;
// build an appropriate string for comparing
time_t t_watermark = now - (time_t)((maxlife * current_stat->low_water)/100);
// printf("Expire files before %s", ctime(&t_expire));
expire_timelimit = strdup(UNIX2ISO(t_watermark));
// printf("down to %s", ctime(&t_watermark));
// printf("Diff: %i\n", t_watermark - t_expire );
}
size_done = maxsize == 0 || current_stat->filesize < maxsize;
sizelimit = (current_stat->low_water * maxsize)/100;
lifetime_done = maxlife == 0 || ( now - current_stat->first ) < maxlife;
num_expired = 0;
PrepareDirLists(channel);
if ( runtime )
alarm(runtime);
while ( !done ) {
char *p;
int file_removed;
// search for the channel with oldest file. If all channel have same age,
// get the last in the list
channel_t *expire_channel = channel;
channel_t *compare_channel = expire_channel->next;
while ( compare_channel ) {
if ( expire_channel->ftsent == NULL ) {
expire_channel = compare_channel;
}
if ( compare_channel->ftsent == NULL ) {
compare_channel = compare_channel->next;
continue;
}
// at this point expire_channel and current_channel fts entries are valid
if ( strcmp(expire_channel->ftsent->fts_name, compare_channel->ftsent->fts_name) >= 0 ) {
expire_channel = compare_channel;
}
compare_channel = compare_channel->next;
}
if ( !expire_channel->ftsent ) {
// no more entries in any channel - we are done
done = 1;
continue;
}
// flag is file got removed
file_removed = 0;
// expire_channel now points to the channel with oldest file
// do expire
p = &(expire_channel->ftsent->fts_name[7]);
// printf("File: %s\n", expire_channel->ftsent->fts_path);
if ( !size_done ) {
// expire size-wise if needed
// printf(" Size expire %llu %llu\n", current_stat->filesize, sizelimit);
if ( current_stat->filesize > sizelimit ) {
// need to delete this file
if ( unlink(expire_channel->ftsent->fts_path) == 0 ) {
// Update profile stat
current_stat->filesize -= 512 * expire_channel->ftsent->fts_statp->st_blocks;
current_stat->numfiles--;
// Update channel stat
expire_channel->dirstat->filesize -= 512 * expire_channel->ftsent->fts_statp->st_blocks;
expire_channel->dirstat->numfiles--;
// decrement number of files seen in this directory
expire_channel->ftsent->fts_number--;
file_removed = 1;
num_expired++;
} else {
LogError( "unlink() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
} else {
// we are done size-wise
// time of first file not expired = start time of channel/profile
expire_channel->dirstat->first = current_stat->first = ISO2UNIX(p);
size_done = 1;
}
} else if ( !lifetime_done ) {
// printf(" Time expire \n");
// expire time-wise if needed
// this part of the code is executed only when size-wise is already fullfilled
if ( strcmp(p, expire_timelimit) < 0 ) {
// need to delete this file
if ( unlink(expire_channel->ftsent->fts_path) == 0 ) {
// Update profile stat
current_stat->filesize -= 512 * expire_channel->ftsent->fts_statp->st_blocks;
current_stat->numfiles--;
// Update channel stat
expire_channel->dirstat->filesize -= 512 * expire_channel->ftsent->fts_statp->st_blocks;
expire_channel->dirstat->numfiles--;
// decrement number of files seen in this directory
expire_channel->ftsent->fts_number--;
file_removed = 1;
num_expired++;
} else {
LogError( "unlink() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
} else {
// we are done time-wise
// time of first file not expired = start time of channel/profile
expire_channel->dirstat->first = current_stat->first = ISO2UNIX(p);
lifetime_done = 1;
}
} else
// all done
done = 1;
if ( timeout )
done = 1;
// advance fts entry in expire channel to next file, if file was removed
if ( file_removed ) {
expire_channel->ftsent = fts_read(expire_channel->fts);
while ( expire_channel->ftsent ) {
if ( expire_channel->ftsent->fts_info == FTS_F ) { // entry is a file
expire_channel->ftsent->fts_number++;
if ( expire_channel->ftsent->fts_namelen == 19 &&
strncmp(expire_channel->ftsent->fts_name, "nfcapd.", 7) == 0 ) {
// if ftsent points to next valid file
char *p = &(expire_channel->ftsent->fts_name[7]);
// next file is first (oldest) for channel and for profile - update first mark
expire_channel->dirstat->first = current_stat->first = ISO2UNIX(p);
break;
}
} else {
switch (expire_channel->ftsent->fts_info) {
case FTS_D: // entry is a directory
// set number of files seen in this directory = 0
expire_channel->ftsent->fts_number = 0;
// skip all '.' entries as well as hidden directories
if ( expire_channel->ftsent->fts_level > 0 && expire_channel->ftsent->fts_name[0] == '.' )
fts_set(expire_channel->fts, expire_channel->ftsent, FTS_SKIP);
// any valid directory needs to start with a digit ( %Y -> year )
if ( expire_channel->ftsent->fts_level > 0 && !isdigit(expire_channel->ftsent->fts_name[0]) )
fts_set(expire_channel->fts, expire_channel->ftsent, FTS_SKIP);
break;
case FTS_DP:
// do not delete base data directory ( level == 0 )
if ( expire_channel->ftsent->fts_number == 0 && expire_channel->ftsent->fts_level > 0 ) {
// directory is empty and can be deleted
// printf("Will remove directory %s\n", expire_channel->ftsent->fts_path);
if ( rmdir(expire_channel->ftsent->fts_path) != 0 ) {
LogError( "rmdir() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) );
}
}
break;
}
}
// otherwise loop
expire_channel->ftsent = fts_read(expire_channel->fts);
} // end advance fts entry
file_removed = 0;
}
if ( expire_channel->ftsent == NULL ) {
// this channel has no more files now
expire_channel->dirstat->first = expire_channel->dirstat->last;
if ( expire_channel->dirstat->numfiles ) {
// if channel is empty, no files must be reported, but rebuild is done anyway
LogError( "Inconsitency detected in channel %s. Will rebuild automatically.\n", expire_channel->datadir);
LogError( "No more files found, but %llu expected.\n", expire_channel->dirstat->numfiles);
}
expire_channel->dirstat->numfiles = 0;
expire_channel->dirstat->status = FORCE_REBUILD;
}
} // while ( !done )
if ( runtime )
alarm(0);
if ( timeout ) {
LogError( "Maximum execution time reached! Interrupt expire.\n");
}
} // End of ExpireProfile
void UpdateBookStat(dirstat_t *dirstat, bookkeeper_t *books) {
if ( books->numfiles ) {
/* prevent some faults and dublicates:
* book records can never be timewise smaller than directory records => fishy!
* in case book records == directory records, the user stopped and restarted nfcapd
* this is not necessarily wrong, but results in overwriting an existing file
* which results in wrong stats => rescan needed
*/
if ( books->last <= dirstat->last || books->first <= dirstat->first) {
dirstat->status = FORCE_REBUILD;
return;
}
dirstat->last = books->last;
dirstat->numfiles += books->numfiles;
dirstat->filesize += books->filesize;
}
} // End of UpdateBookStat