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.28 2006/05/25 01:47:11 johnfranks 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(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.
79 int fd, /* fd of file to operate on */
80 int op) /* true to lock; false to unlock */
85 /* lock from here on */
86 if (lockf(fd, F_LOCK, (off_t)0) == -1) return -1;
89 /* unlock from here on */
90 if (lockf(fd, F_UNLOCK, (off_t)0) == -1) return -1;
92 /* unlock from bof to here */
93 pos = lseek(fd, (off_t)0, SEEK_CUR);
94 if (pos == (off_t)-1) {
95 if (errno == ESPIPE) pos = (off_t)0;
100 lockf(fd, F_UNLOCK, -pos) == -1) return -1;
108 #if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && !defined(USE_LOCKF) && defined(USE_LNLOCK)
109 /* XXX - error checking in this section needs to be tightened up */
111 /* Delete a lock file.
120 if (rc != 0 && errno == ENOENT) rc = 0;
125 /* Create a lock file.
136 (void)delete_lock(fn); /* that's MY file! */
139 fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0640);
141 if (fd == -1) return -1;
143 if((f = fdopen(fd, "w")) == NULL) {
147 fprintf(f, "%ld\n", pid);
148 if (fclose(f) == EOF)
153 /* Read the pid out of a lock file.
154 ** -1=error, otherwise pid.
158 char * fn) /* name of lock file */
164 if ((f = fopen(fn, "r")) == NULL) {
167 if (fscanf(f, "%ld", &pid) != 1) {
173 if (fclose(f) != 0) {
179 /* Link a lock if we can.
180 ** 0=done, 1=already locked, -1=error.
184 char * lk, /* real lock file */
185 char * tlk) /* temp lock file */
188 int serrno; /* saved errno */
189 struct stat lkstat, tlkstat;
191 /* an atomic check and set operation */
193 if (rc == 0) return 0; /* XXX do we trust it? */
195 /* link() says it failed - don't beleive it */
198 if (stat(lk, &lkstat) == 0 &&
199 stat(tlk, &tlkstat) == 0 &&
200 lkstat.st_ino == tlkstat.st_ino)
201 return 0; /* it did work! */
205 if (errno == EEXIST) rc = 1;
210 /* Steal a lock if we can.
211 ** 0=done; 1=still in use; -1 = error.
215 char * fn, /* name of lock file to steal */
216 pid_t mypid, /* my process id */
217 char * sres) /* name of steal-resource to lock */
224 /* prevent a race with another stealer */
225 rc = ln_lock(sres, 1);
226 if (rc != 0) goto error;
230 if (errno == ENOENT) goto done;
234 if (pid == mypid) goto steal; /* i'm the locker! */
236 /* are they still there ? */
237 rc = kill((pid_t)pid, 0);
239 if (errno == ESRCH) goto steal; /* locker has gone */
244 rc = ln_lock(sres, 0);
245 if (rc != 0) goto error;
250 rc = delete_lock(fn);
251 if (rc != 0) goto error;
254 rc = ln_lock(sres, 0);
255 if (rc != 0) goto error;
260 rc = ln_lock(sres, 0);
265 /* Locking using existance of a file.
269 char * res, /* name of resource to lock */
270 int op) /* true to lock; false to unlock */
273 char *lockfile = NULL;
274 char *tlockfile = NULL;
277 char pid_str[NUM_STR_SIZE];
279 mypid = (long)getpid();
281 lockfile = vstralloc(AMANDA_TMPDIR, "/am", res, ".lock", NULL);
284 /* unlock the resource */
285 assert(read_lock(lockfile) == mypid);
287 (void)delete_lock(lockfile);
292 /* lock the resource */
294 snprintf(pid_str, SIZEOF(pid_str), "%ld", mypid);
295 tlockfile = vstralloc(AMANDA_TMPDIR, "am", res, ".", pid_str, NULL);
297 (void)create_lock(tlockfile, mypid);
299 mres = stralloc2(res, ".");
302 rc = link_lock(lockfile, tlockfile);
306 rc = steal_lock(lockfile, mypid, mres);
308 if (rc == 0) continue;
312 (void) delete_lock(tlockfile);
324 * Get a file lock (for read-only files).
333 #ifdef USE_POSIX_FCNTL
334 (void)resource; /* Quiet unused paramater warning */
335 lock.l_type = F_RDLCK;
336 lock.l_whence = SEEK_SET;
337 r = fcntl(fd, F_SETLKW, &lock);
339 (void)fd; /* Quiet unused paramater warning */
340 r = amflock(fd, resource);
347 /* Get a file lock (for read/write files).
356 #ifdef USE_POSIX_FCNTL
357 (void)resource; /* Quiet unused paramater warning */
358 lock.l_type = F_WRLCK;
359 lock.l_whence = SEEK_SET;
360 r = fcntl(fd, F_SETLKW, &lock);
363 (void)resource; /* Quiet unused paramater warning */
364 r = flock(fd, LOCK_EX);
367 (void)resource; /* Quiet unused paramater warning */
368 r = use_lockf(fd, 1);
371 (void)fd; /* Quiet unused paramater warning */
372 r = ln_lock(resource, 1);
374 (void)fd; /* Quiet unused paramater warning */
375 (void)resource; /* Quiet unused paramater warning */
386 /* Release a file lock.
395 #ifdef USE_POSIX_FCNTL
396 (void)resource; /* Quiet unused paramater warning */
397 lock.l_type = F_UNLCK;
398 lock.l_whence = SEEK_SET;
399 r = fcntl(fd, F_SETLK, &lock);
402 (void)resource; /* Quiet unused paramater warning */
403 r = flock(fd, LOCK_UN);
406 (void)fd; /* Quiet unused paramater warning */
407 r = use_lockf(fd, 0);
410 (void)fd; /* Quiet unused paramater warning */
411 r = ln_lock(resource, 0);
413 (void)fd; /* Quiet unused paramater warning */
414 (void)resource; /* Quiet unused paramater warning */
425 /* Test routine for use by configure.
426 ** (I'm not sure why we use both return and exit!)
427 ** XXX the testing here should be a lot more comprehensive.
428 ** - lock the file and then try and lock it from another process
429 ** - lock the file from another process and check that process
430 ** termination unlocks it.
431 ** The hard part is to find a system independent way to not block
434 #ifdef CONFIGURE_TEST
441 char *filen = "/tmp/conftest.lock";
444 (void)argc; /* Quiet compiler warning */
445 (void)argv; /* Quiet compiler warning */
448 if ((lockfd = open(filen, O_RDONLY | O_CREAT | O_EXCL, 0600)) == -1) {
453 if (amroflock(lockfd, resn) != 0) {
454 perror ("amroflock");
457 if (amfunlock(lockfd, resn) != 0) {
458 perror ("amfunlock/2");
463 * Do not use aclose() here. During configure we do not have
464 * areads_relbuf() available and it makes configure think all
465 * the tests have failed.
470 if ((lockfd = open(filen, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
475 if (amflock(lockfd, resn) != 0) {
479 if (amfunlock(lockfd, resn) != 0) {
480 perror ("amfunlock/4");