Imported Upstream version 2.6.0
[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  * 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 /* moved from amflock.c by Dustin J. Mitchell <dustin@zmanda.com> */
28
29 #include "amanda.h"
30
31 static int ln_lock(char *res, int op);
32 char *_lnlock_dir = AMANDA_TMPDIR; /* amflock-test changes this; it's a constant otherwise */
33
34 /* XXX - error checking in this section needs to be tightened up */
35
36 /* Delete a lock file.
37 */
38 static int
39 delete_lock(
40     char *fn)
41 {
42         int rc;
43
44         rc = unlink(fn);
45         if (rc != 0 && errno == ENOENT) rc = 0;
46
47         return rc;
48 }
49
50 /* Create a lock file.
51 */
52 static int
53 create_lock(
54     char *fn,
55     long pid)
56 {
57         int fd;
58         FILE *f;
59         int mask;
60
61         (void)delete_lock(fn);                  /* that's MY file! */
62
63         mask = umask(0027);
64         fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0640);
65         umask(mask);
66         if (fd == -1) return -1;
67
68         if((f = fdopen(fd, "w")) == NULL) {
69             aclose(fd);
70             return -1;
71         }
72         g_fprintf(f, "%ld\n", pid);
73         if (fclose(f) == EOF)
74             return -1;
75         return 0;
76 }
77
78 /* Read the pid out of a lock file.
79 **   -1=error, otherwise pid.
80 */
81 static long
82 read_lock(
83     char *      fn) /* name of lock file */
84 {
85         int save_errno;
86         FILE *f;
87         long pid;
88
89         if ((f = fopen(fn, "r")) == NULL) {
90                 return -1;
91         }
92         if (fscanf(f, "%ld", &pid) != 1) {
93                 save_errno = errno;
94                 afclose(f);
95                 errno = save_errno;
96                 return -1;
97         }
98         if (fclose(f) != 0) {
99                 return -1;
100         }
101         return pid;
102 }
103
104 /* Link a lock if we can.
105 **   0=done, 1=already locked, -1=error.
106 */
107 static int
108 link_lock(
109     char *      lk,     /* real lock file */
110     char *      tlk)    /* temp lock file */
111 {
112         int rc;
113         int serrno;     /* saved errno */
114         struct stat lkstat, tlkstat;
115
116         /* an atomic check and set operation */
117         rc = link(tlk, lk);
118         if (rc == 0) return 0; /* XXX do we trust it? */
119
120         /* link() says it failed - don't beleive it */
121         serrno = errno;
122
123         if (stat(lk, &lkstat) == 0 &&
124             stat(tlk, &tlkstat) == 0 &&
125             lkstat.st_ino == tlkstat.st_ino)
126                 return 0;       /* it did work! */
127
128         errno = serrno;
129
130         if (errno == EEXIST) rc = 1;
131
132         return rc;
133 }
134
135 /* Steal a lock if we can.
136 **   0=done; 1=still in use; -1 = error.
137 */
138 static int
139 steal_lock(
140     char *      fn,     /* name of lock file to steal */
141     long        mypid,  /* my process id */
142     char *      sres)   /* name of steal-resource to lock */
143 {
144         long pid;
145         int rc;
146
147         /* prevent a race with another stealer */
148         rc = ln_lock(sres, 1);
149         if (rc != 0) goto error;
150
151         pid = read_lock(fn);
152         if (pid == -1) {
153                 if (errno == ENOENT) goto done;
154                 goto error;
155         }
156
157         if (pid == mypid) goto steal; /* i'm the locker! */
158
159         /* are they still there ? */
160         rc = kill((pid_t)pid, 0);
161         if (rc != 0) {
162                 if (errno == ESRCH) goto steal; /* locker has gone */
163                 goto error;
164         }
165
166         rc = ln_lock(sres, 0);
167         if (rc != 0) goto error;
168
169         return 1;
170
171 steal:
172         rc = delete_lock(fn);
173         if (rc != 0) goto error;
174
175 done:
176         rc = ln_lock(sres, 0);
177         if (rc != 0) goto error;
178
179         return 0;
180
181 error:
182         rc = ln_lock(sres, 0);
183
184         return -1;
185 }
186
187 static int
188 ln_lock(
189     char *      res, /* name of resource to lock */
190     int         op)  /* true to lock; false to unlock */
191 {
192         long mypid;
193         char *lockfile = NULL;
194         char *tlockfile = NULL;
195         char *mres = NULL;
196         int rc;
197         char pid_str[NUM_STR_SIZE];
198
199         mypid = (long)getpid();
200
201         lockfile = vstralloc(_lnlock_dir, "/am", res, ".lock", NULL);
202
203         if (!op) {
204                 /* unlock the resource */
205                 assert(read_lock(lockfile) == mypid);
206
207                 (void)delete_lock(lockfile);
208                 amfree(lockfile);
209                 return 0;
210         }
211
212         /* lock the resource */
213
214         g_snprintf(pid_str, SIZEOF(pid_str), "%ld", mypid);
215         tlockfile = vstralloc(_lnlock_dir, "/am", res, ".", pid_str, NULL);
216
217         (void)create_lock(tlockfile, mypid);
218
219         mres = stralloc2(res, ".");
220
221         while(1) {
222                 rc = link_lock(lockfile, tlockfile);
223                 if (rc == -1) break;
224                 if (rc == 0) break;
225
226                 rc = steal_lock(lockfile, mypid, mres);
227                 if (rc == -1) break;
228                 if (rc == 0) continue;
229                 sleep(1);
230         }
231
232         (void) delete_lock(tlockfile);
233
234         amfree(mres);
235         amfree(tlockfile);
236         amfree(lockfile);
237
238         return rc;
239 }
240
241 static int
242 lnlock_lock(
243     G_GNUC_UNUSED int fd,
244     char *resource)
245 {
246     return ln_lock(resource, 1);
247 }
248
249 static int
250 lnlock_unlock(
251     G_GNUC_UNUSED int fd,
252     char *resource)
253 {
254     return ln_lock(resource, 0);
255 }
256
257 amflock_impl_t amflock_lnlock_impl = {
258     lnlock_lock,
259     lnlock_lock, /* no read-only support */
260     lnlock_unlock,
261     "lnlock"
262 };