/* $Id: lock.c,v 1.6 2006/07/20 00:41:04 dm Exp $ */
/*
*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1