Improve dotlocking.
[gnupg.git] / util / dotlock.c
1 /* dotlock.c - dotfile locking
2  * Copyright (C) 1998, 1999, 2000, 2001, 2004,
3  *               2005, 2009 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 #ifdef  HAVE_DOSISH_SYSTEM
29 # define WIN32_LEAN_AND_MEAN
30 # include <windows.h>
31 #else
32 # include <sys/utsname.h>
33 #endif
34 #include <sys/types.h>
35 #ifndef _WIN32
36 #include <sys/time.h>
37 #endif
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include "types.h"
42 #include "util.h"
43 #include "memory.h"
44 #include "i18n.h"
45
46
47 /* The object describing a lock.  */
48 struct dotlock_handle {
49     struct dotlock_handle *next;
50     char *tname;    /* name of lockfile template */
51     char *lockname; /* name of the real lockfile */
52     int locked;     /* lock status */
53     int disable;    /* locking */
54 #ifdef HAVE_DOSISH_SYSTEM
55     HANDLE lockhd;  /* The W32 handle of the lock file.  */
56 #else
57     size_t nodename_off; /* Offset in TNAME to the nodename part. */
58     size_t nodename_len; /* Length of the nodename part.  */
59 #endif
60 };
61
62
63 /* A list of of all lock handles. */
64 static volatile DOTLOCK all_lockfiles;
65
66 /* If this has the value true all locking is disabled.  */
67 static int never_lock;
68
69
70 #ifdef _REENTRANT
71  /* fixme: acquire mutex on all_lockfiles */
72 # define lock_all_lockfiles(h)  do {  } while (0) 
73 # define unlock_all_lockfiles(h) do {  } while (0)
74 #else
75 # define lock_all_lockfiles(h)  do {  } while (0) 
76 # define unlock_all_lockfiles(h) do {  } while (0)
77 #endif
78
79
80
81 void
82 disable_dotlock(void)
83 {
84   never_lock = 1;
85 }
86
87
88 /****************
89  * Create a lockfile with the given name and return an object of
90  * type DOTLOCK which may be used later to actually do the lock.
91  * A cleanup routine gets installed to cleanup left over locks
92  * or other files used together with the lockmechanism.
93  * Althoug the function is called dotlock, this does not necessarily
94  * mean that real lockfiles are used - the function may decide to
95  * use fcntl locking.  Calling the function with NULL only install
96  * the atexit handler and maybe used to assure that the cleanup
97  * is called after all other atexit handlers.
98  *
99  * Notes: This function creates a lock file in the same directory
100  *        as file_to_lock with the name "file_to_lock.lock"
101  *        A temporary file ".#lk.<hostname>.pid[.threadid] is used.
102  *        This function does nothing for Windoze.
103  */
104 DOTLOCK
105 create_dotlock( const char *file_to_lock )
106 {
107     static int initialized;
108     DOTLOCK h;
109 #ifndef HAVE_DOSISH_SYSTEM
110     int fd = -1;
111     char pidstr[16];
112     struct utsname utsbuf;
113     const char *nodename;
114     const char *dirpart;
115     int dirpartlen;
116     size_t tnamelen;
117     int n;
118 #endif /*!HAVE_DOSISH_SYSTEM*/
119
120     if( !initialized ) {
121         atexit( remove_lockfiles );
122         initialized = 1;
123     }
124     if( !file_to_lock )
125         return NULL;
126
127     h = xmalloc_clear( sizeof *h );
128     if( never_lock ) {
129         h->disable = 1;
130         lock_all_lockfiles (h);
131         h->next = all_lockfiles;
132         all_lockfiles = h;
133         return h;
134     }
135
136
137 #ifndef HAVE_DOSISH_SYSTEM
138     snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() );
139
140     /* create a temporary file */
141     if( uname( &utsbuf ) )
142         nodename = "unknown";
143     else
144         nodename = utsbuf.nodename;
145
146 # ifdef __riscos__
147     {
148         char *iter = (char *) nodename;
149         for (; iter[0]; iter++)
150             if (iter[0] == '.')
151                 iter[0] = '/';
152     }
153 # endif /* __riscos__ */
154
155     if( !(dirpart = strrchr( file_to_lock, DIRSEP_C )) ) {
156         dirpart = EXTSEP_S;
157         dirpartlen = 1;
158     }
159     else {
160         dirpartlen = dirpart - file_to_lock;
161         dirpart = file_to_lock;
162     }
163
164     lock_all_lockfiles (h);
165     h->next = all_lockfiles;
166     all_lockfiles = h;
167
168     tnamelen = dirpartlen + 6+30+ strlen(nodename) + 10;
169     h->tname = xmalloc (tnamelen + 1);
170     h->nodename_len = strlen (nodename);
171
172 # ifndef __riscos__
173     snprintf (h->tname, tnamelen, "%.*s/.#lk%p.%n%s.%d",
174               dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ());
175 # else /* __riscos__ */
176     snprintf (h->tname, tnamelen, "%.*s.lk%p/%n%s/%d",
177               dirpartlen, dirpart, (void *)h, &n, nodename, (int)getpid ());
178 # endif /* __riscos__ */
179     h->nodename_off = n;
180
181     do {
182         errno = 0;
183         fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL,
184                    S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR);
185     } while( fd == -1 && errno == EINTR );
186     if( fd == -1 ) {
187         all_lockfiles = h->next;
188         log_error( "failed to create temporary file `%s': %s\n",
189                                             h->tname, strerror(errno));
190         xfree(h->tname);
191         xfree(h);
192         return NULL;
193     }
194     if (write (fd, pidstr, 11) != 11) 
195       goto write_failed;
196     if (write (fd, nodename, strlen (nodename)) != strlen (nodename))
197       goto write_failed;
198     if (write (fd, "\n", 1 ) != 1)
199       goto write_failed;
200     if (close (fd))
201       goto write_failed;
202
203     unlock_all_lockfiles (h);
204     h->lockname = xmalloc( strlen(file_to_lock) + 6 );
205     strcpy(stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
206     return h;
207
208  write_failed:
209     all_lockfiles = h->next;
210     unlock_all_lockfiles (h);
211     log_error ("error writing to `%s': %s\n", h->tname, strerror(errno));
212     close (fd);
213     unlink (h->tname);
214     xfree (h->tname);
215     xfree (h);
216     return NULL;
217
218 #else /* HAVE_DOSISH_SYSTEM */
219
220   /* The Windows version does not need a temporary file but uses the
221      plain lock file along with record locking.  We create this file
222      here so that we later do only need to do the file locking.  For
223      error reporting it is useful to keep the name of the file in the
224      handle.  */
225   h->next = all_lockfiles;
226   all_lockfiles = h;
227
228   h->lockname = xmalloc ( strlen (file_to_lock) + 6 );
229   strcpy (stpcpy(h->lockname, file_to_lock), EXTSEP_S "lock");
230
231   /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE
232      along with FILE_SHARE_DELETE but that does not work due to a race
233      condition: Despite the OPEN_ALWAYS flag CreateFile may return an
234      error and we can't reliable create/open the lock file unless we
235      would wait here until it works - however there are other valid
236      reasons why a lock file can't be created and thus the process
237      would not stop as expected but spin until Windows crashes.  Our
238      solution is to keep the lock file open; that does not harm. */ 
239   h->lockhd = CreateFile (h->lockname,
240                           GENERIC_READ|GENERIC_WRITE,
241                           FILE_SHARE_READ|FILE_SHARE_WRITE,
242                           NULL, OPEN_ALWAYS, 0, NULL);
243   if (h->lockhd == INVALID_HANDLE_VALUE)
244     {
245       log_error (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
246       all_lockfiles = h->next;
247       xfree (h->lockname);
248       xfree (h);
249       return NULL;
250     }
251   return h;
252
253 #endif /* HAVE_DOSISH_SYSTEM */
254 }
255
256
257 void
258 destroy_dotlock ( DOTLOCK h )
259 {
260   DOTLOCK hprev, htmp;
261
262   if (!h)
263     return;
264
265   /* First remove the handle from our global list of all locks. */
266   for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
267     if (htmp == h)
268       {
269         if (hprev)
270           hprev->next = htmp->next;
271         else
272           all_lockfiles = htmp->next;
273         h->next = NULL;
274         break;
275       }
276
277   /* Second destroy the lock. */
278   if (!h->disable)
279     {
280 #ifdef HAVE_DOSISH_SYSTEM
281       if (h->locked)
282         UnlockFile (h->lockhd, 0, 0, 1, 0);
283       CloseHandle (h->lockhd);
284 #else
285       if (h->locked && h->lockname)
286         unlink (h->lockname);
287       if (h->tname)
288         unlink (h->tname);
289       xfree (h->tname);
290 #endif
291       xfree (h->lockname);
292     }
293   xfree(h);
294 }
295
296
297 #ifndef HAVE_DOSISH_SYSTEM
298 static int
299 maybe_deadlock( DOTLOCK h )
300 {
301     DOTLOCK r;
302
303     for( r=all_lockfiles; r; r = r->next ) {
304         if( r != h && r->locked )
305             return 1;
306     }
307     return 0;
308 }
309 #endif /* !HAVE_DOSISH_SYSTEM */
310
311
312 /* Read the lock file and return the pid, returns -1 on error.  True
313    will be stored in the integer at address SAME_NODE if the lock file
314    has been created on the same node. */
315 #ifndef HAVE_DOSISH_SYSTEM
316 static int
317 read_lockfile (DOTLOCK h, int *same_node )
318 {
319   char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node
320                                    names are usually shorter.  */
321   int fd;
322   int pid = -1;
323   char *buffer, *p;
324   size_t expected_len;
325   int res, nread;
326   
327   *same_node = 0;
328   expected_len = 10 + 1 + h->nodename_len + 1;
329   if ( expected_len >= sizeof buffer_space)
330     buffer = xmalloc (expected_len);
331   else
332     buffer = buffer_space;
333
334   if ( (fd = open (h->lockname, O_RDONLY)) == -1 )
335     {
336       int e = errno;
337       log_info ("error opening lockfile `%s': %s\n",
338                 h->lockname, strerror(errno) );
339       if (buffer != buffer_space)
340         xfree (buffer);
341       errno = e; /* Need to return ERRNO here. */
342       return -1;
343     }
344
345   p = buffer;
346   nread = 0;
347   do
348     {
349       res = read (fd, p, expected_len - nread);
350       if (res == -1 && errno == EINTR)
351         continue;
352       if (res < 0)
353         {
354           log_info ("error reading lockfile `%s'", h->lockname );
355           close (fd); 
356           if (buffer != buffer_space)
357             xfree (buffer);
358           errno = 0; /* Do not return an inappropriate ERRNO. */
359           return -1;
360         }
361       p += res;
362       nread += res;
363     }
364   while (res && nread != expected_len);
365   close (fd);
366
367   if (nread < 11)
368     {
369       log_info ("invalid size of lockfile `%s'", h->lockname );
370       if (buffer != buffer_space)
371         xfree (buffer);
372       errno = 0; /* Better don't return an inappropriate ERRNO. */
373       return -1;
374     }
375
376   if (buffer[10] != '\n'
377       || (buffer[10] = 0, pid = atoi (buffer)) == -1
378 #ifndef __riscos__
379       || !pid 
380 #else /* __riscos__ */
381       || (!pid && riscos_getpid())
382 #endif /* __riscos__ */
383       )
384     {
385       log_error ("invalid pid %d in lockfile `%s'", pid, h->lockname );
386       if (buffer != buffer_space)
387         xfree (buffer);
388       errno = 0;
389       return -1;
390     }
391
392   if (nread == expected_len
393       && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) 
394       && buffer[11+h->nodename_len] == '\n')
395     *same_node = 1;
396
397   if (buffer != buffer_space)
398     xfree (buffer);
399   return pid;
400 }
401 #endif /* !HAVE_DOSISH_SYSTEM */
402
403
404 /****************
405  * Do a lock on H. A TIMEOUT of 0 returns immediately,
406  * -1 waits forever (hopefully not), other
407  * values are timeouts in milliseconds.
408  * Returns: 0 on success
409  */
410 int
411 make_dotlock( DOTLOCK h, long timeout )
412 {
413   int backoff=0;
414 #ifndef HAVE_DOSISH_SYSTEM
415   int  pid;
416   const char *maybe_dead="";
417   int same_node;
418 #endif  
419  
420   if (h->disable)
421     return 0;
422
423   if (h->locked)
424     {
425 #ifndef __riscos__
426       log_debug ("oops, `%s' is already locked\n", h->lockname);
427 #endif /* !__riscos__ */
428       return 0;
429     }
430   
431   for (;;)
432     {
433 #ifndef HAVE_DOSISH_SYSTEM
434 # ifndef __riscos__
435       if( !link(h->tname, h->lockname) )
436         {
437           /* fixme: better use stat to check the link count */
438           h->locked = 1;
439           return 0; /* okay */
440         }
441       if (errno != EEXIST)
442         {
443           log_error ("lock not made: link() failed: %s\n", strerror(errno));
444           return -1;
445         }
446 # else /* __riscos__ */
447       if ( !riscos_renamefile(h->tname, h->lockname) )
448         {
449           h->locked = 1;
450           return 0; /* okay */
451         }
452       if (errno != EEXIST)
453         {
454           log_error ("lock not made: rename() failed: %s\n", strerror(errno));
455           return -1;
456         }
457 # endif /* __riscos__ */
458
459       if ((pid = read_lockfile (h, &same_node)) == -1 )
460         {
461           if (errno != ENOENT)
462             {
463               log_info ("cannot read lockfile\n");
464               return -1;
465             }
466           log_info ("lockfile disappeared\n");
467           continue;
468         }
469       else if (pid == getpid ())
470         {
471           log_info ("Oops: lock already held by us\n");
472           h->locked = 1;
473           return 0; /* okay */
474         }
475       else if (same_node && kill(pid, 0) && errno == ESRCH)
476         {
477 # ifndef __riscos__
478           log_info (_("removing stale lockfile (created by %d)\n"), pid );
479           unlink (h->lockname);
480           continue;
481 # else /* __riscos__ */
482           /* We are *pretty* sure that the other task is dead and
483              therefore we remove the other lock file. */
484           maybe_dead = " - probably dead - removing lock";
485           unlink (h->lockname);
486 # endif /* __riscos__ */
487         }
488
489       if (timeout == -1)
490         {
491           struct timeval tv;
492
493           log_info ("waiting for lock (held by %d%s) %s...\n",
494                     pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
495
496
497             /* We can't use sleep, because signals may be blocked.  */
498             tv.tv_sec = 1 + backoff;
499             tv.tv_usec = 0;
500             select (0, NULL, NULL, NULL, &tv);
501             if (backoff < 10)
502               backoff++ ;
503         }
504       else
505         return -1;
506
507 #else /*HAVE_DOSISH_SYSTEM*/
508       int w32err;
509       
510       if (LockFile (h->lockhd, 0, 0, 1, 0))
511         {
512           h->locked = 1;
513           return 0; /* okay */
514         }
515       w32err = GetLastError ();
516       if (w32err != ERROR_LOCK_VIOLATION)
517         {
518           log_error (_("lock `%s' not made: %s\n"),
519                      h->lockname, w32_strerror (w32err));
520           return -1;
521         }
522       
523       if (timeout == -1) 
524         {
525           /* Wait until lock has been released. */
526           log_info (_("waiting for lock %s...\n"), h->lockname);
527           Sleep ((1 + backoff)*1000);
528           if ( backoff < 10 )
529             backoff++ ;
530         }
531       else
532         return -1;
533 #endif /*HAVE_DOSISH_SYSTEM*/
534     }
535   /*NOTREACHED */
536 }
537
538
539 /****************
540  * release a lock
541  * Returns: 0 := success
542  */
543 int
544 release_dotlock (DOTLOCK h)
545 {
546 #ifndef HAVE_DOSISH_SYSTEM
547   int pid, same_node;
548 #endif
549
550   /* To avoid atexit race conditions we first check whether there are
551      any locks left.  It might happen that another atexit handler
552      tries to release the lock while the atexit handler of this module
553      already ran and thus H is undefined.  */
554   if (!all_lockfiles)
555     return 0;
556
557   if (h->disable) 
558     return 0;
559
560   if (!h->locked)
561     {
562       log_debug("oops, `%s' is not locked\n", h->lockname );
563       return 0;
564     }
565
566 #ifndef HAVE_DOSISH_SYSTEM
567   pid = read_lockfile (h, &same_node);
568   if (pid == -1)
569     {
570       log_error ("release_dotlock: lockfile error\n");
571       return -1;
572     }
573   if (pid != getpid () || !same_node)
574     {
575       log_error ("release_dotlock: not our lock (pid=%d)\n", pid);
576       return -1;
577     }
578 # ifndef __riscos__
579   if (unlink (h->lockname))
580     {
581       log_error ("release_dotlock: error removing lockfile `%s'",
582                  h->lockname);
583       return -1;
584     }
585 # else /* __riscos__ */
586   if( riscos_renamefile(h->lockname, h->tname) ) 
587     {
588       log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
589                  h->lockname, h->tname);
590       return -1;
591     }
592 # endif /* __riscos__ */
593
594 #else /*HAVE_DOSISH_SYSTEM*/
595
596   if (!UnlockFile (h->lockhd, 0, 0, 1, 0))
597     {
598       log_error ("release_dotlock: error removing lockfile `%s': %s\n",
599                  h->lockname, w32_strerror (-1));
600       return -1;
601     }
602 #endif /*HAVE_DOSISH_SYSTEM*/
603
604   h->locked = 0;
605   return 0;
606 }
607
608
609 void
610 remove_lockfiles()
611 {
612 #if !defined (HAVE_DOSISH_SYSTEM)
613     DOTLOCK h, h2;
614
615     h = all_lockfiles;
616     all_lockfiles = NULL;
617
618     while( h ) {
619         h2 = h->next;
620         destroy_dotlock (h);
621         h = h2;
622     }
623 #endif
624 }