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