2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amflock.c,v 1.17.4.6.6.1 2003/10/22 17:43:33 martinea Exp $
29 * file locking routines, put here to hide the system dependant stuff
30 * from the rest of the code
35 ** - These are "best effort" routines.
36 ** - "configure" has four variables that are used to determine which type of
38 ** USE_POSIX_FCNTL - use fcntl(). The full job.
39 ** USE_FLOCK - use flock(). Does just as well.
40 ** USE_LOCKF - use lockf(). Only handles advisory, exclusive,
41 ** blocking file locks as used by Amanda.
42 ** USE_LNLOCK - Home brew exclusive, blocking file lock.
43 ** <none> - No locking available. User beware!
44 ** - "configure" compiles this with -DCONFIGURE_TEST to try and determine
45 ** whether a particular type of locking works.
50 #if defined(USE_POSIX_FCNTL)
51 static struct flock lock; /* zero-initialized */
54 #if !defined(USE_POSIX_FCNTL) && defined(USE_FLOCK)
55 # ifdef HAVE_SYS_FILE_H
56 # include <sys/file.h>
59 # if !defined(HAVE_FLOCK_DECL) && !defined(CONFIGURE_TEST)
60 extern int flock P((int fd, int operation));
65 #if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && defined(USE_LOCKF)
67 /* XPG4-UNIX (eg, SGI IRIX, DEC DU) has F_ULOCK instead of F_UNLOCK */
68 #if defined(F_ULOCK) && !defined(F_UNLOCK)
69 # define F_UNLOCK F_ULOCK
72 /* Lock a file using lockf().
74 ** - returns errors for some non-files like pipes.
75 ** - probably only works for files open for writing.
78 int fd; /* fd of file to operate on */
79 int op; /* true to lock; false to unlock */
84 /* lock from here on */
85 if (lockf(fd, F_LOCK, (off_t)0) == -1) return -1;
88 /* unlock from here on */
89 if (lockf(fd, F_UNLOCK, (off_t)0) == -1) return -1;
91 /* unlock from bof to here */
92 pos = lseek(fd, (off_t)0, SEEK_CUR);
93 if (pos == (off_t)-1) {
94 if (errno == ESPIPE) pos = (off_t)0;
99 lockf(fd, F_UNLOCK, -pos) == -1) return -1;
107 #if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && !defined(USE_LOCKF) && defined(USE_LNLOCK)
108 /* XXX - error checking in this section needs to be tightened up */
110 /* Delete a lock file.
118 if (rc != 0 && errno == ENOENT) rc = 0;
123 /* Create a lock file.
125 int create_lock(fn, pid)
133 (void)delete_lock(fn); /* that's MY file! */
136 fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0640);
138 if (fd == -1) return -1;
140 if((f = fdopen(fd, "w")) == NULL) {
144 fprintf(f, "%ld\n", pid);
145 if (fclose(f) == EOF)
150 /* Read the pid out of a lock file.
151 ** -1=error, otherwise pid.
154 char *fn; /* name of lock file */
160 if ((f = fopen(fn, "r")) == NULL) {
163 if (fscanf(f, "%ld", &pid) != 1) {
169 if (fclose(f) != 0) {
175 /* Link a lock if we can.
176 ** 0=done, 1=already locked, -1=error.
178 int link_lock(lk, tlk)
179 char *lk; /* real lock file */
180 char *tlk; /* temp lock file */
183 int serrno; /* saved errno */
184 struct stat lkstat, tlkstat;
186 /* an atomic check and set operation */
188 if (rc == 0) return 0; /* XXX do we trust it? */
190 /* link() says it failed - don't beleive it */
193 if (stat(lk, &lkstat) == 0 &&
194 stat(tlk, &tlkstat) == 0 &&
195 lkstat.st_ino == tlkstat.st_ino)
196 return 0; /* it did work! */
200 if (errno == EEXIST) rc = 1;
205 /* Steal a lock if we can.
206 ** 0=done; 1=still in use; -1 = error.
208 int steal_lock(fn, mypid, sres)
209 char *fn; /* name of lock file to steal */
210 long mypid; /* my process id */
211 char *sres; /* name of steal-resource to lock */
218 /* prevent a race with another stealer */
219 rc = ln_lock(sres, 1);
220 if (rc != 0) goto error;
224 if (errno == ENOENT) goto done;
228 if (pid == mypid) goto steal; /* i'm the locker! */
230 /* are they still there ? */
231 rc = kill((pid_t)pid, 0);
233 if (errno == ESRCH) goto steal; /* locker has gone */
238 rc = ln_lock(sres, 0);
239 if (rc != 0) goto error;
244 rc = delete_lock(fn);
245 if (rc != 0) goto error;
248 rc = ln_lock(sres, 0);
249 if (rc != 0) goto error;
254 rc = ln_lock(sres, 0);
259 /* Locking using existance of a file.
262 char *res; /* name of resource to lock */
263 int op; /* true to lock; false to unlock */
266 char *lockfile = NULL;
267 char *tlockfile = NULL;
270 char pid_str[NUM_STR_SIZE];
272 mypid = (long)getpid();
274 lockfile = vstralloc(AMANDA_TMPDIR, "/am", res, ".lock", NULL);
277 /* unlock the resource */
278 assert(read_lock(lockfile) == mypid);
280 (void)delete_lock(lockfile);
285 /* lock the resource */
287 ap_snprintf(pid_str, sizeof(pid_str), "%ld", mypid);
288 tlockfile = vstralloc(AMANDA_TMPDIR, "am", res, ".", pid_str, NULL);
290 (void)create_lock(tlockfile, mypid);
292 mres = stralloc2(res, ".");
295 rc = link_lock(lockfile, tlockfile);
299 rc = steal_lock(lockfile, mypid, mres);
301 if (rc == 0) continue;
305 (void) delete_lock(tlockfile);
316 /* Get a file lock (for read-only files).
318 int amroflock(fd, resource)
324 #ifdef USE_POSIX_FCNTL
325 lock.l_type = F_RDLCK;
326 lock.l_whence = SEEK_SET;
327 r = fcntl(fd, F_SETLKW, &lock);
329 r = amflock(fd, resource);
336 /* Get a file lock (for read/write files).
338 int amflock(fd, resource)
344 #ifdef USE_POSIX_FCNTL
345 lock.l_type = F_WRLCK;
346 lock.l_whence = SEEK_SET;
347 r = fcntl(fd, F_SETLKW, &lock);
350 r = flock(fd, LOCK_EX);
353 r = use_lockf(fd, 1);
356 r = ln_lock(resource, 1);
368 /* Release a file lock.
370 int amfunlock(fd, resource)
376 #ifdef USE_POSIX_FCNTL
377 lock.l_type = F_UNLCK;
378 lock.l_whence = SEEK_SET;
379 r = fcntl(fd, F_SETLK, &lock);
382 r = flock(fd, LOCK_UN);
385 r = use_lockf(fd, 0);
388 r = ln_lock(resource, 0);
400 /* Test routine for use by configure.
401 ** (I'm not sure why we use both return and exit!)
402 ** XXX the testing here should be a lot more comprehensive.
403 ** - lock the file and then try and lock it from another process
404 ** - lock the file from another process and check that process
405 ** termination unlocks it.
406 ** The hard part is to find a system independent way to not block
409 #ifdef CONFIGURE_TEST
413 char *filen = "/tmp/conftest.lock";
417 for(fd = 3; fd < FD_SETSIZE; fd++) {
419 * Make sure nobody spoofs us with a lot of extra open files
420 * that would cause an open we do to get a very high file
421 * descriptor, which in turn might be used as an index into
422 * an array (e.g. an fd_set).
428 if ((lockfd = open(filen, O_RDONLY|O_CREAT|O_EXCL, 0600)) == -1) {
433 if (amroflock(lockfd, resn) != 0) {
434 perror ("amroflock");
437 if (amfunlock(lockfd, resn) != 0) {
438 perror ("amfunlock/2");
443 * Do not use aclose() here. During configure we do not have
444 * areads_relbuf() available and it makes configure think all
445 * the tests have failed.
450 if ((lockfd = open(filen, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1) {
455 if (amflock(lockfd, resn) != 0) {
459 if (amfunlock(lockfd, resn) != 0) {
460 perror ("amfunlock/4");