|
|
1.1 root 1: /* lock.c
2: Lock and unlock a file name.
3:
4: Copyright (C) 1991, 1992 Ian Lance Taylor
5:
6: This file is part of the Taylor UUCP package.
7:
8: This program is free software; you can redistribute it and/or
9: modify it under the terms of the GNU General Public License as
10: published by the Free Software Foundation; either version 2 of the
11: License, or (at your option) any later version.
12:
13: This program is distributed in the hope that it will be useful, but
14: WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16: General Public License for more details.
17:
18: You should have received a copy of the GNU General Public License
19: along with this program; if not, write to the Free Software
20: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21:
22: The author of the program may be contacted at [email protected] or
23: c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
24: */
25:
26: #include "uucp.h"
27:
28: #if USE_RCS_ID
29: const char lock_rcsid[] = "$Id: lock.c,v 1.1 93/07/30 08:02:37 bin Exp Locker: bin $";
30: #endif
31:
32: #include "uudefs.h"
33: #include "sysdep.h"
34: #include "system.h"
35:
36: #include <errno.h>
37:
38: #if HAVE_FCNTL_H
39: #include <fcntl.h>
40: #else
41: #if HAVE_SYS_FILE_H
42: #include <sys/file.h>
43: #endif
44: #endif
45:
46: #ifndef O_RDONLY
47: #define O_RDONLY 0
48: #define O_WRONLY 1
49: #define O_RDWR 2
50: #endif
51:
52: #ifndef O_NOCTTY
53: #define O_NOCTTY 0
54: #endif
55:
56: #ifndef SEEK_SET
57: #define SEEK_SET 0
58: #endif
59:
60: /* Lock something. If the fspooldir argument is TRUE, the argument is
61: a file name relative to the spool directory; otherwise the argument
62: is a simple file name which should be created in the system lock
63: directory (under HDB this is /etc/locks). */
64:
65: boolean
66: fsdo_lock (zlock, fspooldir, pferr)
67: const char *zlock;
68: boolean fspooldir;
69: boolean *pferr;
70: {
71: char *zfree;
72: const char *zpath, *zslash;
73: size_t cslash;
74: pid_t ime;
75: char *ztempfile;
76: char abtempfile[sizeof "TMP1234567890"];
77: int o;
78: #if HAVE_V2_LOCKFILES
79: int i;
80: #else
81: char ab[12];
82: #endif
83: int cwrote;
84: const char *zerr;
85: boolean fret;
86:
87: if (pferr != NULL)
88: *pferr = TRUE;
89:
90: if (fspooldir)
91: {
92: zfree = NULL;
93: zpath = zlock;
94: }
95: else
96: {
97: zfree = zsysdep_in_dir (zSlockdir, zlock);
98: zpath = zfree;
99: }
100:
101: ime = getpid ();
102:
103: /* We do the actual lock by creating a file and then linking it to
104: the final file name we want. This avoids race conditions due to
105: one process checking the file before we have finished writing it,
106: and also works even if we are somehow running as root.
107:
108: First, create the file in the right directory (we must create the
109: file in the same directory since otherwise we might attempt a
110: cross-device link). */
111: zslash = strrchr (zpath, '/');
112: if (zslash == NULL)
113: cslash = 0;
114: else
115: cslash = zslash - zpath + 1;
116:
117: sprintf (abtempfile, "TMP%010lx", (unsigned long) ime);
118: ztempfile = zbufalc (cslash + sizeof abtempfile);
119: memcpy (ztempfile, zpath, cslash);
120: memcpy (ztempfile + cslash, abtempfile, sizeof abtempfile);
121:
122: o = creat (ztempfile, IPUBLIC_FILE_MODE);
123: if (o < 0)
124: {
125: if (errno == ENOENT)
126: {
127: if (! fsysdep_make_dirs (ztempfile, FALSE))
128: {
129: ubuffree (zfree);
130: ubuffree (ztempfile);
131: return FALSE;
132: }
133: o = creat (ztempfile, IPUBLIC_FILE_MODE);
134: }
135: if (o < 0)
136: {
137: ulog (LOG_ERROR, "creat (%s): %s", ztempfile, strerror (errno));
138: ubuffree (zfree);
139: ubuffree (ztempfile);
140: return FALSE;
141: }
142: }
143:
144: #if HAVE_V2_LOCKFILES
145: i = ime;
146: cwrote = write (o, &i, sizeof i);
147: #else
148: sprintf (ab, "%10d\n", (int) ime);
149: cwrote = write (o, ab, strlen (ab));
150: #endif
151:
152: zerr = NULL;
153: if (cwrote < 0)
154: zerr = "write";
155: if (close (o) < 0)
156: zerr = "close";
157: if (zerr != NULL)
158: {
159: ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno));
160: (void) remove (ztempfile);
161: ubuffree (zfree);
162: ubuffree (ztempfile);
163: return FALSE;
164: }
165:
166: /* Now try to link the file we just created to the lock file that we
167: want. If it fails, try reading the existing file to make sure
168: the process that created it still exists. We do this in a loop
169: to make it easy to retry if the old locking process no longer
170: exists. */
171: fret = TRUE;
172: if (pferr != NULL)
173: *pferr = FALSE;
174: o = -1;
175: zerr = NULL;
176:
177: while (link (ztempfile, zpath) != 0)
178: {
179: int cgot;
180: int ipid;
181: boolean freadonly;
182:
183: fret = FALSE;
184:
185: if (errno != EEXIST)
186: {
187: ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath,
188: strerror (errno));
189: if (pferr != NULL)
190: *pferr = TRUE;
191: break;
192: }
193:
194: freadonly = FALSE;
195: o = open ((char *) zpath, O_RDWR | O_NOCTTY, 0);
196: if (o < 0)
197: {
198: if (errno == EACCES)
199: {
200: freadonly = TRUE;
201: o = open ((char *) zpath, O_RDONLY, 0);
202: }
203: if (o < 0)
204: {
205: if (errno == ENOENT)
206: {
207: /* The file was presumably removed between the link
208: and the open. Try the link again. */
209: fret = TRUE;
210: continue;
211: }
212: zerr = "open";
213: break;
214: }
215: }
216:
217: /* The race starts here. See below for a discussion. */
218:
219: #if HAVE_V2_LOCKFILES
220: cgot = read (o, &i, sizeof i);
221: #else
222: cgot = read (o, ab, sizeof ab - 1);
223: #endif
224:
225: if (cgot < 0)
226: {
227: zerr = "read";
228: break;
229: }
230:
231: #if HAVE_V2_LOCKFILES
232: ipid = i;
233: #else
234: ab[cgot] = '\0';
235: ipid = strtol (ab, (char **) NULL, 10);
236: #endif
237:
238: /* On NFS, the link might have actually succeeded even though we
239: got a failure return. This can happen if the original
240: acknowledgement was lost or delayed and the operation was
241: retried. In this case the pid will be our own. This
242: introduces a rather improbable race condition: if a stale
243: lock was left with our process ID in it, and another process
244: just did the kill, below, but has not yet changed the lock
245: file to hold its own process ID, we could start up and make
246: it all the way to here and think we have the lock. I'm not
247: going to worry about this possibility. */
248: if (ipid == ime)
249: {
250: fret = TRUE;
251: break;
252: }
253:
254: /* If the process still exists, we will get EPERM rather than
255: ESRCH. We then return FALSE to indicate that we cannot make
256: the lock. */
257: if (kill (ipid, 0) == 0 || errno == EPERM)
258: break;
259:
260: ulog (LOG_ERROR, "Found stale lock %s held by process %d",
261: zpath, ipid);
262:
263: /* This is a stale lock, created by a process that no longer
264: exists.
265:
266: Now we could remove the file (and, if the file mode disallows
267: writing, that's what we have to do), but we try to avoid
268: doing so since it causes a race condition. If we remove the
269: file, and are interrupted any time after we do the read until
270: we do the remove, another process could get in, open the
271: file, find that it was a stale lock, remove the file and
272: create a new one. When we regained control we would remove
273: the file the other process just created.
274:
275: These files are being generated partially for the benefit of
276: cu, and it would be nice to avoid the race however cu avoids
277: it, so that the programs remain compatible. Unfortunately,
278: nobody seems to know how cu avoids the race, or even if it
279: tries to avoid it at all.
280:
281: There are a few ways to avoid the race. We could use kernel
282: locking primitives, but they may not be available. We could
283: link to a special file name, but if that file were left lying
284: around then no stale lock could ever be broken (Henry Spencer
285: would think this was a good thing).
286:
287: Instead I've implemented the following procedure: seek to the
288: start of the file, write our pid into it, sleep for five
289: seconds, and then make sure our pid is still there. Anybody
290: who checks the file while we're asleep will find our pid
291: there and fail the lock. The only race will come from
292: another process which has done the read by the time we do our
293: write. That process will then have five seconds to do its
294: own write. When we wake up, we'll notice that our pid is no
295: longer in the file, and retry the lock from the beginning.
296:
297: This relies on the atomicity of write(2). If it possible for
298: the writes of two processes to be interleaved, the two
299: processes could livelock. POSIX unfortunately leaves this
300: case explicitly undefined; however, given that the write is
301: of less than a disk block, it's difficult to imagine an
302: interleave occurring.
303:
304: Note that this is still a race. If it takes the second
305: process more than five seconds to do the kill, the lseek, and
306: the write, both processes will think they have the lock.
307: Perhaps the length of time to sleep should be configurable.
308: Even better, perhaps I should add a configuration option to
309: use a permanent lock file, which eliminates any race and
310: forces the installer to be aware of the existence of the
311: permanent lock file.
312:
313: We stat the file after the sleep, to make sure some other
314: program hasn't deleted it for us. */
315: if (freadonly)
316: {
317: (void) close (o);
318: o = -1;
319: (void) remove (zpath);
320: continue;
321: }
322:
323: if (lseek (o, (off_t) 0, SEEK_SET) != 0)
324: {
325: zerr = "lseek";
326: break;
327: }
328:
329: #if HAVE_V2_LOCKFILES
330: i = ime;
331: cwrote = write (o, &i, sizeof i);
332: #else
333: sprintf (ab, "%10d\n", (int) ime);
334: cwrote = write (o, ab, strlen (ab));
335: #endif
336:
337: if (cwrote < 0)
338: {
339: zerr = "write";
340: break;
341: }
342:
343: (void) sleep (5);
344:
345: if (lseek (o, (off_t) 0, SEEK_SET) != 0)
346: {
347: zerr = "lseek";
348: break;
349: }
350:
351: #if HAVE_V2_LOCKFILES
352: cgot = read (o, &i, sizeof i);
353: #else
354: cgot = read (o, ab, sizeof ab - 1);
355: #endif
356:
357: if (cgot < 0)
358: {
359: zerr = "read";
360: break;
361: }
362:
363: #if HAVE_V2_LOCKFILES
364: ipid = i;
365: #else
366: ab[cgot] = '\0';
367: ipid = strtol (ab, (char **) NULL, 10);
368: #endif
369:
370: if (ipid == ime)
371: {
372: struct stat sfile, sdescriptor;
373:
374: /* It looks like we have the lock. Do the final stat
375: check. */
376: if (stat ((char *) zpath, &sfile) < 0)
377: {
378: if (errno != ENOENT)
379: {
380: zerr = "stat";
381: break;
382: }
383: /* Loop around and try again. */
384: }
385: else
386: {
387: if (fstat (o, &sdescriptor) < 0)
388: {
389: zerr = "fstat";
390: break;
391: }
392:
393: if (sfile.st_ino == sdescriptor.st_ino
394: && sfile.st_dev == sdescriptor.st_dev)
395: {
396: /* Close the file before assuming we've succeeded to
397: pick up any trailing errors. */
398: if (close (o) < 0)
399: {
400: zerr = "close";
401: break;
402: }
403:
404: o = -1;
405:
406: /* We have the lock. */
407: fret = TRUE;
408: break;
409: }
410: }
411: }
412:
413: /* Loop around and try the lock again. We keep doing this until
414: the lock file holds a pid that exists. */
415: (void) close (o);
416: o = -1;
417: fret = TRUE;
418: }
419:
420: if (zerr != NULL)
421: {
422: ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno));
423: if (pferr != NULL)
424: *pferr = TRUE;
425: }
426:
427: if (o >= 0)
428: (void) close (o);
429:
430: ubuffree (zfree);
431:
432: /* It would be nice if we could leave the temporary file around for
433: future calls, but considering that we create lock files in
434: various different directories it's probably more trouble than
435: it's worth. */
436: if (remove (ztempfile) != 0)
437: ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno));
438:
439: ubuffree (ztempfile);
440:
441: return fret;
442: }
443:
444: /* Unlock something. The fspooldir argument is as in fsdo_lock. */
445:
446: boolean
447: fsdo_unlock (zlock, fspooldir)
448: const char *zlock;
449: boolean fspooldir;
450: {
451: char *zfree;
452: const char *zpath;
453:
454: if (fspooldir)
455: {
456: zfree = NULL;
457: zpath = zlock;
458: }
459: else
460: {
461: zfree = zsysdep_in_dir (zSlockdir, zlock);
462: zpath = zfree;
463: }
464:
465: if (remove (zpath) == 0
466: || errno == ENOENT)
467: {
468: ubuffree (zfree);
469: return TRUE;
470: }
471: else
472: {
473: ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno));
474: ubuffree (zfree);
475: return FALSE;
476: }
477: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.