leafnode  1.12.0
About: Leafnode is a store & forward NNTP proxy for small (dialup) sites.
  Fossies Dox: leafnode-1.12.0.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

lockfile.c
Go to the documentation of this file.
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 */
51static nlink_t
52fd_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" */
73static int
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 {
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)
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)) {
143 "erased stale pid %lu host %s lockfile %s",
144 npid, host, name);
145 return 1;
146 } else {
147 if (!quiet)
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. */
162int
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 */
199int
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)) {
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))
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 */
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))
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 */
327int 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}
int verbose
Definition: applyfilter.c:31
int debugmode
Definition: configutil.c:67
char * critmalloc(size_t size, const char *message)
Definition: critmem.c:61
char fqdn[255+1]
Definition: miscutil.c:58
int mkstemp(char *)
Definition: mkstemp.c:27
ssize_t writes(int fd, const char *string)
Definition: writes.c:7
sigjmp_buf timeout
Definition: mgetaline.c:39
const char * lockfile
void ln_log(int sev, int ctx, const char *format,...)
Definition: ln_log.c:103
#define LNLOG_SERR
Definition: ln_log.h:13
#define LNLOG_SCRIT
Definition: ln_log.h:12
#define LNLOG_CTOP
Definition: ln_log.h:22
#define LNLOG_SNOTICE
Definition: ln_log.h:15
int try_lock(unsigned long timeout)
Definition: lockfile.c:200
static nlink_t fd_st_nlink(const int fd)
Definition: lockfile.c:52
int safe_mkstemp(char *templ)
Definition: lockfile.c:163
static int lock_is_stale(const char *const name, const int quiet)
Definition: lockfile.c:74
int handover_lock(pid_t pid)
Definition: lockfile.c:327
#define len
Definition: mastring.c:31
const char * name
Definition: miscutil.c:126
static int quiet
Definition: texpire.c:54
int is_validfqdn(const char *f)
Definition: validatefqdn.c:66