Imported Upstream version 2.5.1
[debian/amanda] / common-src / amflock.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amflock.c,v 1.28 2006/05/25 01:47:11 johnfranks Exp $
28  *
29  * file locking routines, put here to hide the system dependant stuff
30  * from the rest of the code
31  */
32 /*
33 **
34 ** Notes:
35 ** - These are "best effort" routines.
36 ** - "configure" has four variables that are used to determine which type of
37 **   locking to use:
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.
46 */
47
48 #include "amanda.h"
49
50 #if defined(USE_POSIX_FCNTL)
51      static struct flock lock; /* zero-initialized */
52 #endif
53
54 #if !defined(USE_POSIX_FCNTL) && defined(USE_FLOCK)
55 #  ifdef HAVE_SYS_FILE_H
56 #    include <sys/file.h>
57 #  endif
58
59 #  if !defined(HAVE_FLOCK_DECL) && !defined(CONFIGURE_TEST)
60      extern int flock(int fd, int operation);
61 #  endif
62 #endif
63
64
65 #if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && defined(USE_LOCKF)
66
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
70 #endif
71
72 /* Lock a file using lockf().
73 ** Notes:
74 ** - returns errors for some non-files like pipes.
75 ** - probably only works for files open for writing.
76 */
77 int
78 use_lockf(
79     int fd,     /* fd of file to operate on */
80     int op)     /* true to lock; false to unlock */
81 {
82         off_t pos;
83
84         if (op) {
85                 /* lock from here on */
86                 if (lockf(fd, F_LOCK, (off_t)0) == -1) return -1;
87         }
88         else {
89                 /* unlock from here on */
90                 if (lockf(fd, F_UNLOCK, (off_t)0) == -1) return -1;
91
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;
96                         else return -1;
97                 }
98
99                 if (pos > (off_t)0 &&
100                     lockf(fd, F_UNLOCK, -pos) == -1) return -1;
101         }
102
103         return 0;
104 }
105
106 #endif
107
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 */
110
111 /* Delete a lock file.
112 */
113 int
114 delete_lock(
115     char *fn)
116 {
117         int rc;
118
119         rc = unlink(fn);
120         if (rc != 0 && errno == ENOENT) rc = 0;
121
122         return rc;
123 }
124
125 /* Create a lock file.
126 */
127 int
128 create_lock(
129     char *fn,
130     pid_t pid)
131 {
132         int fd;
133         FILE *f;
134         int mask;
135
136         (void)delete_lock(fn);                  /* that's MY file! */
137
138         mask = umask(0027);
139         fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0640);
140         umask(mask);
141         if (fd == -1) return -1;
142
143         if((f = fdopen(fd, "w")) == NULL) {
144             aclose(fd);
145             return -1;
146         }
147         fprintf(f, "%ld\n", pid);
148         if (fclose(f) == EOF)
149             return -1;
150         return 0;
151 }
152
153 /* Read the pid out of a lock file.
154 **   -1=error, otherwise pid.
155 */
156 long
157 read_lock(
158     char *      fn) /* name of lock file */
159 {
160         int save_errno;
161         FILE *f;
162         long pid;
163
164         if ((f = fopen(fn, "r")) == NULL) {
165                 return -1;
166         }
167         if (fscanf(f, "%ld", &pid) != 1) {
168                 save_errno = errno;
169                 afclose(f);
170                 errno = save_errno;
171                 return -1;
172         }
173         if (fclose(f) != 0) {
174                 return -1;
175         }
176         return pid;
177 }
178
179 /* Link a lock if we can.
180 **   0=done, 1=already locked, -1=error.
181 */
182 int
183 link_lock(
184     char *      lk,     /* real lock file */
185     char *      tlk)    /* temp lock file */
186 {
187         int rc;
188         int serrno;     /* saved errno */
189         struct stat lkstat, tlkstat;
190
191         /* an atomic check and set operation */
192         rc = link(tlk, lk);
193         if (rc == 0) return 0; /* XXX do we trust it? */
194
195         /* link() says it failed - don't beleive it */
196         serrno = errno;
197
198         if (stat(lk, &lkstat) == 0 &&
199             stat(tlk, &tlkstat) == 0 &&
200             lkstat.st_ino == tlkstat.st_ino)
201                 return 0;       /* it did work! */
202
203         errno = serrno;
204
205         if (errno == EEXIST) rc = 1;
206
207         return rc;
208 }
209
210 /* Steal a lock if we can.
211 **   0=done; 1=still in use; -1 = error.
212 */
213 int
214 steal_lock(
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 */
218 {
219         int fd;
220         char buff[64];
221         long pid;
222         int rc;
223
224         /* prevent a race with another stealer */
225         rc = ln_lock(sres, 1);
226         if (rc != 0) goto error;
227
228         pid = read_lock(fn);
229         if (pid == -1) {
230                 if (errno == ENOENT) goto done;
231                 goto error;
232         }
233
234         if (pid == mypid) goto steal; /* i'm the locker! */
235
236         /* are they still there ? */
237         rc = kill((pid_t)pid, 0);
238         if (rc != 0) {
239                 if (errno == ESRCH) goto steal; /* locker has gone */
240                 goto error;
241         }
242
243 inuse:
244         rc = ln_lock(sres, 0);
245         if (rc != 0) goto error;
246
247         return 1;
248
249 steal:
250         rc = delete_lock(fn);
251         if (rc != 0) goto error;
252
253 done:
254         rc = ln_lock(sres, 0);
255         if (rc != 0) goto error;
256
257         return 0;
258
259 error:
260         rc = ln_lock(sres, 0);
261
262         return -1;
263 }
264
265 /* Locking using existance of a file.
266 */
267 int
268 ln_lock(
269     char *      res, /* name of resource to lock */
270     int         op)  /* true to lock; false to unlock */
271 {
272         long mypid;
273         char *lockfile = NULL;
274         char *tlockfile = NULL;
275         char *mres = NULL;
276         int rc;
277         char pid_str[NUM_STR_SIZE];
278
279         mypid = (long)getpid();
280
281         lockfile = vstralloc(AMANDA_TMPDIR, "/am", res, ".lock", NULL);
282
283         if (!op) {
284                 /* unlock the resource */
285                 assert(read_lock(lockfile) == mypid);
286
287                 (void)delete_lock(lockfile);
288                 amfree(lockfile);
289                 return 0;
290         }
291
292         /* lock the resource */
293
294         snprintf(pid_str, SIZEOF(pid_str), "%ld", mypid);
295         tlockfile = vstralloc(AMANDA_TMPDIR, "am", res, ".", pid_str, NULL);
296
297         (void)create_lock(tlockfile, mypid);
298
299         mres = stralloc2(res, ".");
300
301         while(1) {
302                 rc = link_lock(lockfile, tlockfile);
303                 if (rc == -1) break;
304                 if (rc == 0) break;
305
306                 rc = steal_lock(lockfile, mypid, mres);
307                 if (rc == -1) break;
308                 if (rc == 0) continue;
309                 sleep(1);
310         }
311
312         (void) delete_lock(tlockfile);
313
314         amfree(mres);
315         amfree(tlockfile);
316         amfree(lockfile);
317
318         return rc;
319 }
320 #endif
321
322
323 /*
324  * Get a file lock (for read-only files).
325  */
326 int
327 amroflock(
328     int         fd,
329     char *      resource)
330 {
331         int r;
332
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);
338 #else
339         (void)fd; /* Quiet unused paramater warning */
340         r = amflock(fd, resource);
341 #endif
342
343         return r;
344 }
345
346
347 /* Get a file lock (for read/write files).
348 */
349 int
350 amflock(
351     int         fd,
352     char *      resource)
353 {
354         int r;
355
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);
361 #else
362 #ifdef USE_FLOCK
363         (void)resource; /* Quiet unused paramater warning */
364         r = flock(fd, LOCK_EX);
365 #else
366 #ifdef USE_LOCKF
367         (void)resource; /* Quiet unused paramater warning */
368         r = use_lockf(fd, 1);
369 #else
370 #ifdef USE_LNLOCK
371         (void)fd; /* Quiet unused paramater warning */
372         r = ln_lock(resource, 1);
373 #else
374         (void)fd; /* Quiet unused paramater warning */
375         (void)resource; /* Quiet unused paramater warning */
376         r = 0;
377 #endif
378 #endif
379 #endif
380 #endif
381
382         return r;
383 }
384
385
386 /* Release a file lock.
387 */
388 int
389 amfunlock(
390     int         fd,
391     char *      resource)
392 {
393         int r;
394
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);
400 #else
401 #ifdef USE_FLOCK
402         (void)resource; /* Quiet unused paramater warning */
403         r = flock(fd, LOCK_UN);
404 #else
405 #ifdef USE_LOCKF
406         (void)fd; /* Quiet unused paramater warning */
407         r = use_lockf(fd, 0);
408 #else
409 #ifdef USE_LNLOCK
410         (void)fd; /* Quiet unused paramater warning */
411         r = ln_lock(resource, 0);
412 #else
413         (void)fd; /* Quiet unused paramater warning */
414         (void)resource; /* Quiet unused paramater warning */
415         r = 0;
416 #endif
417 #endif
418 #endif
419 #endif
420
421         return r;
422 }
423
424
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
432 **     for ever.
433 */
434 #ifdef CONFIGURE_TEST
435 int
436 main(
437     int argc,
438     char **argv)
439 {
440     int lockfd;
441     char *filen = "/tmp/conftest.lock";
442     char *resn = "test";
443     int fd;
444
445     (void)argc;         /* Quiet compiler warning */
446     (void)argv;         /* Quiet compiler warning */
447
448     unlink(filen);
449     if ((lockfd = open(filen, O_RDONLY | O_CREAT | O_EXCL, 0600)) == -1) {
450         perror (filen);
451         exit(10);
452     }
453
454     if (amroflock(lockfd, resn) != 0) {
455         perror ("amroflock");
456         exit(1);
457     }
458     if (amfunlock(lockfd, resn) != 0) {
459         perror ("amfunlock/2");
460         exit(2);
461     }
462
463     /*
464      * Do not use aclose() here.  During configure we do not have
465      * areads_relbuf() available and it makes configure think all
466      * the tests have failed.
467      */
468     close(lockfd);
469
470     unlink(filen);
471     if ((lockfd = open(filen, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
472         perror (filen);
473         exit(20);
474     }
475
476     if (amflock(lockfd, resn) != 0) {
477         perror ("amflock");
478         exit(3);
479     }
480     if (amfunlock(lockfd, resn) != 0) {
481         perror ("amfunlock/4");
482         exit(4);
483     }
484
485     close(lockfd);
486
487     exit(0);
488 }
489 #endif