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