Back to index

courier  0.68.2
Defines | Functions
mail.c File Reference
#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>

Go to the source code of this file.

Defines

#define IDBUFSIZE   512

Functions

struct ll_mailll_mail_alloc (const char *filename)
static void getid (char *idbuf)
static int writeid (char *idbuf, int fd)
static int readid (char *p, int fd)
static pid_t getpidid (char *idbuf, char *myidbuf)
int ll_mail_lock (struct ll_mail *p)
static int try_dotlock (const char *tmpfile, const char *dotlock, char *idbuf)
static int try_mail_dotlock (const char *dotlock, char *idbuf)
static void dotlock_exists (const char *dotlock, char *myidbuf, int timeout)
static int ll_mail_open_do (struct ll_mail *p, int ro)
int ll_mail_open_ro (struct ll_mail *p)
int ll_mail_open (struct ll_mail *p)
void ll_mail_free (struct ll_mail *p)
int ll_dotlock (const char *dotlock, const char *tmpfile, int timeout)

Define Documentation

#define IDBUFSIZE   512

Definition at line 44 of file mail.c.


Function Documentation

static void dotlock_exists ( const char *  dotlock,
char *  myidbuf,
int  timeout 
) [static]

Definition at line 299 of file mail.c.

{
       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 (p == getpid() /* Possibly recycled PID */
                         || (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;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void getid ( char *  idbuf) [static]

Definition at line 50 of file mail.c.

{
       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");
}

Here is the call graph for this function:

Here is the caller graph for this function:

static pid_t getpidid ( char *  idbuf,
char *  myidbuf 
) [static]

Definition at line 103 of file mail.c.

{
       pid_t p=atol(idbuf);

       if ((idbuf=strchr(idbuf, ':')) == NULL ||
           (myidbuf=strchr(myidbuf, ':')) == NULL ||
           strcmp(idbuf, myidbuf))
              return 0;

       return p;
}

Here is the caller graph for this function:

int ll_dotlock ( const char *  dotlock,
const char *  tmpfile,
int  timeout 
)

Definition at line 487 of file mail.c.

{
       char myidbuf[IDBUFSIZE];

       getid(myidbuf);

       if (try_dotlock(tmpfile, dotlock, myidbuf))
       {
              if (errno == EEXIST)
                     dotlock_exists(dotlock, myidbuf, timeout);
              return -1;
       }
       return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

struct ll_mail* ll_mail_alloc ( const char *  filename) [read]

Definition at line 23 of file mail.c.

{
       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;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void ll_mail_free ( struct ll_mail p)

Definition at line 442 of file mail.c.

{
       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);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int ll_mail_lock ( struct ll_mail p)

Definition at line 115 of file mail.c.

{
       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);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int ll_mail_open ( struct ll_mail p)

Definition at line 437 of file mail.c.

{
       return ll_mail_open_do(p, 0);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int ll_mail_open_do ( struct ll_mail p,
int  ro 
) [static]

Definition at line 347 of file mail.c.

{
       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);
}

Here is the call graph for this function:

Here is the caller graph for this function:

int ll_mail_open_ro ( struct ll_mail p)

Definition at line 432 of file mail.c.

{
       return ll_mail_open_do(p, 1);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int readid ( char *  p,
int  fd 
) [static]

Definition at line 82 of file mail.c.

{
       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;
}

Here is the caller graph for this function:

static int try_dotlock ( const char *  tmpfile,
const char *  dotlock,
char *  idbuf 
) [static]

Definition at line 265 of file mail.c.

{
       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);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int try_mail_dotlock ( const char *  dotlock,
char *  idbuf 
) [static]

Definition at line 236 of file mail.c.

{
       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);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int writeid ( char *  idbuf,
int  fd 
) [static]

Definition at line 65 of file mail.c.

{
       int l=strlen(idbuf);

       while (l)
       {
              int n=write(fd, idbuf, l);

              if (n <= 0)
                     return (-1);

              l -= n;
              idbuf += n;
       }
       return 0;
}

Here is the caller graph for this function: