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