* cardglue.c (open_card): Ask for card insertion.
[gnupg.git] / util / dotlock.c
1 /* dotlock.c - dotfile locking
2  *      Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
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 static int read_lockfile( const char *name );
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     int  fd = -1;
84     char pidstr[16];
85 #if !defined (HAVE_DOSISH_SYSTEM)
86     struct utsname utsbuf;
87 #endif
88     const char *nodename;
89     const char *dirpart;
90     int dirpartlen;
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 static int
201 maybe_deadlock( DOTLOCK h )
202 {
203     DOTLOCK r;
204
205     for( r=all_lockfiles; r; r = r->next ) {
206         if( r != h && r->locked )
207             return 1;
208     }
209     return 0;
210 }
211
212 /****************
213  * Do a lock on H. A TIMEOUT of 0 returns immediately,
214  * -1 waits forever (hopefully not), other
215  * values are timeouts in milliseconds.
216  * Returns: 0 on success
217  */
218 int
219 make_dotlock( DOTLOCK h, long timeout )
220 {
221 #if defined (HAVE_DOSISH_SYSTEM)
222     return 0;
223 #else
224     int  pid;
225     const char *maybe_dead="";
226     int backoff=0;
227
228     if( h->disable ) {
229         return 0;
230     }
231
232     if( h->locked ) {
233 #ifndef __riscos__
234         log_debug("oops, `%s' is already locked\n", h->lockname );
235 #endif /* !__riscos__ */
236         return 0;
237     }
238
239     for(;;) {
240 #ifndef __riscos__
241         if( !link(h->tname, h->lockname) ) {
242             /* fixme: better use stat to check the link count */
243             h->locked = 1;
244             return 0; /* okay */
245         }
246         if( errno != EEXIST ) {
247             log_error( "lock not made: link() failed: %s\n", strerror(errno) );
248             return -1;
249         }
250 #else /* __riscos__ */
251         if( !riscos_renamefile(h->tname, h->lockname) ) {
252             h->locked = 1;
253             return 0; /* okay */
254         }
255         if( errno != EEXIST ) {
256             log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
257             return -1;
258         }
259 #endif /* __riscos__ */
260         if( (pid = read_lockfile(h->lockname)) == -1 ) {
261             if( errno != ENOENT ) {
262                 log_info("cannot read lockfile\n");
263                 return -1;
264             }
265             log_info( "lockfile disappeared\n");
266             continue;
267         }
268         else if( pid == getpid() ) {
269             log_info( "Oops: lock already held by us\n");
270             h->locked = 1;
271             return 0; /* okay */
272         }
273         else if( kill(pid, 0) && errno == ESRCH ) {
274 #ifndef __riscos__
275             maybe_dead = " - probably dead";
276 #if 0 /* we should not do this without checking the permissions */
277                /* and the hostname */
278             log_info( "removing stale lockfile (created by %d)", pid );
279 #endif
280 #else /* __riscos__ */
281             /* we are *pretty* sure that the other task is dead and therefore
282                we remove the other lock file */
283             maybe_dead = " - probably dead - removing lock";
284             unlink(h->lockname);
285 #endif /* __riscos__ */
286         }
287         if( timeout == -1 ) {
288             struct timeval tv;
289             log_info( "waiting for lock (held by %d%s) %s...\n",
290                       pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
291
292
293             /* can't use sleep, cause signals may be blocked */
294             tv.tv_sec = 1 + backoff;
295             tv.tv_usec = 0;
296             select(0, NULL, NULL, NULL, &tv);
297             if( backoff < 10 )
298                 backoff++ ;
299         }
300         else
301             return -1;
302     }
303     /*not reached */
304 #endif
305 }
306
307
308 /****************
309  * release a lock
310  * Returns: 0 := success
311  */
312 int
313 release_dotlock( DOTLOCK h )
314 {
315 #if defined (HAVE_DOSISH_SYSTEM)
316     return 0;
317 #else
318     int pid;
319
320     if( h->disable ) {
321         return 0;
322     }
323
324     if( !h->locked ) {
325         log_debug("oops, `%s' is not locked\n", h->lockname );
326         return 0;
327     }
328
329     pid = read_lockfile( h->lockname );
330     if( pid == -1 ) {
331         log_error( "release_dotlock: lockfile error\n");
332         return -1;
333     }
334     if( pid != getpid() ) {
335         log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
336         return -1;
337     }
338 #ifndef __riscos__
339     if( unlink( h->lockname ) ) {
340         log_error( "release_dotlock: error removing lockfile `%s'",
341                                                         h->lockname);
342         return -1;
343     }
344 #else /* __riscos__ */
345     if( riscos_renamefile(h->lockname, h->tname) ) {
346         log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
347                                                         h->lockname, h->tname);
348         return -1;
349     }
350 #endif /* __riscos__ */
351     /* fixme: check that the link count is now 1 */
352     h->locked = 0;
353     return 0;
354 #endif
355 }
356
357
358 /****************
359  * Read the lock file and return the pid, returns -1 on error.
360  */
361 static int
362 read_lockfile( const char *name )
363 {
364 #if defined (HAVE_DOSISH_SYSTEM)
365     return 0;
366 #else
367     int fd, pid;
368     char pidstr[16];
369
370     if( (fd = open(name, O_RDONLY)) == -1 ) {
371         int e = errno;
372         log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
373         errno = e;
374         return -1;
375     }
376     if( read(fd, pidstr, 10 ) != 10 ) {  /* Read 10 digits w/o newline */
377         log_debug("error reading lockfile `%s'", name );
378         close(fd);
379         errno = 0;
380         return -1;
381     }
382     pidstr[10] = 0;  /* terminate pid string */
383     close(fd);
384     pid = atoi(pidstr);
385 #ifndef __riscos__
386     if( !pid || pid == -1 ) {
387 #else /* __riscos__ */
388     if( (!pid && riscos_getpid()) || pid == -1 ) {
389 #endif /* __riscos__ */
390         log_error("invalid pid %d in lockfile `%s'", pid, name );
391         errno = 0;
392         return -1;
393     }
394     return pid;
395 #endif
396 }
397
398
399 void
400 remove_lockfiles()
401 {
402 #if !defined (HAVE_DOSISH_SYSTEM)
403     DOTLOCK h, h2;
404
405     h = all_lockfiles;
406     all_lockfiles = NULL;
407
408     while( h ) {
409         h2 = h->next;
410         if( !h->disable ) {
411             if( h->locked )
412                 unlink( h->lockname );
413             unlink(h->tname);
414             m_free(h->tname);
415             m_free(h->lockname);
416         }
417         m_free(h);
418         h = h2;
419     }
420 #endif
421 }