* gpg.texi (GPG Configuration Options): Make http_proxy option
[gnupg.git] / jnlib / dotlock.c
blobba89bcea62cf7cd0996d2d52e2f31f7aa203c57a
1 /* dotlock.c - dotfile locking
2 * Copyright (C) 1998, 2000, 2001, 2003, 2004,
3 * 2005, 2006 Free Software Foundation, Inc.
5 * This file is part of JNLIB.
7 * JNLIB is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * JNLIB is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
23 #include <config.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #ifndef HAVE_DOSISH_SYSTEM
31 #include <sys/utsname.h>
32 #endif
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <signal.h>
39 #include "libjnlib-config.h"
40 #include "dotlock.h"
42 #if !defined(DIRSEP_C) && !defined(EXTSEP_C) \
43 && !defined(DIRSEP_S) && !defined(EXTSEP_S)
44 #ifdef HAVE_DOSISH_SYSTEM
45 #define DIRSEP_C '\\'
46 #define EXTSEP_C '.'
47 #define DIRSEP_S "\\"
48 #define EXTSEP_S "."
49 #else
50 #define DIRSEP_C '/'
51 #define EXTSEP_C '.'
52 #define DIRSEP_S "/"
53 #define EXTSEP_S "."
54 #endif
55 #endif
58 struct dotlock_handle
60 struct dotlock_handle *next;
61 char *tname; /* Name of lockfile template. */
62 size_t nodename_off; /* Offset in TNAME of the nodename part. */
63 size_t nodename_len; /* Length of the nodename part. */
64 char *lockname; /* Name of the real lockfile. */
65 int locked; /* Lock status. */
66 int disable; /* When true, locking is disabled. */
70 static volatile DOTLOCK all_lockfiles;
71 static int never_lock;
73 static int read_lockfile (DOTLOCK h, int *same_node);
75 void
76 disable_dotlock(void)
78 never_lock = 1;
81 /****************
82 * Create a lockfile with the given name and return an object of
83 * type DOTLOCK which may be used later to actually do the lock.
84 * A cleanup routine gets installed to cleanup left over locks
85 * or other files used together with the lock mechanism.
86 * Although the function is called dotlock, this does not necessarily
87 * mean that real lockfiles are used - the function may decide to
88 * use fcntl locking. Calling the function with NULL only install
89 * the atexit handler and maybe used to assure that the cleanup
90 * is called after all other atexit handlers.
92 * Notes: This function creates a lock file in the same directory
93 * as file_to_lock with the name "file_to_lock.lock"
94 * A temporary file ".#lk.<hostname>.pid[.threadid] is used.
95 * This function does nothing for Windoze.
97 DOTLOCK
98 create_dotlock( const char *file_to_lock )
100 static int initialized;
101 DOTLOCK h;
102 int fd = -1;
103 char pidstr[16];
104 const char *nodename;
105 const char *dirpart;
106 int dirpartlen;
107 #ifndef HAVE_DOSISH_SYSTEM
108 struct utsname utsbuf;
109 #endif
111 if ( !initialized )
113 atexit( dotlock_remove_lockfiles );
114 initialized = 1;
116 if ( !file_to_lock )
117 return NULL; /* Only initialization was requested. */
119 h = jnlib_xcalloc ( 1, sizeof *h );
120 if( never_lock )
122 h->disable = 1;
123 #ifdef _REENTRANT
124 /* fixme: aquire mutex on all_lockfiles */
125 #endif
126 h->next = all_lockfiles;
127 all_lockfiles = h;
128 return h;
131 #ifndef HAVE_DOSISH_SYSTEM
132 sprintf (pidstr, "%10d\n", (int)getpid() );
133 /* fixme: add the hostname to the second line (FQDN or IP addr?) */
135 /* Create a temporary file. */
136 if ( uname ( &utsbuf ) )
137 nodename = "unknown";
138 else
139 nodename = utsbuf.nodename;
141 #ifdef __riscos__
143 char *iter = (char *) nodename;
144 for (; iter[0]; iter++)
145 if (iter[0] == '.')
146 iter[0] = '/';
148 #endif /* __riscos__ */
150 if ( !(dirpart = strrchr ( file_to_lock, DIRSEP_C )) )
152 dirpart = EXTSEP_S;
153 dirpartlen = 1;
155 else
157 dirpartlen = dirpart - file_to_lock;
158 dirpart = file_to_lock;
161 #ifdef _REENTRANT
162 /* fixme: aquire mutex on all_lockfiles */
163 #endif
164 h->next = all_lockfiles;
165 all_lockfiles = h;
167 h->tname = jnlib_xmalloc ( dirpartlen + 6+30+ strlen(nodename) + 11 );
168 h->nodename_len = strlen (nodename);
169 #ifndef __riscos__
170 sprintf (h->tname, "%.*s/.#lk%p.", dirpartlen, dirpart, h );
171 h->nodename_off = strlen (h->tname);
172 sprintf (h->tname+h->nodename_off, "%s.%d", nodename, (int)getpid ());
173 #else /* __riscos__ */
174 sprintf (h->tname, "%.*s.lk%p/", dirpartlen, dirpart, h );
175 h->nodename_off = strlen (h->tname);
176 sprintf (h->tname+h->nodename_off, "%s/%d", nodename, (int)getpid () );
177 #endif /* __riscos__ */
181 errno = 0;
182 fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
183 S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
185 while (fd == -1 && errno == EINTR);
187 if ( fd == -1 )
189 all_lockfiles = h->next;
190 log_error ( "failed to create temporary file `%s': %s\n",
191 h->tname, strerror(errno));
192 jnlib_free(h->tname);
193 jnlib_free(h);
194 return NULL;
196 if ( write (fd, pidstr, 11 ) != 11 )
197 goto write_failed;
198 if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) )
199 goto write_failed;
200 if ( write (fd, "\n", 1 ) != 1 )
201 goto write_failed;
202 if ( close (fd) )
203 goto write_failed;
205 # ifdef _REENTRANT
206 /* release mutex */
207 # endif
208 #endif /* !HAVE_DOSISH_SYSTEM */
209 h->lockname = jnlib_xmalloc ( strlen (file_to_lock) + 6 );
210 strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
211 return h;
212 write_failed:
213 all_lockfiles = h->next;
214 # ifdef _REENTRANT
215 /* fixme: release mutex */
216 # endif
217 log_error ( "error writing to `%s': %s\n", h->tname, strerror(errno) );
218 close(fd);
219 unlink(h->tname);
220 jnlib_free(h->tname);
221 jnlib_free(h);
222 return NULL;
226 void
227 destroy_dotlock ( DOTLOCK h )
229 #ifndef HAVE_DOSISH_SYSTEM
230 if ( h )
232 DOTLOCK hprev, htmp;
234 /* First remove the handle from our global list of all locks. */
235 for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
236 if (htmp == h)
238 if (hprev)
239 hprev->next = htmp->next;
240 else
241 all_lockfiles = htmp->next;
242 h->next = NULL;
243 break;
246 /* Second destroy the lock. */
247 if (!h->disable)
249 if (h->locked && h->lockname)
250 unlink (h->lockname);
251 if (h->tname)
252 unlink (h->tname);
253 jnlib_free (h->tname);
254 jnlib_free (h->lockname);
256 jnlib_free(h);
258 #endif /*!HAVE_DOSISH_SYSTEM*/
263 static int
264 maybe_deadlock( DOTLOCK h )
266 DOTLOCK r;
268 for ( r=all_lockfiles; r; r = r->next )
270 if ( r != h && r->locked )
271 return 1;
273 return 0;
276 /****************
277 * Do a lock on H. A TIMEOUT of 0 returns immediately, -1 waits
278 * forever (hopefully not), other values are reserved (should then be
279 * timeouts in milliseconds). Returns: 0 on success
282 make_dotlock( DOTLOCK h, long timeout )
284 #ifdef HAVE_DOSISH_SYSTEM
285 return 0;
286 #else
287 int pid;
288 const char *maybe_dead="";
289 int backoff=0;
290 int same_node;
292 if ( h->disable )
293 return 0; /* Locks are completely disabled. Return success. */
295 if ( h->locked )
297 #ifndef __riscos__
298 log_debug("oops, `%s' is already locked\n", h->lockname );
299 #endif /* !__riscos__ */
300 return 0;
303 for(;;)
305 #ifndef __riscos__
306 if ( !link(h->tname, h->lockname) )
308 /* fixme: better use stat to check the link count */
309 h->locked = 1;
310 return 0; /* okay */
312 if ( errno != EEXIST )
314 log_error( "lock not made: link() failed: %s\n", strerror(errno) );
315 return -1;
317 #else /* __riscos__ */
318 if ( !renamefile(h->tname, h->lockname) )
320 h->locked = 1;
321 return 0; /* okay */
323 if ( errno != EEXIST )
325 log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
326 return -1;
328 #endif /* __riscos__ */
330 if ( (pid = read_lockfile (h, &same_node)) == -1 )
332 if ( errno != ENOENT )
334 log_info ("cannot read lockfile\n");
335 return -1;
337 log_info( "lockfile disappeared\n");
338 continue;
340 else if ( pid == getpid() && same_node )
342 log_info( "Oops: lock already held by us\n");
343 h->locked = 1;
344 return 0; /* okay */
346 else if ( same_node && kill (pid, 0) && errno == ESRCH )
348 #ifndef __riscos__
349 log_info ("removing stale lockfile (created by %d)", pid );
350 unlink (h->lockname);
351 continue;
352 #else /* __riscos__ */
353 /* Under RISCOS we are *pretty* sure that the other task
354 is dead and therefore we remove the stale lock file. */
355 maybe_dead = " - probably dead - removing lock";
356 unlink(h->lockname);
357 #endif /* __riscos__ */
360 if ( timeout == -1 )
362 /* Wait until lock has been released. */
363 struct timeval tv;
365 log_info ("waiting for lock (held by %d%s) %s...\n",
366 pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
369 /* We can't use sleep, cause signals may be blocked. */
370 tv.tv_sec = 1 + backoff;
371 tv.tv_usec = 0;
372 select(0, NULL, NULL, NULL, &tv);
373 if ( backoff < 10 )
374 backoff++ ;
376 else
377 return -1;
379 /*NOTREACHED*/
380 #endif /* !HAVE_DOSISH_SYSTEM */
384 /****************
385 * release a lock
386 * Returns: 0 := success
389 release_dotlock( DOTLOCK h )
391 #ifdef HAVE_DOSISH_SYSTEM
392 return 0;
393 #else
394 int pid, same_node;
396 /* To avoid atexit race conditions we first check whether there are
397 any locks left. It might happen that another atexit handler
398 tries to release the lock while the atexit handler of this module
399 already ran and thus H is undefined. */
400 if (!all_lockfiles)
401 return 0;
403 if ( h->disable )
404 return 0;
406 if ( !h->locked )
408 log_debug("oops, `%s' is not locked\n", h->lockname );
409 return 0;
412 pid = read_lockfile (h, &same_node);
413 if ( pid == -1 )
415 log_error( "release_dotlock: lockfile error\n");
416 return -1;
418 if ( pid != getpid() || !same_node )
420 log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
421 return -1;
423 #ifndef __riscos__
424 if ( unlink( h->lockname ) )
426 log_error( "release_dotlock: error removing lockfile `%s'",
427 h->lockname);
428 return -1;
430 #else /* __riscos__ */
431 if ( renamefile(h->lockname, h->tname) )
433 log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
434 h->lockname, h->tname);
435 return -1;
437 #endif /* __riscos__ */
438 /* fixme: check that the link count is now 1 */
439 h->locked = 0;
440 return 0;
441 #endif /* !HAVE_DOSISH_SYSTEM */
446 Read the lock file and return the pid, returns -1 on error. True
447 will be stored at SAME_NODE if the lock file has been created on
448 the same node.
450 static int
451 read_lockfile (DOTLOCK h, int *same_node )
453 #ifdef HAVE_DOSISH_SYSTEM
454 return 0;
455 #else
456 char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
457 name are usually shorter. */
458 int fd, pid;
459 char *buffer, *p;
460 size_t expected_len;
461 int res, nread;
463 *same_node = 0;
464 expected_len = 10 + 1 + h->nodename_len + 1;
465 if ( expected_len >= sizeof buffer_space)
466 buffer = jnlib_xmalloc (expected_len);
467 else
468 buffer = buffer_space;
470 if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
472 int e = errno;
473 log_info ("error opening lockfile `%s': %s\n",
474 h->lockname, strerror(errno) );
475 if (buffer != buffer_space)
476 jnlib_free (buffer);
477 errno = e; /* Need to return ERRNO here. */
478 return -1;
481 p = buffer;
482 nread = 0;
485 res = read (fd, p, expected_len - nread);
486 if (res == -1 && errno == EINTR)
487 continue;
488 if (res < 0)
490 log_info ("error reading lockfile `%s'", h->lockname );
491 close (fd);
492 if (buffer != buffer_space)
493 jnlib_free (buffer);
494 errno = 0; /* Do not return an inappropriate ERRNO. */
495 return -1;
497 p += res;
498 nread += res;
500 while (res && nread != expected_len);
501 close(fd);
503 if (nread < 11)
505 log_info ("invalid size of lockfile `%s'", h->lockname );
506 if (buffer != buffer_space)
507 jnlib_free (buffer);
508 errno = 0; /* Do not return an inappropriate ERRNO. */
509 return -1;
512 if (buffer[10] != '\n'
513 || (buffer[10] = 0, pid = atoi (buffer)) == -1
514 #ifndef __riscos__
515 || !pid
516 #else /* __riscos__ */
517 || (!pid && riscos_getpid())
518 #endif /* __riscos__ */
521 log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname );
522 if (buffer != buffer_space)
523 jnlib_free (buffer);
524 errno = 0;
525 return -1;
528 if (nread == expected_len
529 && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len)
530 && buffer[11+h->nodename_len] == '\n')
531 *same_node = 1;
533 if (buffer != buffer_space)
534 jnlib_free (buffer);
535 return pid;
536 #endif
540 void
541 dotlock_remove_lockfiles()
543 #ifndef HAVE_DOSISH_SYSTEM
544 DOTLOCK h, h2;
546 h = all_lockfiles;
547 all_lockfiles = NULL;
549 while ( h )
551 h2 = h->next;
552 destroy_dotlock (h);
553 h = h2;
555 #endif