⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 db_lock.c

📁 一个C语言写的快速贝叶斯垃圾邮件过滤工具
💻 C
字号:
/** \file db_lock.c * \brief Lock handler to detect application crashes * \author Matthias Andree * \date 2004 * * GNU GPL v2 * with optimization ideas by Pavel Kankovsky * * \attention * This code uses signal handlers and must pay extra attention to * reentrancy! * * \par Lock file layout: * the lock file has a list of cells, which can be either 0 or 1. * - 0 means: slot is free * - 1 means: slot in use *   - 1 with fcntl lock: process running *   - 1 without lock: process quit prematurely, recovery needed * * \sa http://article.gmane.org/gmane.mail.bogofilter.devel/3240\n *     http://article.gmane.org/gmane.mail.bogofilter.devel/3260\n *     http://article.gmane.org/gmane.mail.bogofilter.devel/3270 */#include "common.h"#include <errno.h>#include <fcntl.h>#include <signal.h>#include <stdlib.h>#include "db_lock.h"#include "debug.h"#include "error.h"#include "globals.h"#include "mxcat.h"#include "system.h"#include "xmalloc.h"#include "xstrdup.h"/** figure out which flag we pass to open() to enforce synchronous data * I/O completion */#if HAVE_DECL_O_DSYNCstatic const int syncflag = O_DSYNC;#elif HAVE_DECL_O_SYNCstatic const int syncflag = O_SYNC;#elif HAVE_DECL_O_FSYNCstatic const int syncflag = O_FSYNC;#endif/** Type we use for a lock cell. */typedef char bf_cell_t;/** Periodic check interval in seconds, for set_lock(). */static const int chk_intval = 30;/** String to append to base directory, for process table file. */static const char aprt[] = DIRSEP_S "lockfile-p";/** Size of a cell, must match sizeof(bf_cell_t). */static const off_t cellsize = 1;/** Offset of our lock cell inside the lock file. */static off_t lockpos;/** Boolean marker to remember if we hold the lock. */static int locked;/** File descriptor of the open lock file, or -1 when not open. * The lock file descriptor must be long-lived because fcntl() or * lockf() will immediately release all locks held on a file once we * call close() for the file for the first time no matter how many file * descriptors to the file we still hold, so we cannot use * open-check-close cycles but need to keep the * descriptor open until we'll let go of the locks. */static int lockfd = -1;/** Constant cell content for cells that are in use. */static const bf_cell_t cell_inuse = '1';/** Constant cell content for cells that are \b not in use. */static const bf_cell_t cell_free = '0';/** Save area for previous SIGALRM signal handler. */static struct sigaction oldact;/** Convert the fcntl lock type to a string. * \return constant string to resolve the numeric \a locktype. */static const char *s_locktype(int locktype) {    /* reentrant */    switch (locktype) {	case F_UNLCK: return "F_UNLCK";	case F_RDLCK: return "F_RDLCK";	case F_WRLCK: return "F_WRLCK";	default:      return "UNKNOWN";    }}/** Checks if the cell in file \a fd at given \a offset is locked. * \return 1 if locked, 0 if unlocked, negative if error *//* part of the signal handler */static int check_celllock(int fd, off_t offset) {    /* reentrant */    struct flock fl;    int r;    /* check our own lock explicitly first, F_GETLK will return F_UNLCK     * for our own locks because we can demote, upgrade, reset, set our     * own locks at will, IOW, F_GETLK will only detect locks set by     * other processes */    if (offset == lockpos && locked)	return 1;    fl.l_type = F_WRLCK;    fl.l_whence = SEEK_SET;    fl.l_start = offset;    fl.l_len = cellsize;    r = fcntl(fd, F_GETLK, &fl);    if (r) {	/* cannot use fprintf for debugging here - isn't reentrant! */	return -1;    }    /* cannot use fprintf for debugging here - isn't reentrant! */    return fl.l_type == F_UNLCK ? 0 : 1;}/** Fast lock function, uses F_SETLK so does not wait. Not reentrant. */static int set_celllock(int fd, off_t offset, int locktype) {    struct flock fl;    int r;    fl.l_type = locktype;    fl.l_whence = SEEK_SET;    fl.l_start = offset;    fl.l_len = cellsize;    r = fcntl(fd, F_SETLK, &fl);    if (DEBUG_DATABASE(2))	fprintf(dbgout, "set_celllock(fd=%d, offset=%ld, type=%d (%s)) = %d%s%s\n",		fd, (long)offset, locktype, s_locktype(locktype),		r, r < 0 ? ", " : "", r < 0 ? strerror(errno) : "");    return r;}/** initialize the lock file which is presumed to exist and have the * file name \a fn, and which will be deleted in case of trouble. */static int init_lockfile(const char *fn) {    char b[1024];	/* XXX FIXME: make lock size configurable */    int rc = 0;    memset(b, (unsigned char)cell_free, sizeof(b)); /* XXX FIXME: only works for char */    if (lseek(lockfd, (off_t)0, SEEK_SET) != (off_t)0)	rc = -1;    if (rc	    || sizeof(b) != write(lockfd, b, sizeof(b))	    || fsync(lockfd))    {	close(lockfd);	lockfd = -1;	if (fn)	    unlink(fn);	return -1;    }    return 0;}/** Create a lock file with name \a fn and open modes \a modes to which * O_CREAT and O_EXCL are or'd. Will retry when a race was detected. * \return descriptor of open lock file for success, * -1 for error */static int create_lockfile(const char *fn, int modes) {    char *tmp = NULL;    int count=1;    do {	char buf[50];	snprintf(buf, sizeof(buf), ".%ld.%d", (long)getpid(), count++);	if (tmp) free(tmp);	tmp = mxcat(fn, buf, NULL);	lockfd = open(tmp, modes|O_CREAT|O_EXCL, DS_MODE); /* umask will decide about group writability */    } while (lockfd < 0 && errno == EEXIST);    if (lockfd >= 0) {	if (init_lockfile(tmp)) {	    free(tmp);	    return -1;	}	if (link(tmp, fn)) {	    int e = errno;	    close(lockfd);	    lockfd = -1;	    unlink(tmp);	    free(tmp);	    errno = e;	    return -1;	}	unlink(tmp);    }    return lockfd;}/** Open and possibly create the lock file. * This will do nothing and flag success if the file is open already * \return 0 for success, -1 for error. */static int open_lockfile(const char *bogohomedir) {    char *fn;    int modes = O_RDWR|syncflag;    if (lockfd >= 0) return 0;    fn = mxcat(bogohomedir, aprt, NULL);    do {	lockfd = open(fn, modes);	if (lockfd < 0 && errno == ENOENT)	    lockfd = create_lockfile(fn, modes);    } while (lockfd < 0 && errno == EEXIST);    if (lockfd < 0) {	print_error(__FILE__, __LINE__, "open_lockfile: open(%s): %s",		fn, strerror(errno));    } else {	if (DEBUG_DATABASE(1)) {	    fprintf(dbgout, "open_lockfile: open(%s) succeeded, fd #%d\n", fn, lockfd);	}    }    xfree(fn);    return (lockfd < 0) ? -1 : 0;}/** Close the lock file, releasing all locks. * \return is propagated from the underlying close() function. */static int close_lockfile(void) {    int r = 0;    if (lockfd >= 0) {	if (DEBUG_DATABASE(1))	    fprintf(dbgout, "close_lockfile\n");	r = close(lockfd);	lockfd = -1;	if (r) {	    int e = errno;	    print_error(__FILE__, __LINE__, "close_lockfile: close(%d) failed: %s",		    lockfd, strerror(errno));	    errno = e;	}    }    return r;}/** This function checks if any processes have previously crashed. * \return * - 0 - no zombies * - 1 - zombies * - 2 - file not found * - -1 - other error *//* part of the signal handler *//* reentrant */static int check_zombies(void) {    ssize_t r;    off_t pos, savepos;    bf_cell_t cell;    savepos = lseek(lockfd, 0, SEEK_CUR);    if (savepos < 0)	return -1;    if (lseek(lockfd, 0, SEEK_SET) < 0)	return -1;    for (;;) {	pos = lseek(lockfd, 0, SEEK_CUR);	r = read(lockfd, &cell, sizeof(cell));	if (r != sizeof(cell)) break;	if (cell == cell_inuse && 1 != check_celllock(lockfd, pos)) {	    if (lseek(lockfd, savepos, SEEK_SET) != savepos)		return -1;	    return 1;	}    }    if (lseek(lockfd, savepos, SEEK_SET) != savepos)	return -1;    return r == 0 ? 0 : -1;}/** Signal handler, checks if processes have crashed and if so, * writes an error message to STDERR_FILENO and calls _exit(). */static void check_lock(int unused) {    (void)unused;    if (0 != check_zombies()) {	const char *text = "bogofilter or related application has crashed or directory damaged, aborting.\n";	write(STDERR_FILENO, text, strlen(text));	_exit(EX_ERROR);	/* use _exit, not exit, to avoid running the atexit handler that might deadlock */    }    alarm(chk_intval);}/** Initialize signal handler and start the timer. \return 0 for * success, -1 for error */static int init_sig(void) {#ifdef	SA_RESTART    struct sigaction sa;    sigset_t ss;    sigemptyset(&ss);    sa.sa_handler = check_lock;    sa.sa_mask = ss;    sa.sa_flags = SA_RESTART;    if (sigaction(SIGALRM, &sa, &oldact)) return -1;    alarm(chk_intval);#endif    return 0;}/** Shut down the timer and restore the previous SIGALRM handler. * \return is propagated from sigaction(). */static int shut_sig(void) {    alarm(0);    return sigaction(SIGALRM, &oldact, NULL);}int set_lock(void) {    bf_cell_t cell;    ssize_t r;    if (lseek(lockfd, 0, SEEK_SET) < 0)	return -1;    for (;;) {	lockpos = lseek(lockfd, 0, SEEK_CUR);	r = read(lockfd, &cell, sizeof(cell));	if (r != sizeof(cell))	    return -1; /* XXX FIXME: retry? */	if (cell == cell_free && 0 == set_celllock(lockfd, lockpos, F_WRLCK)) {	    if (lseek(lockfd, lockpos, SEEK_SET) >= 0) {		r = read(lockfd, &cell, sizeof(cell));		if (r != sizeof(cell) || cell != cell_free) {		    /* found fresh zombie */		    set_celllock(lockfd, lockpos, F_UNLCK);		    return -2;		}		if (lseek(lockfd, lockpos, SEEK_SET) < 0)		    return -1;		if (cellsize != write(lockfd, &cell_inuse, cellsize))		    return -1;#if 0/* disabled for now, O_{D,,F}SYNC should handle this for us */#ifdef HAVE_FDATASYNC		if (fdatasync(lockfd))		    return -1;#else		if (fsync(lockfd))		    return -1;#endif#endif		init_sig();		locked = 1;		return 0;	    }	}    }}int clear_lock(void) {    shut_sig();    if (lseek(lockfd, lockpos, SEEK_SET) < 0)	return -1;    locked = 0;    if (cellsize != write(lockfd, &cell_free, cellsize))	return -1;    if (set_celllock(lockfd, lockpos, F_UNLCK))	return -1;    if (close_lockfile())	return -1;    return 0;}int init_dbl(const char *bogodir) {    return open_lockfile(bogodir) ? -1 : 0;}int needs_recovery(void) {    return 0 != check_zombies();}int clear_lockfile(void) {    if (init_lockfile(NULL))	return -1;    return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -