/* GNU mailutils - a suite of utilities for electronic mail Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOCKFILE_ATTR 0444 /* First draft by Brian Edmond. */ struct _locker { int fd; int refcnt; char *fname; int flags; }; int locker_create (locker_t *plocker, const char *filename, size_t len, int flags) { locker_t l; if (plocker == NULL || filename == NULL || len == 0) return EINVAL; l = malloc (sizeof (*l)); if (l == NULL) return ENOMEM; l->fname = calloc (len + 5 /*strlen(".lock")*/ + 1, sizeof (*(l->fname))); if (l->fname == NULL) { free (l); return ENOMEM; } memcpy (l->fname, filename, len); strcat (l->fname, ".lock"); if (flags) l->flags = flags; else l->flags = MU_LOCKER_TIME; /* Default is time lock implementation. */ l->fd = -1; l->refcnt = 0; *plocker = l; return 0; } void locker_destroy (locker_t *plocker) { if (plocker && *plocker) { free ((*plocker)->fname); free (*plocker); *plocker = NULL; } } int locker_lock (locker_t lock, int flags) { int fd = -1; char buf[16]; pid_t pid; int removed = 0; (void)flags; /* Ignore for now. */ if (lock == NULL) return EINVAL; /* Is the lock already applied? FIXME: should we check flags != lock->flags ?? */ if (lock->fd != -1) { lock->refcnt++; return 0; } /* Check for lock existance: if it exists but the process is gone the lock can be removed, if the lock is expired, remove it. */ fd = open (lock->fname, O_RDONLY); if (fd != -1) { /* Check to see if this process is still running. */ if (lock->flags & MU_LOCKER_PID) { int nread = read (fd, buf, sizeof (buf) - 1); if (nread > 0) { buf[nread] = '\0'; pid = strtol (buf, NULL, 10); if (pid > 0) { /* Process is gone so we try to remove the lock. */ if (kill (pid, 0) == -1) removed = 1; } else removed = 1; /* Corrupted file, remove the lock. */ } } /* Check to see if the lock expired. */ if (lock->flags & MU_LOCKER_TIME) { struct stat stbuf; fstat (fd, &stbuf); /* The lock has expired. */ if ((time (NULL) - stbuf.st_mtime) > MU_LOCKER_EXPIRE_TIME) removed = 1; } close (fd); if (removed) unlink (lock->fname); } /* Try to create the lockfile. */ fd = open (lock->fname, O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); if (fd == -1) return errno; else { struct stat fn_stat; struct stat fd_stat; if (lstat (lock->fname, &fn_stat) || fstat(fd, &fd_stat) || fn_stat.st_nlink != 1 || fn_stat.st_dev != fd_stat.st_dev || fn_stat.st_ino != fd_stat.st_ino || fn_stat.st_uid != fd_stat.st_uid || fn_stat.st_gid != fd_stat.st_gid) { close (fd); unlink (lock->fname); return EPERM; } } /* Success. */ sprintf (buf, "%ld", (long)getpid ()); write (fd, buf, strlen (buf)); /* Try to get a file lock. */ if (lock->flags & MU_LOCKER_FCNTL) { struct flock fl; memset (&fl, 0, sizeof (struct flock)); fl.l_type = F_WRLCK; if (fcntl (fd, F_SETLK, &fl) == -1) { int err = errno; /* Could not get the file lock. */ close (fd); unlink (lock->fname); /* Remove the file I created. */ return err; } } lock->fd = fd; lock->refcnt++; return 0; } int locker_touchlock (locker_t lock) { if (!lock || !lock->fname || lock->fd == -1) return EINVAL; return utime (lock->fname, NULL); } int locker_unlock (locker_t lock) { if (!lock || !lock->fname || lock->fd == -1 || lock->refcnt <= 0) return EINVAL; if (--lock->refcnt > 0) return 0; if (lock->flags & MU_LOCKER_FCNTL) { struct flock fl; memset (&fl, 0, sizeof (struct flock)); fl.l_type = F_UNLCK; /* Unlock failed? */ if (fcntl (lock->fd, F_SETLK, &fl) == -1) return errno; } close (lock->fd); lock->fd = -1; unlink (lock->fname); return 0; }