Logo Search packages:      
Sourcecode: mailavenger version File versions  Download package

lock.c

/* $Id$ */

/*
 *
 * Copyright (C) 2004 David Mazieres (dm@uun.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include "local.h"

int opt_noflock;
int opt_fcntl;
int opt_nowait;

int
tmperr (int err)
{
  switch (err) {
  case EIO:
  case ESTALE:
  case EAGAIN:
    return 1;
  default:
    return 0;
  }
}

int
stat_unchanged (const struct stat *sb1, const struct stat *sb2)
{
  return sb1->st_mtime == sb2->st_mtime
    && sb1->st_ctime == sb2->st_ctime
    && sb1->st_dev == sb2->st_dev
    && sb1->st_ino == sb2->st_ino
    && sb1->st_size == sb2->st_size;
}

int
dotlock (int *fdp, char **lockfilep, const char *path, int *lfdp)
{
  static const char lockext[] = ".lock";
  char *lockfile = NULL;
  int dfd;
  int ntries;
  struct stat msb, lsb;
  int lock_ex = LOCK_EX;
  int f_setlk = F_SETLKW;

  if (opt_nowait) {
    lock_ex |= LOCK_NB;
    f_setlk = F_SETLK;
  }

  bzero (&msb, sizeof (msb));
  bzero (&lsb, sizeof (lsb));

  lockfile = xmalloc (strlen (path) + sizeof (lockext));
  strcpy (lockfile, path);
  strcat (lockfile, lockext);

  for (ntries = 1;; ntries++) {
    int lfd;
    struct stat tsb;

    if ((dfd = open (path, O_CREAT|O_WRONLY|O_APPEND, 0666)) < 0) {
      perror (path);
      free (lockfile);
      return EX_CANTCREAT;
    }
    if (!opt_noflock && flock (dfd, lock_ex)) {
      perror (path);
      close (dfd);
      free (lockfile);
      return EX_OSERR;
    }

    if (opt_fcntl) {
      struct flock larg;
      bzero (&larg, sizeof (larg));
      larg.l_start = 0;
      larg.l_len = 0;
      larg.l_type = F_WRLCK;
      larg.l_whence = SEEK_SET;

      if (opt_noflock && fcntl (dfd, f_setlk, &larg) < 0) {
      close (dfd);
      free (lockfile);
      return EX_OSERR;
      }
      /* Can't make blocking fcntl while holding flock, as other
       * programs might reverse the order of lockf/flock. */
      else if (!opt_noflock && fcntl (dfd, F_SETLK, &larg) < 0) {
      int rv = -1;
      if (opt_nowait || errno != EAGAIN
          || flock (dfd, LOCK_UN)
          || fcntl (dfd, f_setlk, &larg)
          || ((rv = flock (dfd, LOCK_EX|LOCK_NB)) && errno != EWOULDBLOCK)) {
        close (dfd);
        free (lockfile);
        return EX_OSERR;
      }
      if (rv < 0) {
        close (dfd);
        ntries = 0;
        continue;
      }
      }
    }

    if ((lfd = open (lockfile, O_CREAT|O_EXCL|O_WRONLY, 0666)) >= 0) {
      if (lfdp)
      *lfdp = lfd;
      else
      close (lfd);
      break;
    }
    if (errno != EEXIST) {
      perror (lockfile);
      close (dfd);
      free (lockfile);
      return tmperr (errno) ? EX_OSERR : EX_CANTCREAT;
    }

    if (stat (lockfile, &tsb))
      ntries = 0;
    else if (!stat_unchanged (&lsb, &tsb)) {
      lsb = tsb;
      ntries = 0;
    }
    else if (fstat (dfd, &tsb)) {
      close (dfd);
      free (lockfile);
      return EX_OSERR;
    }
    else if (!stat_unchanged (&msb, &tsb)) {
      msb = tsb;
      ntries = 0;
    }
    else if (ntries >= 3)
      unlink (lockfile);

    close (dfd);
    sleep (2 << ntries);
  }

  *lockfilep = lockfile;
  *fdp = dfd;
  return 0;
}


Generated by  Doxygen 1.6.0   Back to index