+++ /dev/null
-/*
-** Copyright 2006 Double Precision, Inc. See COPYING for
-** distribution information.
-*/
-
-#include "config.h"
-#include "liblock.h"
-#include "mail.h"
-#include "../numlib/numlib.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#if HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
-static const char rcsid[]="$Id: mail.c,v 1.10 2006/05/28 15:29:52 mrsam Exp $";
-
-struct ll_mail *ll_mail_alloc(const char *filename)
-{
- struct ll_mail *p=(struct ll_mail *)malloc(sizeof(struct ll_mail));
-
- if (!p)
- return NULL;
-
- if ((p->file=strdup(filename)) == NULL)
- {
- free(p);
- return NULL;
- }
-
- p->cclientfd= -1;
- p->cclientfile=NULL;
-
- p->dotlock=NULL;
-
- return p;
-}
-
-#define IDBUFSIZE 512
-
-/*
-** For extra credit, we mark our territory.
-*/
-
-static void getid(char *idbuf)
-{
- libmail_str_pid_t(getpid(), idbuf);
-
- while (*idbuf)
- idbuf++;
-
- *idbuf++=':';
-
- idbuf[IDBUFSIZE-NUMBUFSIZE-10]=0;
-
- if (gethostname(idbuf, IDBUFSIZE-NUMBUFSIZE-10) < 0)
- strcpy(idbuf, "localhost");
-}
-
-static int writeid(char *idbuf, int fd)
-{
- int l=strlen(idbuf);
-
- while (l)
- {
- int n=write(fd, idbuf, l);
-
- if (n <= 0)
- return (-1);
-
- l -= n;
- idbuf += n;
- }
- return 0;
-}
-
-static int readid(char *p, int fd)
-{
- int l=IDBUFSIZE-1;
-
- while (l)
- {
- int n=read(fd, p, l);
-
- if (n < 0)
- return (-1);
-
- if (n == 0)
- break;
-
- p += n;
- l -= n;
- }
- *p=0;
- return 0;
-}
-
-static pid_t getpidid(char *idbuf, char *myidbuf)
-{
- pid_t p=atol(idbuf);
-
- if ((idbuf=strchr(idbuf, ':')) == NULL ||
- (myidbuf=strchr(myidbuf, ':')) == NULL ||
- strcmp(idbuf, myidbuf))
- return 0;
-
- return p;
-}
-
-int ll_mail_lock(struct ll_mail *p)
-{
- struct stat stat_buf;
- char idbuf[IDBUFSIZE];
- char idbuf2[IDBUFSIZE];
-
- char fn[NUMBUFSIZE*2 + 20];
- char *f;
- int fd;
-
- getid(idbuf);
-
- if (p->cclientfd >= 0)
- return 0;
-
- if (stat(p->file, &stat_buf) < 0)
- return -1;
-
- if (snprintf(fn, sizeof(fn), "/tmp/.%lx.%lx",
- (unsigned long)stat_buf.st_dev,
- (unsigned long)stat_buf.st_ino) < 0)
- {
- errno=ENOSPC;
- return (-1);
- }
-
- if ((f=strdup(fn)) == NULL)
- return (-1);
-
- /* We do things a bit differently. First, try O_EXCL */
-
- if ((fd=open(f, O_RDWR|O_CREAT|O_EXCL, 0644)) >= 0)
- {
- struct stat stat_buf2;
-
- if (ll_lockfd(fd, ll_writelock, ll_whence_start, 0) < 0 ||
- fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
- writeid(idbuf, fd) < 0)
- {
- /* This shouldn't happen */
-
- close(fd);
- free(f);
- return (-1);
- }
-
- /* Rare race condition: */
-
- if (fstat(fd, &stat_buf) < 0 ||
- lstat(f, &stat_buf2) < 0 ||
- stat_buf.st_dev != stat_buf2.st_dev ||
- stat_buf.st_ino != stat_buf2.st_ino)
- {
- errno=EAGAIN;
- close(fd);
- free(f);
- return (-1);
- }
-
- p->cclientfd=fd;
- p->cclientfile=f;
- return 0;
- }
-
- /*
- ** An existing lockfile. See if it's tagged with another
- ** pid on this server, which no longer exists.
- */
-
- if ((fd=open(f, O_RDONLY)) >= 0)
- {
- pid_t p=-1;
-
- if (readid(idbuf2, fd) == 0 &&
- (p=getpidid(idbuf2, idbuf)) != 0 &&
- kill(p, 0) < 0 && errno == ESRCH)
- {
- errno=EAGAIN;
- close(fd);
- unlink(f); /* Don't try again right away */
- free(f);
- return (-1);
- }
-
- /* If we can't lock, someone must have it open, game over. */
-
- if (p == getpid() /* It's us! */
-
- || ll_lockfd(fd, ll_readlock, ll_whence_start, 0) < 0)
- {
- errno=EEXIST;
- close(fd);
- free(f);
- return (-1);
- }
-
- close(fd);
- }
-
- /* Stale 0-length lockfiles are blown away after 5 mins */
-
- if (lstat(f, &stat_buf) == 0 && stat_buf.st_size == 0 &&
- stat_buf.st_mtime + 300 < time(NULL))
- {
- errno=EAGAIN;
- unlink(f);
- free(f);
- return (-1);
- }
-
- errno=EAGAIN;
- free(f);
- return (-1);
-}
-
-/* Try to create a dot-lock */
-
-static int try_dotlock(const char *tmpfile,
- const char *dotlock,
- char *idbuf);
-
-static int try_mail_dotlock(const char *dotlock, char *idbuf)
-{
- char timebuf[NUMBUFSIZE];
- char pidbuf[NUMBUFSIZE];
- char *tmpname;
- int rc;
-
- libmail_str_time_t(time(NULL), timebuf);
- libmail_str_pid_t(getpid(), pidbuf);
-
- tmpname=malloc(strlen(dotlock) + strlen(timebuf) + strlen(pidbuf) +
- strlen(idbuf) + 10);
-
- if (!tmpname)
- return -1;
-
- strcpy(tmpname, dotlock);
- strcat(tmpname, ".");
- strcat(tmpname, timebuf);
- strcat(tmpname, ".");
- strcat(tmpname, pidbuf);
- strcat(tmpname, ".");
- strcat(tmpname, strchr(idbuf, ':')+1);
-
- rc=try_dotlock(tmpname, dotlock, idbuf);
- free(tmpname);
- return (rc);
-}
-
-static int try_dotlock(const char *tmpname,
- const char *dotlock,
- char *idbuf)
-{
- struct stat stat_buf;
-
- int fd;
-
- fd=open(tmpname, O_RDWR | O_CREAT, 0644);
-
- if (fd < 0)
- return (-1);
-
- if (writeid(idbuf, fd))
- {
- close(fd);
- unlink(tmpname);
- return (-1);
- }
- close(fd);
-
- if (link(tmpname, dotlock) < 0 || stat(tmpname, &stat_buf) ||
- stat_buf.st_nlink != 2)
- {
- if (errno != EEXIST)
- errno=EIO;
-
- unlink(tmpname);
- return (-1);
- }
- unlink(tmpname);
- return (0);
-}
-
-static void dotlock_exists(const char *dotlock, char *myidbuf,
- int timeout)
-{
- char idbuf[IDBUFSIZE];
- struct stat stat_buf;
- int fd;
-
- if ((fd=open(dotlock, O_RDONLY)) >= 0)
- {
- pid_t p;
-
- /*
- ** Where the locking process is on the same server,
- ** the decision is easy: does the process still exist,
- ** or not?
- */
-
- if (readid(idbuf, fd) == 0 && (p=getpidid(idbuf, myidbuf)))
- {
- if (kill(p, 0) < 0 && errno == ESRCH)
- {
- close(fd);
- if (unlink(dotlock) == 0)
- errno=EAGAIN;
- else
- errno=EEXIST;
- return;
- }
- }
- else if (timeout > 0 && fstat(fd, &stat_buf) >= 0 &&
- stat_buf.st_mtime < time(NULL) - timeout)
- {
- close(fd);
-
- if (unlink(dotlock) == 0)
- errno=EAGAIN;
- else
- errno=EEXIST;
- return;
- }
-
- close(fd);
- }
-
- errno=EEXIST;
-}
-
-static int ll_mail_open_do(struct ll_mail *p, int ro)
-{
- char *dotlock;
- char myidbuf[IDBUFSIZE];
- int save_errno;
- int fd;
-
- getid(myidbuf);
-
- if (p->dotlock) /* Already locked */
- {
- fd=open(p->file, ro ? O_RDONLY:O_RDWR);
-
- if (fd >= 0 &&
- (ll_lockfd(fd, ro ? ll_readlock:ll_writelock, 0, 0) < 0 ||
- fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
- {
- close(fd);
- fd= -1;
- }
- return fd;
- }
-
- if ((dotlock=malloc(strlen(p->file)+sizeof(".lock"))) == NULL)
- return -1;
-
- strcat(strcpy(dotlock, p->file), ".lock");
-
- if (try_mail_dotlock(dotlock, myidbuf) == 0)
- {
- fd=open(p->file, ro ? O_RDONLY:O_RDWR);
-
- if (fd >= 0 &&
- (ll_lockfd(fd, ro ? ll_readlock:ll_writelock, 0, 0) ||
- fcntl(fd, F_SETFD, FD_CLOEXEC) < 0))
- {
- close(fd);
- fd= -1;
- }
-
- p->dotlock=dotlock;
- return fd;
- }
-
- save_errno=errno;
-
- /*
- ** Last fallback: for EEXIST, a read-only lock should suffice
- ** In all other instances, we'll fallback to read/write or read-only
- ** flock as last resort.
- */
-
- if ((errno == EEXIST && ro) || errno == EPERM || errno == EACCES)
- {
- fd=open(p->file, ro ? O_RDONLY:O_RDWR);
-
- if (fd >= 0)
- {
- if (ll_lockfd(fd, ro ? ll_readlock:ll_writelock,
- 0, 0) == 0 &&
- fcntl(fd, F_SETFD, FD_CLOEXEC) == 0)
- {
- free(dotlock);
- return fd;
- }
- close(fd);
- }
- }
-
- /*
- ** If try_dotlock blew up for anything other than EEXIST, we don't
- ** know what the deal is, so punt.
- */
-
- if (save_errno != EEXIST)
- {
- free(dotlock);
- return (-1);
- }
-
- dotlock_exists(dotlock, myidbuf, 300);
- free(dotlock);
- return (-1);
-}
-
-int ll_mail_open_ro(struct ll_mail *p)
-{
- return ll_mail_open_do(p, 1);
-}
-
-int ll_mail_open(struct ll_mail *p)
-{
- return ll_mail_open_do(p, 0);
-}
-
-void ll_mail_free(struct ll_mail *p)
-{
- char myid[IDBUFSIZE];
- char idbuf[IDBUFSIZE];
-
- getid(myid);
-
- if (p->cclientfd >= 0)
- {
- if (lseek(p->cclientfd, 0L, SEEK_SET) == 0 &&
- readid(idbuf, p->cclientfd) == 0 &&
- strcmp(myid, idbuf) == 0)
- {
- if (ftruncate(p->cclientfd, 0) >= 0)
- unlink(p->cclientfile);
- }
- close(p->cclientfd);
- free(p->cclientfile);
- }
-
- if (p->dotlock)
- {
- int fd=open(p->dotlock, O_RDONLY);
-
- if (fd >= 0)
- {
- if (readid(idbuf, fd) == 0 &&
- strcmp(myid, idbuf) == 0)
- {
- close(fd);
- unlink(p->dotlock);
- free(p->dotlock);
- free(p->file);
- free(p);
- return;
- }
- close(fd);
- }
-
- free(p->dotlock);
- }
- free(p->file);
- free(p);
-}
-
-int ll_dotlock(const char *dotlock, const char *tmpfile,
- int timeout)
-{
- char myidbuf[IDBUFSIZE];
-
- getid(myidbuf);
-
- if (try_dotlock(tmpfile, dotlock, myidbuf))
- {
- if (errno == EEXIST)
- dotlock_exists(dotlock, myidbuf, timeout);
- return -1;
- }
- return 0;
-}
-
-