9fe27c7c575397c2e1878c3ef92b524b4bc9dcb0
[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.27 2005/10/06 17:26:21 martinea 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 P((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 use_lockf(fd, op)
78 int fd; /* fd of file to operate on */
79 int op; /* true to lock; false to unlock */
80 {
81         off_t pos;
82
83         if (op) {
84                 /* lock from here on */
85                 if (lockf(fd, F_LOCK, (off_t)0) == -1) return -1;
86         }
87         else {
88                 /* unlock from here on */
89                 if (lockf(fd, F_UNLOCK, (off_t)0) == -1) return -1;
90
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;
95                         else return -1;
96                 }
97
98                 if (pos > (off_t)0 &&
99                     lockf(fd, F_UNLOCK, -pos) == -1) return -1;
100         }
101
102         return 0;
103 }
104
105 #endif
106
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 */
109
110 /* Delete a lock file.
111 */
112 int delete_lock(fn)
113 char *fn;
114 {
115         int rc;
116
117         rc = unlink(fn);
118         if (rc != 0 && errno == ENOENT) rc = 0;
119
120         return rc;
121 }
122
123 /* Create a lock file.
124 */
125 int create_lock(fn, pid)
126 char *fn;
127 long pid;
128 {
129         int fd;
130         FILE *f;
131         int mask;
132
133         (void)delete_lock(fn);                  /* that's MY file! */
134
135         mask = umask(0027);
136         fd = open(fn, O_WRONLY|O_CREAT|O_EXCL, 0640);
137         umask(mask);
138         if (fd == -1) return -1;
139
140         if((f = fdopen(fd, "w")) == NULL) {
141             aclose(fd);
142             return -1;
143         }
144         fprintf(f, "%ld\n", pid);
145         if (fclose(f) == EOF)
146             return -1;
147         return 0;
148 }
149
150 /* Read the pid out of a lock file.
151 **   -1=error, otherwise pid.
152 */
153 long read_lock(fn)
154 char *fn; /* name of lock file */
155 {
156         int save_errno;
157         FILE *f;
158         long pid;
159
160         if ((f = fopen(fn, "r")) == NULL) {
161                 return -1;
162         }
163         if (fscanf(f, "%ld", &pid) != 1) {
164                 save_errno = errno;
165                 afclose(f);
166                 errno = save_errno;
167                 return -1;
168         }
169         if (fclose(f) != 0) {
170                 return -1;
171         }
172         return pid;
173 }
174
175 /* Link a lock if we can.
176 **   0=done, 1=already locked, -1=error.
177 */
178 int link_lock(lk, tlk)
179 char *lk;       /* real lock file */
180 char *tlk;      /* temp lock file */
181 {
182         int rc;
183         int serrno;     /* saved errno */
184         struct stat lkstat, tlkstat;
185
186         /* an atomic check and set operation */
187         rc = link(tlk, lk);
188         if (rc == 0) return 0; /* XXX do we trust it? */
189
190         /* link() says it failed - don't beleive it */
191         serrno = errno;
192
193         if (stat(lk, &lkstat) == 0 &&
194             stat(tlk, &tlkstat) == 0 &&
195             lkstat.st_ino == tlkstat.st_ino)
196                 return 0;       /* it did work! */
197
198         errno = serrno;
199
200         if (errno == EEXIST) rc = 1;
201
202         return rc;
203 }
204
205 /* Steal a lock if we can.
206 **   0=done; 1=still in use; -1 = error.
207 */
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 */
212 {
213         int fd;
214         char buff[64];
215         long pid;
216         int rc;
217
218         /* prevent a race with another stealer */
219         rc = ln_lock(sres, 1);
220         if (rc != 0) goto error;
221
222         pid = read_lock(fn);
223         if (pid == -1) {
224                 if (errno == ENOENT) goto done;
225                 goto error;
226         }
227
228         if (pid == mypid) goto steal; /* i'm the locker! */
229
230         /* are they still there ? */
231         rc = kill((pid_t)pid, 0);
232         if (rc != 0) {
233                 if (errno == ESRCH) goto steal; /* locker has gone */
234                 goto error;
235         }
236
237 inuse:
238         rc = ln_lock(sres, 0);
239         if (rc != 0) goto error;
240
241         return 1;
242
243 steal:
244         rc = delete_lock(fn);
245         if (rc != 0) goto error;
246
247 done:
248         rc = ln_lock(sres, 0);
249         if (rc != 0) goto error;
250
251         return 0;
252
253 error:
254         rc = ln_lock(sres, 0);
255
256         return -1;
257 }
258
259 /* Locking using existance of a file.
260 */
261 int ln_lock(res, op)
262 char *res; /* name of resource to lock */
263 int op;    /* true to lock; false to unlock */
264 {
265         long mypid;
266         char *lockfile = NULL;
267         char *tlockfile = NULL;
268         char *mres = NULL;
269         int rc;
270         char pid_str[NUM_STR_SIZE];
271
272         mypid = (long)getpid();
273
274         lockfile = vstralloc(AMANDA_TMPDIR, "/am", res, ".lock", NULL);
275
276         if (!op) {
277                 /* unlock the resource */
278                 assert(read_lock(lockfile) == mypid);
279
280                 (void)delete_lock(lockfile);
281                 amfree(lockfile);
282                 return 0;
283         }
284
285         /* lock the resource */
286
287         snprintf(pid_str, sizeof(pid_str), "%ld", mypid);
288         tlockfile = vstralloc(AMANDA_TMPDIR, "am", res, ".", pid_str, NULL);
289
290         (void)create_lock(tlockfile, mypid);
291
292         mres = stralloc2(res, ".");
293
294         while(1) {
295                 rc = link_lock(lockfile, tlockfile);
296                 if (rc == -1) break;
297                 if (rc == 0) break;
298
299                 rc = steal_lock(lockfile, mypid, mres);
300                 if (rc == -1) break;
301                 if (rc == 0) continue;
302                 sleep(1);
303         }
304
305         (void) delete_lock(tlockfile);
306
307         amfree(mres);
308         amfree(tlockfile);
309         amfree(lockfile);
310
311         return rc;
312 }
313 #endif
314
315
316 /* Get a file lock (for read-only files).
317 */
318 int amroflock(fd, resource)
319 int fd;
320 char *resource;
321 {
322         int r;
323
324 #ifdef USE_POSIX_FCNTL
325         lock.l_type = F_RDLCK;
326         lock.l_whence = SEEK_SET;
327         r = fcntl(fd, F_SETLKW, &lock);
328 #else
329         r = amflock(fd, resource);
330 #endif
331
332         return r;
333 }
334
335
336 /* Get a file lock (for read/write files).
337 */
338 int amflock(fd, resource)
339 int fd;
340 char *resource;
341 {
342         int r;
343
344 #ifdef USE_POSIX_FCNTL
345         lock.l_type = F_WRLCK;
346         lock.l_whence = SEEK_SET;
347         r = fcntl(fd, F_SETLKW, &lock);
348 #else
349 #ifdef USE_FLOCK
350         r = flock(fd, LOCK_EX);
351 #else
352 #ifdef USE_LOCKF
353         r = use_lockf(fd, 1);
354 #else
355 #ifdef USE_LNLOCK
356         r = ln_lock(resource, 1);
357 #else
358         r = 0;
359 #endif
360 #endif
361 #endif
362 #endif
363
364         return r;
365 }
366
367
368 /* Release a file lock.
369 */
370 int amfunlock(fd, resource)
371 int fd;
372 char *resource;
373 {
374         int r;
375
376 #ifdef USE_POSIX_FCNTL
377         lock.l_type = F_UNLCK;
378         lock.l_whence = SEEK_SET;
379         r = fcntl(fd, F_SETLK, &lock);
380 #else
381 #ifdef USE_FLOCK
382         r = flock(fd, LOCK_UN);
383 #else
384 #ifdef USE_LOCKF
385         r = use_lockf(fd, 0);
386 #else
387 #ifdef USE_LNLOCK
388         r = ln_lock(resource, 0);
389 #else
390         r = 0;
391 #endif
392 #endif
393 #endif
394 #endif
395
396         return r;
397 }
398
399
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
407 **     for ever.
408 */
409 #ifdef CONFIGURE_TEST
410 main()
411 {
412     int lockfd;
413     char *filen = "/tmp/conftest.lock";
414     char *resn = "test";
415     int fd;
416
417     unlink(filen);
418     if ((lockfd = open(filen, O_RDONLY|O_CREAT|O_EXCL, 0600)) == -1) {
419         perror (filen);
420         exit(10);
421     }
422
423     if (amroflock(lockfd, resn) != 0) {
424         perror ("amroflock");
425         exit(1);
426     }
427     if (amfunlock(lockfd, resn) != 0) {
428         perror ("amfunlock/2");
429         exit(2);
430     }
431
432     /*
433      * Do not use aclose() here.  During configure we do not have
434      * areads_relbuf() available and it makes configure think all
435      * the tests have failed.
436      */
437     close(lockfd);
438
439     unlink(filen);
440     if ((lockfd = open(filen, O_WRONLY|O_CREAT|O_EXCL, 0600)) == -1) {
441         perror (filen);
442         exit(20);
443     }
444
445     if (amflock(lockfd, resn) != 0) {
446         perror ("amflock");
447         exit(3);
448     }
449     if (amfunlock(lockfd, resn) != 0) {
450         perror ("amfunlock/4");
451         exit(4);
452     }
453
454     close(lockfd);
455
456     exit(0);
457 }
458 #endif