~ubuntu-branches/ubuntu/quantal/liblockfile/quantal-proposed

« back to all changes in this revision

Viewing changes to lockfile.c

  • Committer: Bazaar Package Importer
  • Author(s): Miquel van Smoorenburg
  • Date: 2001-04-06 09:53:32 UTC
  • Revision ID: james.westby@ubuntu.com-20010406095332-xtysb4vtpotoa4fx
Tags: 1.03
* Some more manpage updates
* Set priority for liblockfile1 to "important" (closes: #92551)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * lockfile.c   Safely creates a lockfile, also over NFS.
 
3
 *              This file also holds the implementation for
 
4
 *              the Svr4 maillock functions.
 
5
 *
 
6
 * Version:     @(#)lockfile.c  1.0  05-Jun-1999  miquels@cistron.nl
 
7
 *
 
8
 *              Copyright (C) Miquel van Smoorenburg 1997,1998,1999.
 
9
 *
 
10
 *              This library is free software; you can redistribute it and/or
 
11
 *              modify it under the terms of the GNU Library General Public
 
12
 *              License as published by the Free Software Foundation; either
 
13
 *              version 2 of the License, or (at your option) any later version.
 
14
 */
 
15
 
 
16
#include "autoconf.h"
 
17
 
 
18
#include <sys/types.h>
 
19
#if HAVE_SYS_PARAM_H
 
20
#include <sys/param.h>
 
21
#endif
 
22
#include <sys/stat.h>
 
23
#include <sys/wait.h>
 
24
#include <stdio.h>
 
25
#include <string.h>
 
26
#include <signal.h>
 
27
#include <fcntl.h>
 
28
#include <stdlib.h>
 
29
#include <unistd.h>
 
30
#include <time.h>
 
31
#include <errno.h>
 
32
#include <lockfile.h>
 
33
#include <maillock.h>
 
34
 
 
35
#ifdef HAVE_UTIME
 
36
#include <utime.h>
 
37
#endif
 
38
 
 
39
#ifdef LIB
 
40
static char mlockfile[MAXPATHLEN];
 
41
static int  islocked = 0;
 
42
#endif
 
43
 
 
44
#ifndef LIB
 
45
extern int check_sleep(int);
 
46
#endif
 
47
 
 
48
#if !defined(LIB) || defined(MAILGROUP)
 
49
/*
 
50
 *      See if we can write to the directory.
 
51
 *      Returns: -1 fail
 
52
 *                0 OK writable
 
53
 */
 
54
#ifdef LIB
 
55
static
 
56
#endif
 
57
int eaccess(char *fn, gid_t gid, struct stat *st)
 
58
{
 
59
        struct stat     tmp;
 
60
        uid_t           uid = geteuid();
 
61
 
 
62
        if (st == NULL) st = &tmp;
 
63
 
 
64
        if (stat(fn, st) != 0)
 
65
                return -1;
 
66
        errno = EPERM;
 
67
        if (uid == 0) return 0;
 
68
        if (st->st_uid == uid)
 
69
                return (st->st_mode & 0200) ? 0 : -1;
 
70
        if (st->st_gid == gid)
 
71
                return (st->st_mode & 0020) ? 0 : -1;
 
72
        return (st->st_mode & 0002) ? 0 : -1;
 
73
}
 
74
#endif
 
75
 
 
76
#if defined(LIB) && defined(MAILGROUP)
 
77
/*
 
78
 *      Can we write to the directory of the lockfile ?
 
79
 */
 
80
static int need_extern(const char *file)
 
81
{
 
82
        gid_t           egid = getegid();
 
83
        struct stat     st;
 
84
        static gid_t    mailgid = -1;
 
85
        char            dir[MAXPATHLEN];
 
86
        char            *p;
 
87
 
 
88
        /*
 
89
         *      Find directory.
 
90
         */
 
91
        strcpy(dir, file);
 
92
        if ((p = strrchr(dir, '/')) != NULL)
 
93
                *p = 0;
 
94
        else
 
95
                strcpy(dir, ".");
 
96
 
 
97
        if (eaccess(dir, egid, NULL) >= 0)
 
98
                return 0;
 
99
 
 
100
        /*
 
101
         *      See if accessible for group mail. We find out what
 
102
         *      "group mail" is by statting LOCKPROG, that saves us
 
103
         *      from having to call getgrgid() in a library.
 
104
         */
 
105
        if (mailgid == (gid_t)-1) {
 
106
                if (stat(LOCKPROG, &st) < 0 || !(st.st_mode & S_ISGID))
 
107
                        return 0;
 
108
                mailgid = st.st_gid;
 
109
        }
 
110
        return (eaccess(dir, mailgid, NULL) >= 0);
 
111
}
 
112
 
 
113
/*
 
114
 *      Call external program to do the actual locking.
 
115
 */
 
116
static int do_extern(char *opt, const char *lockfile, int retries, int flags)
 
117
{
 
118
        sigset_t        set, oldset;
 
119
        char            buf[4];
 
120
        pid_t           pid, n;
 
121
        int             st;
 
122
 
 
123
        /*
 
124
         *      Block SIGCHLD. The main program might have installed
 
125
         *      handlers we don't want to call.
 
126
         */
 
127
        sigemptyset(&set);
 
128
        sigaddset(&set, SIGCHLD);
 
129
        sigprocmask(SIG_BLOCK, &set, &oldset);
 
130
 
 
131
        /*
 
132
         *      Fork, execute locking program and wait.
 
133
         */
 
134
        if ((pid = fork()) < 0)
 
135
                return L_ERROR;
 
136
        if (pid == 0) {
 
137
                sprintf(buf, "%d", retries % 1000);
 
138
                execl(LOCKPROG, LOCKPROG, opt, "-r", buf, "-q",
 
139
                        (flags & L_PID) ? "-p" : "-N", lockfile, NULL);
 
140
                _exit(L_ERROR);
 
141
        }
 
142
 
 
143
        /*
 
144
         *      Wait for return status - do something appropriate
 
145
         *      if program died or returned L_ERROR.
 
146
         */
 
147
        while ((n = waitpid(pid, &st, 0)) != pid)
 
148
                if (n < 0 && errno != EINTR)
 
149
                        break;
 
150
        if (!sigismember(&oldset, SIGCHLD))
 
151
                sigprocmask(SIG_UNBLOCK, &set, NULL);
 
152
        if (n < 0)
 
153
                return L_ERROR;
 
154
        if (!WIFEXITED(st) || WEXITSTATUS(st) == L_ERROR) {
 
155
                errno = EINTR;
 
156
                return L_ERROR;
 
157
        }
 
158
 
 
159
        return WEXITSTATUS(st);
 
160
}
 
161
 
 
162
#endif
 
163
 
 
164
/*
 
165
 *      Create a lockfile.
 
166
 */
 
167
int lockfile_create(const char *lockfile, int retries, int flags)
 
168
{
 
169
        struct stat     st, st1;
 
170
        char            tmplock[MAXPATHLEN];
 
171
        char            sysname[256];
 
172
        char            buf[8];
 
173
        char            *p;
 
174
        int             sleeptime = 5;
 
175
        int             statfailed = 0;
 
176
        int             fd;
 
177
        int             i, e, len;
 
178
 
 
179
        /*
 
180
         *      Safety measure.
 
181
         */
 
182
        if (strlen(lockfile) + 32 > MAXPATHLEN) {
 
183
                errno = ENAMETOOLONG;
 
184
                return L_ERROR;
 
185
        }
 
186
 
 
187
#if defined(LIB) && defined(MAILGROUP)
 
188
        if (need_extern(lockfile))
 
189
                return do_extern("-l", lockfile, retries, flags);
 
190
#endif
 
191
 
 
192
        /*
 
193
         *      Create a temp lockfile (hopefully unique) and write
 
194
         *      either our pid/ppid in it, or 0\0 for svr4 compatibility.
 
195
         */
 
196
        if (gethostname(sysname, sizeof(sysname)) < 0)
 
197
                return L_ERROR;
 
198
        if ((p = strchr(sysname, '.')) != NULL)
 
199
                *p = 0;
 
200
        strcpy(tmplock, lockfile);
 
201
        if ((p = strrchr(tmplock, '/')) == NULL)
 
202
                p = tmplock;
 
203
        else
 
204
                p++;
 
205
        sprintf(p, ".lk%05d%x%s",
 
206
                (int)getpid(), (int)time(NULL) & 15, sysname);
 
207
        i = umask(022);
 
208
        fd = open(tmplock, O_WRONLY|O_CREAT|O_EXCL, 0644);
 
209
        e = errno;
 
210
        umask(i);
 
211
        if (fd < 0) {
 
212
                errno = e;
 
213
                return L_TMPLOCK;
 
214
        }
 
215
        if (flags & (L_PID | L_PPID)) {
 
216
                sprintf(buf, "%d\n",
 
217
                        (flags & L_PID) ? (int)getpid() : (int)getppid());
 
218
                p = buf;
 
219
                len = strlen(buf);
 
220
        } else {
 
221
                p = "0";
 
222
                len = 2;
 
223
        }
 
224
        i = write(fd, p, len);
 
225
        e = errno;
 
226
        if (close(fd) != 0) {
 
227
                e = errno;
 
228
                i = -1;
 
229
        }
 
230
        if (i != len) {
 
231
                unlink(tmplock);
 
232
                errno = i < 0 ? e : EAGAIN;
 
233
                return L_TMPWRITE;
 
234
        }
 
235
 
 
236
        /*
 
237
         *      Now try to link the temporary lock to the lock.
 
238
         */
 
239
        for (i = 0; i < retries && retries > 0; i++) {
 
240
 
 
241
                sleeptime = i > 12 ? 60 : 5 * i;
 
242
                if (sleeptime > 0)
 
243
#ifdef LIB
 
244
                        sleep(sleeptime);
 
245
#else
 
246
                        if ((e = check_sleep(sleeptime)) != 0) {
 
247
                                unlink(tmplock);
 
248
                                return e;
 
249
                        }
 
250
#endif
 
251
                /*
 
252
                 *      Now lock by linking the tempfile to the lock.
 
253
                 *
 
254
                 *      KLUDGE: some people say the return code of
 
255
                 *      link() over NFS can't be trusted.
 
256
                 *      EXTRA FIX: the value of the nlink field
 
257
                 *      can't be trusted (may be cached).
 
258
                 */
 
259
                (void)link(tmplock, lockfile);
 
260
 
 
261
                if (lstat(tmplock, &st1) < 0)
 
262
                        return L_ERROR; /* Can't happen */
 
263
 
 
264
                if (lstat(lockfile, &st) < 0) {
 
265
                        if (statfailed++ > 5) {
 
266
                                /*
 
267
                                 *      Normally, this can't happen; either
 
268
                                 *      another process holds the lockfile or
 
269
                                 *      we do. So if this error pops up
 
270
                                 *      repeatedly, just exit...
 
271
                                 */
 
272
                                e = errno;
 
273
                                (void)unlink(tmplock);
 
274
                                errno = e;
 
275
                                return L_MAXTRYS;
 
276
                        }
 
277
                        continue;
 
278
                }
 
279
 
 
280
                /*
 
281
                 *      See if we got the lock.
 
282
                 */
 
283
                if (st.st_rdev == st1.st_rdev &&
 
284
                    st.st_ino  == st1.st_ino) {
 
285
                        (void)unlink(tmplock);
 
286
                        return L_SUCCESS;
 
287
                }
 
288
                statfailed = 0;
 
289
 
 
290
                /*
 
291
                 *      If there is a lockfile and it is invalid,
 
292
                 *      remove the lockfile.
 
293
                 */
 
294
                if (lockfile_check(lockfile, flags) == -1)
 
295
                        unlink(lockfile);
 
296
 
 
297
        }
 
298
        (void)unlink(tmplock);
 
299
        errno = EAGAIN;
 
300
        return L_MAXTRYS;
 
301
}
 
302
 
 
303
/*
 
304
 *      See if a valid lockfile is present.
 
305
 *      Returns 0 if so, -1 if not.
 
306
 */
 
307
int lockfile_check(const char *lockfile, int flags)
 
308
{
 
309
        struct stat     st;
 
310
        char            buf[16];
 
311
        time_t          now;
 
312
        pid_t           pid;
 
313
        int             fd, len, r;
 
314
 
 
315
        if (stat(lockfile, &st) < 0)
 
316
                return -1;
 
317
 
 
318
        /*
 
319
         *      Get the contents and mtime of the lockfile.
 
320
         *      Use the time of the file system.
 
321
         */
 
322
        time(&now);
 
323
        pid = 0;
 
324
        if ((fd = open(lockfile, O_RDONLY)) >= 0) {
 
325
                if ((len = read(fd, buf, sizeof(buf))) >= 0 &&
 
326
                    fstat(fd, &st) == 0)
 
327
                        now = st.st_atime;
 
328
                close(fd);
 
329
                if (len > 0 && (flags & (L_PID|L_PPID))) {
 
330
                buf[len] = 0;
 
331
                        pid = atoi(buf);
 
332
                }
 
333
        }
 
334
 
 
335
        if (pid > 0) {
 
336
                /*
 
337
                 *      If we have a pid, see if the process
 
338
                 *      owning the lockfile is still alive.
 
339
                 */
 
340
                r = kill(pid, 0);
 
341
                if (r == 0 || errno == EPERM)
 
342
                        return 0;
 
343
                if (r < 0 && errno == ESRCH)
 
344
                        return -1;
 
345
                /* EINVAL - FALLTHRU */
 
346
        }
 
347
 
 
348
        /*
 
349
         *      Without a pid in the lockfile, the lock
 
350
         *      is valid if it is newer than 5 mins.
 
351
         */
 
352
        if (now < st.st_mtime + 300)
 
353
                return 0;
 
354
        return -1;
 
355
}
 
356
 
 
357
/*
 
358
 *      Remove a lock.
 
359
 */
 
360
int lockfile_remove(const char *lockfile)
 
361
{
 
362
#if defined(LIB) && defined(MAILGROUP)
 
363
        if (need_extern(lockfile))
 
364
                return do_extern("-u", lockfile, 0, 0);
 
365
#endif
 
366
        return (unlink(lockfile) < 0 && errno != ENOENT) ? -1 : 0;
 
367
}
 
368
 
 
369
/*
 
370
 *      Touch a lock.
 
371
 */
 
372
int lockfile_touch(const char *lockfile)
 
373
{
 
374
#ifdef HAVE_UTIME
 
375
        return utime(lockfile, NULL);
 
376
#else
 
377
        return utimes(lockfile, NULL);
 
378
#endif
 
379
}
 
380
 
 
381
#ifdef LIB
 
382
/*
 
383
 *      Lock a mailfile. This looks a lot like the SVR4 function.
 
384
 *      Arguments: lusername, retries.
 
385
 */
 
386
int maillock(const char *name, int retries)
 
387
{
 
388
        char            *p, *mail;
 
389
        int             i;
 
390
 
 
391
        if (islocked) return 0;
 
392
        if (strlen(name) + sizeof(MAILDIR) + 6 > MAXPATHLEN) {
 
393
                errno = ENAMETOOLONG;
 
394
                return L_NAMELEN;
 
395
        }
 
396
 
 
397
        /*
 
398
         *      If $MAIL is for the same username as "name"
 
399
         *      then use $MAIL instead.
 
400
         */
 
401
        sprintf(mlockfile, "%s%s.lock", MAILDIR, name);
 
402
        if ((mail = getenv("MAIL")) != NULL) {
 
403
                if ((p = strrchr(mail, '/')) != NULL)
 
404
                        p++;
 
405
                else
 
406
                        p = mail;
 
407
                if (strcmp(p, name) == 0) {
 
408
                        if (strlen(mail) + 6 > MAXPATHLEN) {
 
409
                                errno = ENAMETOOLONG;
 
410
                                return L_NAMELEN;
 
411
                        }
 
412
                        sprintf(mlockfile, "%s.lock", mail);
 
413
                }
 
414
        }
 
415
        i = lockfile_create(mlockfile, retries, 0);
 
416
        if (i == 0) islocked = 1;
 
417
 
 
418
        return i;
 
419
}
 
420
 
 
421
void mailunlock(void)
 
422
{
 
423
        if (!islocked) return;
 
424
        lockfile_remove(mlockfile);
 
425
        islocked = 0;
 
426
}
 
427
 
 
428
void touchlock(void)
 
429
{
 
430
        lockfile_touch(mlockfile);
 
431
}
 
432
#endif
 
433