89edb7b91e2973731976203b4e3a58f726491020
[gnupg.git] / jnlib / dotlock.c
1 /* dotlock.c - dotfile locking
2  * Copyright (C) 1998, 2000, 2001, 2003, 2004, 
3  *               2005 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20  * USA.
21  */
22
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>
38
39 #include "libjnlib-config.h"
40 #include "dotlock.h"
41
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
56
57
58 struct dotlock_handle {
59     struct dotlock_handle *next;
60     char *tname;    /* name of lockfile template */
61     char *lockname; /* name of the real lockfile */
62     int locked;     /* lock status */
63     int disable;    /* locking */
64 };
65
66
67 static volatile DOTLOCK all_lockfiles;
68 static int never_lock;
69
70 static int read_lockfile( const char *name );
71
72 void
73 disable_dotlock(void)
74 {
75     never_lock = 1;
76 }
77
78 /****************
79  * Create a lockfile with the given name and return an object of
80  * type DOTLOCK which may be used later to actually do the lock.
81  * A cleanup routine gets installed to cleanup left over locks
82  * or other files used together with the lockmechanism.
83  * Althoug the function is called dotlock, this does not necessarily
84  * mean that real lockfiles are used - the function may decide to
85  * use fcntl locking.  Calling the function with NULL only install
86  * the atexit handler and maybe used to assure that the cleanup
87  * is called after all other atexit handlers.
88  *
89  * Notes: This function creates a lock file in the same directory
90  *        as file_to_lock with the name "file_to_lock.lock"
91  *        A temporary file ".#lk.<hostname>.pid[.threadid] is used.
92  *        This function does nothing for Windoze.
93  */
94 DOTLOCK
95 create_dotlock( const char *file_to_lock )
96 {
97     static int initialized;
98     DOTLOCK h;
99     int  fd = -1;
100     char pidstr[16];
101   #ifndef  HAVE_DOSISH_SYSTEM
102     struct utsname utsbuf;
103   #endif
104     const char *nodename;
105     const char *dirpart;
106     int dirpartlen;
107
108     if( !initialized ) {
109         atexit( dotlock_remove_lockfiles );
110         initialized = 1;
111     }
112     if( !file_to_lock )
113         return NULL;
114
115     h = jnlib_xcalloc( 1, sizeof *h );
116     if( never_lock ) {
117         h->disable = 1;
118 #ifdef _REENTRANT
119         /* fixme: aquire mutex on all_lockfiles */
120 #endif
121         h->next = all_lockfiles;
122         all_lockfiles = h;
123         return h;
124     }
125
126 #ifndef HAVE_DOSISH_SYSTEM
127     sprintf( pidstr, "%10d\n", (int)getpid() );
128     /* fixme: add the hostname to the second line (FQDN or IP addr?) */
129
130     /* create a temporary file */
131     if( uname( &utsbuf ) )
132         nodename = "unknown";
133     else
134         nodename = utsbuf.nodename;
135
136 #ifdef __riscos__
137     {
138         char *iter = (char *) nodename;
139         for (; iter[0]; iter++)
140             if (iter[0] == '.')
141                 iter[0] = '/';
142     }
143 #endif /* __riscos__ */
144
145     if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) {
146         dirpart = EXTSEP_S;
147         dirpartlen = 1;
148     }
149     else {
150         dirpartlen = dirpart - file_to_lock;
151         dirpart = file_to_lock;
152     }
153
154 #ifdef _REENTRANT
155     /* fixme: aquire mutex on all_lockfiles */
156 #endif
157     h->next = all_lockfiles;
158     all_lockfiles = h;
159
160     h->tname = jnlib_xmalloc( dirpartlen + 6+30+ strlen(nodename) + 11 );
161 #ifndef __riscos__
162      sprintf( h->tname, "%.*s/.#lk%p.%s.%d",
163              dirpartlen, dirpart, h, nodename, (int)getpid() );
164 #else /* __riscos__ */
165     sprintf( h->tname, "%.*s.lk%p/%s/%d",
166              dirpartlen, dirpart, h, nodename, (int)getpid() );
167 #endif /* __riscos__ */
168
169     do {
170         errno = 0;
171         fd = open( h->tname, O_WRONLY|O_CREAT|O_EXCL,
172                           S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR );
173     } while( fd == -1 && errno == EINTR );
174     if( fd == -1 ) {
175         all_lockfiles = h->next;
176         log_error( "failed to create temporary file `%s': %s\n",
177                                             h->tname, strerror(errno));
178         jnlib_free(h->tname);
179         jnlib_free(h);
180         return NULL;
181     }
182     if( write(fd, pidstr, 11 ) != 11 ) {
183         all_lockfiles = h->next;
184       #ifdef _REENTRANT
185         /* release mutex */
186       #endif
187         log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
188         close(fd);
189         unlink(h->tname);
190         jnlib_free(h->tname);
191         jnlib_free(h);
192         return NULL;
193     }
194     if( close(fd) ) {
195         all_lockfiles = h->next;
196       #ifdef _REENTRANT
197         /* release mutex */
198       #endif
199         log_fatal( "error writing to `%s': %s\n", h->tname, strerror(errno) );
200         close(fd);
201         unlink(h->tname);
202         jnlib_free(h->tname);
203         jnlib_free(h);
204         return NULL;
205     }
206
207 # ifdef _REENTRANT
208     /* release mutex */
209 # endif
210 #endif /* !HAVE_DOSISH_SYSTEM */
211     h->lockname = jnlib_xmalloc( strlen(file_to_lock) + 6 );
212     strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
213     return h;
214 }
215
216
217 void
218 destroy_dotlock ( DOTLOCK h )
219 {
220 #if !defined (HAVE_DOSISH_SYSTEM)
221     if ( h )
222       {
223         DOTLOCK hprev, htmp;
224
225         /* First remove the handle from our global list of all locks. */
226         for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
227           if (htmp == h)
228             {
229               if (hprev)
230                 hprev->next = htmp->next;
231               else
232                 all_lockfiles = htmp->next;
233               h->next = NULL;
234               break;
235             }
236
237         /* Second destroy the lock. */
238         if (!h->disable)
239           {
240             if (h->locked && h->lockname)
241               unlink (h->lockname);
242             if (h->tname)
243               unlink (h->tname);
244             jnlib_free (h->tname);
245             jnlib_free (h->lockname);
246           }
247         jnlib_free(h);
248
249       }
250 #endif
251 }
252
253
254
255 static int
256 maybe_deadlock( DOTLOCK h )
257 {
258     DOTLOCK r;
259
260     for( r=all_lockfiles; r; r = r->next ) {
261         if( r != h && r->locked )
262             return 1;
263     }
264     return 0;
265 }
266
267 /****************
268  * Do a lock on H. A TIMEOUT of 0 returns immediately,
269  * -1 waits forever (hopefully not), other
270  * values are timeouts in milliseconds.
271  * Returns: 0 on success
272  */
273 int
274 make_dotlock( DOTLOCK h, long timeout )
275 {
276 #ifdef HAVE_DOSISH_SYSTEM
277     return 0;
278 #else
279     int  pid;
280     const char *maybe_dead="";
281     int backoff=0;
282
283     if( h->disable ) {
284         return 0;
285     }
286
287     if( h->locked ) {
288 #ifndef __riscos__
289         log_debug("oops, `%s' is already locked\n", h->lockname );
290 #endif /* !__riscos__ */
291         return 0;
292     }
293
294     for(;;) {
295 #ifndef __riscos__
296         if( !link(h->tname, h->lockname) ) {
297             /* fixme: better use stat to check the link count */
298             h->locked = 1;
299             return 0; /* okay */
300         }
301         if( errno != EEXIST ) {
302             log_error( "lock not made: link() failed: %s\n", strerror(errno) );
303             return -1;
304         }
305 #else /* __riscos__ */
306         if( !renamefile(h->tname, h->lockname) ) {
307             h->locked = 1;
308             return 0; /* okay */
309         }
310         if( errno != EEXIST ) {
311             log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
312             return -1;
313         }
314 #endif /* __riscos__ */
315         if( (pid = read_lockfile(h->lockname)) == -1 ) {
316             if( errno != ENOENT ) {
317                 log_info("cannot read lockfile\n");
318                 return -1;
319             }
320             log_info( "lockfile disappeared\n");
321             continue;
322         }
323         else if( pid == getpid() ) {
324             log_info( "Oops: lock already held by us\n");
325             h->locked = 1;
326             return 0; /* okay */
327         }
328         else if( kill(pid, 0) && errno == ESRCH ) {
329 #ifndef __riscos__
330             maybe_dead = " - probably dead";
331 #if 0 /* we should not do this without checking the permissions */
332                /* and the hostname */
333             log_info( "removing stale lockfile (created by %d)", pid );
334 #endif
335 #else /* __riscos__ */
336             /* we are *pretty* sure that the other task is dead and therefore
337                we remove the other lock file */
338             maybe_dead = " - probably dead - removing lock";
339             unlink(h->lockname);
340 #endif /* __riscos__ */
341         }
342         if( timeout == -1 ) {
343             struct timeval tv;
344             log_info( "waiting for lock (held by %d%s) %s...\n",
345                       pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
346
347
348             /* can't use sleep, cause signals may be blocked */
349             tv.tv_sec = 1 + backoff;
350             tv.tv_usec = 0;
351             select(0, NULL, NULL, NULL, &tv);
352             if( backoff < 10 )
353                 backoff++ ;
354         }
355         else
356             return -1;
357     }
358     /*not reached */
359 #endif /* !HAVE_DOSISH_SYSTEM */
360 }
361
362
363 /****************
364  * release a lock
365  * Returns: 0 := success
366  */
367 int
368 release_dotlock( DOTLOCK h )
369 {
370 #ifdef HAVE_DOSISH_SYSTEM
371     return 0;
372 #else
373     int pid;
374
375     /* To avoid atexit race conditions we first check whether there
376        are any locks left.  It might happen that another atexit
377        handler tries to release the lock while the atexit handler of
378        this module already ran and thus H is undefined.  */
379     if(!all_lockfiles)
380         return 0;
381
382     if( h->disable ) {
383         return 0;
384     }
385
386     if( !h->locked ) {
387         log_debug("oops, `%s' is not locked\n", h->lockname );
388         return 0;
389     }
390
391     pid = read_lockfile( h->lockname );
392     if( pid == -1 ) {
393         log_error( "release_dotlock: lockfile error\n");
394         return -1;
395     }
396     if( pid != getpid() ) {
397         log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
398         return -1;
399     }
400 #ifndef __riscos__
401     if( unlink( h->lockname ) ) {
402         log_error( "release_dotlock: error removing lockfile `%s'",
403                                                         h->lockname);
404         return -1;
405     }
406 #else /* __riscos__ */
407     if( renamefile(h->lockname, h->tname) ) {
408         log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
409                                                         h->lockname, h->tname);
410         return -1;
411     }
412 #endif /* __riscos__ */
413     /* fixme: check that the link count is now 1 */
414     h->locked = 0;
415     return 0;
416 #endif /* !HAVE_DOSISH_SYSTEM */
417 }
418
419
420 /****************
421  * Read the lock file and return the pid, returns -1 on error.
422  */
423 static int
424 read_lockfile( const char *name )
425 {
426 #ifdef HAVE_DOSISH_SYSTEM
427     return 0;
428 #else
429     int fd, pid;
430     char pidstr[16];
431
432     if( (fd = open(name, O_RDONLY)) == -1 ) {
433         int e = errno;
434         log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
435         errno = e;
436         return -1;
437     }
438     if( read(fd, pidstr, 10 ) != 10 ) {  /* Read 10 digits w/o newline */
439         log_debug("error reading lockfile `%s'", name );
440         close(fd);
441         errno = 0;
442         return -1;
443     }
444     pidstr[10] = 0;  /* terminate pid string */
445     close(fd);
446     pid = atoi(pidstr);
447 #ifndef __riscos__
448     if( !pid || pid == -1 ) {
449 #else /* __riscos__ */
450     if( (!pid && riscos_getpid()) || pid == -1 ) {
451 #endif /* __riscos__ */
452         log_error("invalid pid %d in lockfile `%s'", pid, name );
453         errno = 0;
454         return -1;
455     }
456     return pid;
457 #endif
458 }
459
460
461 void
462 dotlock_remove_lockfiles()
463 {
464 #ifndef HAVE_DOSISH_SYSTEM
465   DOTLOCK h, h2;
466   
467   h = all_lockfiles;
468   all_lockfiles = NULL;
469     
470   while ( h )
471     {
472       h2 = h->next;
473       destroy_dotlock (h);
474       h = h2;
475     }
476 #endif
477 }
478