"Fossies" - the Fresh Open Source Software Archive 
Member "leafnode-1.12.0/lockfile.c" (28 Dec 2021, 8794 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "lockfile.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.11.12_vs_1.12.0.
1 /** \file lockfile.c
2 * library module to safely create a lock file.
3 * \author Matthias Andree
4 * \date 2001 - 2006
5 *
6 * Copyright (C) 2001 - 2006 Matthias Andree <matthias.andree@gmx.de>
7 *
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser 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 library 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 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24 #include "leafnode.h"
25 #include "ln_log.h"
26 #include "critmem.h"
27 #include "validatefqdn.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37
38 #ifdef WITH_DMALLOC
39 #include <dmalloc.h>
40 #endif
41
42 #include <signal.h> /* for kill */
43
44 /** get hard link count of open file pointed to by filedes.
45 * uses fstat(2)
46 * \return 0 in case of trouble
47 * (which is also logged), the count of hard links otherwise
48 *
49 * NOTE: you cannot distinguish "fstat error" from "deleted file".
50 */
51 static nlink_t
52 fd_st_nlink(const int fd /** open file descriptor */ )
53 {
54 struct stat st;
55
56 if (fstat(fd, &st)) {
57 ln_log(LNLOG_SERR, LNLOG_CTOP, "Cannot fstat %d: %m", fd);
58 return 0;
59 }
60
61 return st.st_nlink;
62 }
63
64 /** Check if the lock file given by "name" is stale and if so, erase it.
65 * A lock is stale when the process to which it belongs, is dead.
66 *
67 * \bug cannot detect if lock files held by other hosts are stale.
68 *
69 * \return
70 * - -1 for error "failure"
71 * - 0 if lock file is still in use or held by another host "failure"
72 * - 1 if lock file is stale and has been erased "success" */
73 static int
74 lock_is_stale(
75 /** file name of lock file */
76 const char *const name,
77 /** quiet flag */
78 const int quiet)
79 {
80 char buf[512];
81 int fd;
82 int len;
83 char *pid;
84 char *host;
85 char *tmp;
86 unsigned long npid;
87
88 fd = open(name, O_RDONLY, 0);
89 if (fd < 0) {
90 if (errno == ENOENT) {
91 /* file has just disappeared, thus it's not stale */
92 return 0;
93 } else {
94 ln_log(LNLOG_SERR, LNLOG_CTOP,
95 "cannot open %s for reading: %m", name);
96 return -1;
97 }
98 }
99
100 if ((len = read(fd, buf, sizeof(buf) - 1)) < 0) {
101 ln_log(LNLOG_SERR, LNLOG_CTOP, "read error on %s: %m", name);
102 (void)close(fd);
103 return -1;
104 }
105
106 if (close(fd) < 0) {
107 ln_log(LNLOG_SERR, LNLOG_CTOP, "read error on %s: %m", name);
108 return -1;
109 }
110
111 /* read pid and host */
112 buf[len - 1] = '\0';
113 pid = host = buf;
114 /* we expect a single \n here */
115 host += strcspn(host, "\n");
116 *(host++) = '\0';
117
118 /* kill trailing \n */
119 tmp = host;
120 tmp += strcspn(tmp, "\n");
121 *tmp = '\0';
122
123 npid = strtoul(pid, 0, 10);
124 if (npid == ULONG_MAX && errno == ERANGE) {
125 /* overflow error, should not happen, bail out */
126 ln_log(LNLOG_SERR, LNLOG_CTOP, "bogus pid in %s: %m", name);
127 return -1;
128 }
129
130 if (strcasecmp(host, fqdn)) {
131 if (!quiet)
132 ln_log(LNLOG_SERR, LNLOG_CTOP,
133 "lockfile held by pid %lu on host %s, we're %s",
134 npid, host, fqdn);
135 return 0; /* other host holds the lock */
136 }
137
138 /* okay, we can see if there's still a process with that pid active */
139 if (kill((pid_t)npid, 0) && errno == ESRCH) {
140 /* no such process, good */
141 if (!unlink(name)) {
142 ln_log(LNLOG_SNOTICE, LNLOG_CTOP,
143 "erased stale pid %lu host %s lockfile %s",
144 npid, host, name);
145 return 1;
146 } else {
147 if (!quiet)
148 ln_log(LNLOG_SERR, LNLOG_CTOP,
149 "unable to erase stale pid %lu host %s lockfile %s",
150 npid, host, name);
151 return 0;
152 }
153 }
154
155 /* there is a process active */
156 return 0;
157 }
158
159 /** Safe mkstemp replacement.
160 * Ensures the file is only read- and writable by its owner; some systems
161 * create these with 0777 or 0666 permissions. */
162 int
163 safe_mkstemp(
164 /** template to build filename upon, as for mkstemp */
165 char *templ)
166 {
167 mode_t oldmask;
168 int ret;
169
170 oldmask = umask(077);
171 ret = mkstemp(templ);
172 (void)umask(oldmask);
173
174 return ret;
175 }
176
177
178 /**
179 * Try to set a lockfile, blocking or non-blocking.
180 * Checks if the lockfile exists and is active.
181 *
182 * requires: atomic link(2) syscall.
183 *
184 * features:
185 * - NFS safe (but leafnode does not work distributed)
186 * - stale detection by PID in lock file (may be fooled)
187 *
188 * \bug Cannot remove stale lock on other machine.
189 *
190 * \bug Stale detection may be fooled if another process has been
191 * assigned the PID that the last caller had.
192 *
193 * \return
194 * - 0: if locking succeeded
195 * - 1: if locking failed because the lock is held by someone else and
196 * isn't stale
197 * - -1: for other errors
198 */
199 int
200 try_lock(
201 /** Timeout, if nonzero, wait at most this many seconds. */
202 unsigned long timeout)
203 {
204 const int block = 1;
205 char *l2, *pid;
206 int fd;
207 int have_lock = 0;
208 int quiet = 0;
209 const char *const append = ".XXXXXXXXXX";
210 const int have_timeout = (timeout != 0);
211
212 if (debugmode)
213 syslog(LOG_DEBUG,
214 "try_lock(timeout=%lu), fqdn=\"%s\"",
215 timeout, fqdn);
216 if (verbose)
217 printf("try_lock(timeout=%lu), fqdn=\"%s\"\n",
218 timeout, fqdn);
219
220 /* kill bogus fqdn */
221 if (!is_validfqdn(fqdn)) {
222 ln_log(LNLOG_SCRIT, LNLOG_CTOP,
223 "Internal error: "
224 "must not try to lock with local host name \"%s\"", fqdn);
225 return -1;
226 }
227
228 l2 = (char *)critmalloc(strlen(lockfile) + strlen(append) + 1,
229 "try_lock");
230 pid = (char *)critmalloc(strlen(fqdn) + sizeof(unsigned long) * 4 + 4,
231 "try_lock");
232
233 strcpy(l2, lockfile); /* RATS: ignore */
234 strcat(l2, append); /* RATS: ignore */
235
236 /* make a temporary file */
237 fd = safe_mkstemp(l2);
238 if (fd < 0) {
239 ln_log(LNLOG_SERR, LNLOG_CTOP, "mkstemp(%s) failed: %m", l2);
240 free(l2);
241 free(pid);
242 return -1;
243 }
244
245 /* write our PID and host into it (stale detection) */
246 sprintf(pid, "%lu\n%s\n", (unsigned long)getpid(), fqdn);
247 /* safe, see malloc above */
248 if (writes(fd, pid) < 0 || fsync(fd) < 0) {
249 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot write to %s: %m", l2);
250 if (unlink(l2))
251 ln_log(LNLOG_SERR, LNLOG_CTOP,
252 "Cannot remove lock helper file %s: %m", l2);
253 free(l2);
254 free(pid);
255 return -1;
256 }
257
258 /* and try to finally lock */
259 while (!have_lock) {
260 if (0 == link(l2, lockfile)) {
261 /* link succeeded. good. */
262 have_lock = 1;
263 break;
264 } else {
265 if (2 == fd_st_nlink(fd)) {
266 /* link failed, but st_nlink has increased to 2, good. */
267 have_lock = 1;
268 } else {
269 int stale;
270 struct timeval tv = { 1, 0 };
271
272 /* Could not create link. Check if the lock file is stale. */
273 stale = lock_is_stale(lockfile, quiet);
274
275 /* if we have a stale file, it's just been removed.
276 retry, don't care for what block says */
277 if (stale == 1)
278 continue;
279
280 quiet = 1;
281
282 /* if we have a problem with stale detection, or
283 if we are in non-blocking mode, abort */
284 if (stale == -1 || !block)
285 break;
286
287 if (have_timeout) {
288 if (timeout == 0)
289 break;
290
291 --timeout;
292 }
293
294 /* retry after a second, select does not interfere w/ alarm */
295 if (select(0, NULL, NULL, NULL, &tv) < 0) {
296 /* must not happen */
297 ln_log(LNLOG_SERR, LNLOG_CTOP,
298 "try_lock: select failed: %m");
299 break;
300 }
301 }
302 }
303 }
304
305 if (close(fd) < 0) {
306 ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot write to %s: %m", l2);
307 have_lock = 0;
308 }
309
310 /* unlink l2, but just log if unable to unlink, ignore otherwise */
311 if (unlink(l2))
312 ln_log(LNLOG_SERR, LNLOG_CTOP,
313 "Cannot remove lock helper file %s: %m", l2);
314
315 /* clean up */
316 free(l2);
317 free(pid);
318
319 /* mind the return logic */
320 return have_lock ? 0 : 1;
321 }
322
323 /** Tries to hand over lock to the process with the given pid. \return
324 * 0 for success, nonzero for failure -- check errno for details in
325 * case of error.
326 */
327 int handover_lock(pid_t pid) {
328 int fd = open(lockfile, O_RDWR|O_TRUNC, (mode_t)0600);
329 char *buf = (char *)critmalloc(strlen(fqdn) + sizeof(unsigned long) * 4 + 4,
330 "handover_lock");
331 if (fd < 0) { free(buf); return fd; }
332 sprintf(buf, "%lu\n%s\n", (unsigned long)pid, fqdn);
333 if (-1 == writes(fd, buf)) goto close_bail;
334 if (-1 == fsync(fd)) goto close_bail;
335 if (-1 == close(fd)) goto close_bail;
336 free(buf);
337 return 0;
338
339 close_bail:
340 (void)close(fd);
341 free(buf);
342 return -1;
343 }