Imported Upstream version 3.3.3
[debian/amanda] / common-src / amflock-lnlock.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27
28 /* moved from amflock.c by Dustin J. Mitchell <dustin@zmanda.com> */
29
30 #include "amanda.h"
31
32 static int ln_lock(char *res, int op);
33 char *_lnlock_dir = AMANDA_TMPDIR; /* amflock-test changes this; it's a constant otherwise */
34
35 /* XXX - error checking in this section needs to be tightened up */
36
37 /* Delete a lock file.
38 */
39 static int
40 delete_lock(
41     char *fn)
42 {
43         int rc;
44
45         rc = unlink(fn);
46         if (rc != 0 && errno == ENOENT) rc = 0;
47
48         return rc;
49 }
50
51 /* Create a lock file.
52 */
53 static int
54 create_lock(
55     char *fn,
56     long pid)
57 {
58         int fd;
59         FILE *f;
60         int mask;
61
62         (void)delete_lock(fn);                  /* that's MY file! */
63
64         mask = umask(0027);
65         fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0640);
66         umask(mask);
67         if (fd == -1) return -1;
68
69         if((f = fdopen(fd, "w")) == NULL) {
70             aclose(fd);
71             return -1;
72         }
73         g_fprintf(f, "%ld\n", pid);
74         if (fclose(f) == EOF)
75             return -1;
76         return 0;
77 }
78
79 /* Read the pid out of a lock file.
80 **   -1=error, otherwise pid.
81 */
82 static long
83 read_lock(
84     char *      fn) /* name of lock file */
85 {
86         int save_errno;
87         FILE *f;
88         long pid;
89
90         if ((f = fopen(fn, "r")) == NULL) {
91                 return -1;
92         }
93         if (fscanf(f, "%ld", &pid) != 1) {
94                 save_errno = errno;
95                 afclose(f);
96                 errno = save_errno;
97                 return -1;
98         }
99         if (fclose(f) != 0) {
100                 return -1;
101         }
102         return pid;
103 }
104
105 /* Link a lock if we can.
106 **   0=done, 1=already locked, -1=error.
107 */
108 static int
109 link_lock(
110     char *      lk,     /* real lock file */
111     char *      tlk)    /* temp lock file */
112 {
113         int rc;
114         int serrno;     /* saved errno */
115         struct stat lkstat, tlkstat;
116
117         /* an atomic check and set operation */
118         rc = link(tlk, lk);
119         if (rc == 0) return 0; /* XXX do we trust it? */
120
121         /* link() says it failed - don't beleive it */
122         serrno = errno;
123
124         if (stat(lk, &lkstat) == 0 &&
125             stat(tlk, &tlkstat) == 0 &&
126             lkstat.st_ino == tlkstat.st_ino)
127                 return 0;       /* it did work! */
128
129         errno = serrno;
130
131         if (errno == EEXIST) rc = 1;
132
133         return rc;
134 }
135
136 /* Steal a lock if we can.
137 **   0=done; 1=still in use; -1 = error.
138 */
139 static int
140 steal_lock(
141     char *      fn,     /* name of lock file to steal */
142     long        mypid,  /* my process id */
143     char *      sres)   /* name of steal-resource to lock */
144 {
145         long pid;
146         int rc;
147
148         /* prevent a race with another stealer */
149         rc = ln_lock(sres, 1);
150         if (rc != 0) goto error;
151
152         pid = read_lock(fn);
153         if (pid == -1) {
154                 if (errno == ENOENT) goto done;
155                 goto error;
156         }
157
158         if (pid == mypid) goto steal; /* i'm the locker! */
159
160         /* are they still there ? */
161         rc = kill((pid_t)pid, 0);
162         if (rc != 0) {
163                 if (errno == ESRCH) goto steal; /* locker has gone */
164                 goto error;
165         }
166
167         rc = ln_lock(sres, 0);
168         if (rc != 0) goto error;
169
170         return 1;
171
172 steal:
173         rc = delete_lock(fn);
174         if (rc != 0) goto error;
175
176 done:
177         rc = ln_lock(sres, 0);
178         if (rc != 0) goto error;
179
180         return 0;
181
182 error:
183         rc = ln_lock(sres, 0);
184
185         return -1;
186 }
187
188 static int
189 ln_lock(
190     char *      res, /* name of resource to lock */
191     int         op)  /* true to lock; false to unlock */
192 {
193         long mypid;
194         char *lockfile = NULL;
195         char *tlockfile = NULL;
196         char *mres = NULL;
197         int rc;
198         char pid_str[NUM_STR_SIZE];
199
200         mypid = (long)getpid();
201
202         lockfile = vstralloc(_lnlock_dir, "/am", res, ".lock", NULL);
203
204         if (!op) {
205                 /* unlock the resource */
206                 assert(read_lock(lockfile) == mypid);
207
208                 (void)delete_lock(lockfile);
209                 amfree(lockfile);
210                 return 0;
211         }
212
213         /* lock the resource */
214
215         g_snprintf(pid_str, SIZEOF(pid_str), "%ld", mypid);
216         tlockfile = vstralloc(_lnlock_dir, "/am", res, ".", pid_str, NULL);
217
218         (void)create_lock(tlockfile, mypid);
219
220         mres = stralloc2(res, ".");
221
222         while(1) {
223                 rc = link_lock(lockfile, tlockfile);
224                 if (rc == -1) break;
225                 if (rc == 0) break;
226
227                 rc = steal_lock(lockfile, mypid, mres);
228                 if (rc == -1) break;
229                 if (rc == 0) continue;
230                 sleep(1);
231         }
232
233         (void) delete_lock(tlockfile);
234
235         amfree(mres);
236         amfree(tlockfile);
237         amfree(lockfile);
238
239         return rc;
240 }
241
242 static int
243 lnlock_lock(
244     G_GNUC_UNUSED int fd,
245     char *resource)
246 {
247     return ln_lock(resource, 1);
248 }
249
250 static int
251 lnlock_unlock(
252     G_GNUC_UNUSED int fd,
253     char *resource)
254 {
255     return ln_lock(resource, 0);
256 }
257
258 amflock_impl_t amflock_lnlock_impl = {
259     lnlock_lock,
260     lnlock_lock, /* no read-only support */
261     lnlock_unlock,
262     "lnlock"
263 };