~ubuntu-branches/ubuntu/karmic/gnupg2/karmic-security

« back to all changes in this revision

Viewing changes to util/dotlock.c

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-10-04 10:25:53 UTC
  • mfrom: (5.1.15 intrepid)
  • Revision ID: james.westby@ubuntu.com-20081004102553-fv62pp8dsitxli47
Tags: 2.0.9-3.1
* Non-maintainer upload.
* agent/gpg-agent.c: Deinit the threading library before exec'ing
  the command to run in --daemon mode. And because that still doesn't
  restore the sigprocmask, do that manually. Closes: #499569

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* dotlock.c - dotfile locking
2
 
 * Copyright (C) 1998, 1999, 2000, 2001, 2004 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
 
 
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
 
 
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
 
 * Do a lock on H. A TIMEOUT of 0 returns immediately,
253
 
 * -1 waits forever (hopefully not), other
254
 
 * values are timeouts in milliseconds.
255
 
 * Returns: 0 on success
256
 
 */
257
 
int
258
 
make_dotlock( DOTLOCK h, long timeout )
259
 
{
260
 
#if defined (HAVE_DOSISH_SYSTEM)
261
 
    return 0;
262
 
#else
263
 
    int  pid;
264
 
    const char *maybe_dead="";
265
 
    int backoff=0;
266
 
 
267
 
    if( h->disable ) {
268
 
        return 0;
269
 
    }
270
 
 
271
 
    if( h->locked ) {
272
 
#ifndef __riscos__
273
 
        log_debug("oops, `%s' is already locked\n", h->lockname );
274
 
#endif /* !__riscos__ */
275
 
        return 0;
276
 
    }
277
 
 
278
 
    for(;;) {
279
 
#ifndef __riscos__
280
 
        if( !link(h->tname, h->lockname) ) {
281
 
            /* fixme: better use stat to check the link count */
282
 
            h->locked = 1;
283
 
            return 0; /* okay */
284
 
        }
285
 
        if( errno != EEXIST ) {
286
 
            log_error( "lock not made: link() failed: %s\n", strerror(errno) );
287
 
            return -1;
288
 
        }
289
 
#else /* __riscos__ */
290
 
        if( !riscos_renamefile(h->tname, h->lockname) ) {
291
 
            h->locked = 1;
292
 
            return 0; /* okay */
293
 
        }
294
 
        if( errno != EEXIST ) {
295
 
            log_error( "lock not made: rename() failed: %s\n", strerror(errno) );
296
 
            return -1;
297
 
        }
298
 
#endif /* __riscos__ */
299
 
        if( (pid = read_lockfile(h->lockname)) == -1 ) {
300
 
            if( errno != ENOENT ) {
301
 
                log_info("cannot read lockfile\n");
302
 
                return -1;
303
 
            }
304
 
            log_info( "lockfile disappeared\n");
305
 
            continue;
306
 
        }
307
 
        else if( pid == getpid() ) {
308
 
            log_info( "Oops: lock already held by us\n");
309
 
            h->locked = 1;
310
 
            return 0; /* okay */
311
 
        }
312
 
        else if( kill(pid, 0) && errno == ESRCH ) {
313
 
#ifndef __riscos__
314
 
            maybe_dead = " - probably dead";
315
 
#if 0 /* we should not do this without checking the permissions */
316
 
               /* and the hostname */
317
 
            log_info( "removing stale lockfile (created by %d)", pid );
318
 
#endif
319
 
#else /* __riscos__ */
320
 
            /* we are *pretty* sure that the other task is dead and therefore
321
 
               we remove the other lock file */
322
 
            maybe_dead = " - probably dead - removing lock";
323
 
            unlink(h->lockname);
324
 
#endif /* __riscos__ */
325
 
        }
326
 
        if( timeout == -1 ) {
327
 
            struct timeval tv;
328
 
            log_info( "waiting for lock (held by %d%s) %s...\n",
329
 
                      pid, maybe_dead, maybe_deadlock(h)? "(deadlock?) ":"");
330
 
 
331
 
 
332
 
            /* can't use sleep, cause signals may be blocked */
333
 
            tv.tv_sec = 1 + backoff;
334
 
            tv.tv_usec = 0;
335
 
            select(0, NULL, NULL, NULL, &tv);
336
 
            if( backoff < 10 )
337
 
                backoff++ ;
338
 
        }
339
 
        else
340
 
            return -1;
341
 
    }
342
 
    /*not reached */
343
 
#endif
344
 
}
345
 
 
346
 
 
347
 
/****************
348
 
 * release a lock
349
 
 * Returns: 0 := success
350
 
 */
351
 
int
352
 
release_dotlock( DOTLOCK h )
353
 
{
354
 
#if defined (HAVE_DOSISH_SYSTEM)
355
 
    return 0;
356
 
#else
357
 
    int pid;
358
 
 
359
 
    /* To avoid atexit race conditions we first check whether there
360
 
       are any locks left.  It might happen that another atexit
361
 
       handler tries to release the lock while the atexit handler of
362
 
       this module already ran and thus H is undefined.  */
363
 
    if(!all_lockfiles)
364
 
        return 0;
365
 
 
366
 
    if( h->disable ) 
367
 
        return 0;
368
 
 
369
 
    if( !h->locked ) {
370
 
        log_debug("oops, `%s' is not locked\n", h->lockname );
371
 
        return 0;
372
 
    }
373
 
 
374
 
    pid = read_lockfile( h->lockname );
375
 
    if( pid == -1 ) {
376
 
        log_error( "release_dotlock: lockfile error\n");
377
 
        return -1;
378
 
    }
379
 
    if( pid != getpid() ) {
380
 
        log_error( "release_dotlock: not our lock (pid=%d)\n", pid);
381
 
        return -1;
382
 
    }
383
 
#ifndef __riscos__
384
 
    if( unlink( h->lockname ) ) {
385
 
        log_error( "release_dotlock: error removing lockfile `%s'",
386
 
                                                        h->lockname);
387
 
        return -1;
388
 
    }
389
 
#else /* __riscos__ */
390
 
    if( riscos_renamefile(h->lockname, h->tname) ) {
391
 
        log_error( "release_dotlock: error renaming lockfile `%s' to `%s'",
392
 
                                                        h->lockname, h->tname);
393
 
        return -1;
394
 
    }
395
 
#endif /* __riscos__ */
396
 
    /* fixme: check that the link count is now 1 */
397
 
    h->locked = 0;
398
 
    return 0;
399
 
#endif
400
 
}
401
 
 
402
 
 
403
 
/****************
404
 
 * Read the lock file and return the pid, returns -1 on error.
405
 
 */
406
 
static int
407
 
read_lockfile( const char *name )
408
 
{
409
 
#if defined (HAVE_DOSISH_SYSTEM)
410
 
    return 0;
411
 
#else
412
 
    int fd, pid;
413
 
    char pidstr[16];
414
 
 
415
 
    if( (fd = open(name, O_RDONLY)) == -1 ) {
416
 
        int e = errno;
417
 
        log_debug("error opening lockfile `%s': %s\n", name, strerror(errno) );
418
 
        errno = e;
419
 
        return -1;
420
 
    }
421
 
    if( read(fd, pidstr, 10 ) != 10 ) {  /* Read 10 digits w/o newline */
422
 
        log_debug("error reading lockfile `%s'", name );
423
 
        close(fd);
424
 
        errno = 0;
425
 
        return -1;
426
 
    }
427
 
    pidstr[10] = 0;  /* terminate pid string */
428
 
    close(fd);
429
 
    pid = atoi(pidstr);
430
 
#ifndef __riscos__
431
 
    if( !pid || pid == -1 ) {
432
 
#else /* __riscos__ */
433
 
    if( (!pid && riscos_getpid()) || pid == -1 ) {
434
 
#endif /* __riscos__ */
435
 
        log_error("invalid pid %d in lockfile `%s'", pid, name );
436
 
        errno = 0;
437
 
        return -1;
438
 
    }
439
 
    return pid;
440
 
#endif
441
 
}
442
 
 
443
 
 
444
 
void
445
 
remove_lockfiles()
446
 
{
447
 
#if !defined (HAVE_DOSISH_SYSTEM)
448
 
    DOTLOCK h, h2;
449
 
 
450
 
    h = all_lockfiles;
451
 
    all_lockfiles = NULL;
452
 
 
453
 
    while( h ) {
454
 
        h2 = h->next;
455
 
        destroy_dotlock (h);
456
 
        h = h2;
457
 
    }
458
 
#endif
459
 
}