2
* lockfile.c Safely creates a lockfile, also over NFS.
3
* This file also holds the implementation for
4
* the Svr4 maillock functions.
6
* Version: @(#)lockfile.c 1.0 05-Jun-1999 miquels@cistron.nl
8
* Copyright (C) Miquel van Smoorenburg 1997,1998,1999.
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.
18
#include <sys/types.h>
20
#include <sys/param.h>
40
static char mlockfile[MAXPATHLEN];
41
static int islocked = 0;
45
extern int check_sleep(int);
48
#if !defined(LIB) || defined(MAILGROUP)
50
* See if we can write to the directory.
57
int eaccess(char *fn, gid_t gid, struct stat *st)
60
uid_t uid = geteuid();
62
if (st == NULL) st = &tmp;
64
if (stat(fn, st) != 0)
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;
76
#if defined(LIB) && defined(MAILGROUP)
78
* Can we write to the directory of the lockfile ?
80
static int need_extern(const char *file)
82
gid_t egid = getegid();
84
static gid_t mailgid = -1;
92
if ((p = strrchr(dir, '/')) != NULL)
97
if (eaccess(dir, egid, NULL) >= 0)
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.
105
if (mailgid == (gid_t)-1) {
106
if (stat(LOCKPROG, &st) < 0 || !(st.st_mode & S_ISGID))
110
return (eaccess(dir, mailgid, NULL) >= 0);
114
* Call external program to do the actual locking.
116
static int do_extern(char *opt, const char *lockfile, int retries, int flags)
118
sigset_t set, oldset;
124
* Block SIGCHLD. The main program might have installed
125
* handlers we don't want to call.
128
sigaddset(&set, SIGCHLD);
129
sigprocmask(SIG_BLOCK, &set, &oldset);
132
* Fork, execute locking program and wait.
134
if ((pid = fork()) < 0)
137
sprintf(buf, "%d", retries % 1000);
138
execl(LOCKPROG, LOCKPROG, opt, "-r", buf, "-q",
139
(flags & L_PID) ? "-p" : "-N", lockfile, NULL);
144
* Wait for return status - do something appropriate
145
* if program died or returned L_ERROR.
147
while ((n = waitpid(pid, &st, 0)) != pid)
148
if (n < 0 && errno != EINTR)
150
if (!sigismember(&oldset, SIGCHLD))
151
sigprocmask(SIG_UNBLOCK, &set, NULL);
154
if (!WIFEXITED(st) || WEXITSTATUS(st) == L_ERROR) {
159
return WEXITSTATUS(st);
167
int lockfile_create(const char *lockfile, int retries, int flags)
170
char tmplock[MAXPATHLEN];
182
if (strlen(lockfile) + 32 > MAXPATHLEN) {
183
errno = ENAMETOOLONG;
187
#if defined(LIB) && defined(MAILGROUP)
188
if (need_extern(lockfile))
189
return do_extern("-l", lockfile, retries, flags);
193
* Create a temp lockfile (hopefully unique) and write
194
* either our pid/ppid in it, or 0\0 for svr4 compatibility.
196
if (gethostname(sysname, sizeof(sysname)) < 0)
198
if ((p = strchr(sysname, '.')) != NULL)
200
strcpy(tmplock, lockfile);
201
if ((p = strrchr(tmplock, '/')) == NULL)
205
sprintf(p, ".lk%05d%x%s",
206
(int)getpid(), (int)time(NULL) & 15, sysname);
208
fd = open(tmplock, O_WRONLY|O_CREAT|O_EXCL, 0644);
215
if (flags & (L_PID | L_PPID)) {
217
(flags & L_PID) ? (int)getpid() : (int)getppid());
224
i = write(fd, p, len);
226
if (close(fd) != 0) {
232
errno = i < 0 ? e : EAGAIN;
237
* Now try to link the temporary lock to the lock.
239
for (i = 0; i < retries && retries > 0; i++) {
241
sleeptime = i > 12 ? 60 : 5 * i;
246
if ((e = check_sleep(sleeptime)) != 0) {
252
* Now lock by linking the tempfile to the lock.
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).
259
(void)link(tmplock, lockfile);
261
if (lstat(tmplock, &st1) < 0)
262
return L_ERROR; /* Can't happen */
264
if (lstat(lockfile, &st) < 0) {
265
if (statfailed++ > 5) {
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...
273
(void)unlink(tmplock);
281
* See if we got the lock.
283
if (st.st_rdev == st1.st_rdev &&
284
st.st_ino == st1.st_ino) {
285
(void)unlink(tmplock);
291
* If there is a lockfile and it is invalid,
292
* remove the lockfile.
294
if (lockfile_check(lockfile, flags) == -1)
298
(void)unlink(tmplock);
304
* See if a valid lockfile is present.
305
* Returns 0 if so, -1 if not.
307
int lockfile_check(const char *lockfile, int flags)
315
if (stat(lockfile, &st) < 0)
319
* Get the contents and mtime of the lockfile.
320
* Use the time of the file system.
324
if ((fd = open(lockfile, O_RDONLY)) >= 0) {
325
if ((len = read(fd, buf, sizeof(buf))) >= 0 &&
329
if (len > 0 && (flags & (L_PID|L_PPID))) {
337
* If we have a pid, see if the process
338
* owning the lockfile is still alive.
341
if (r == 0 || errno == EPERM)
343
if (r < 0 && errno == ESRCH)
345
/* EINVAL - FALLTHRU */
349
* Without a pid in the lockfile, the lock
350
* is valid if it is newer than 5 mins.
352
if (now < st.st_mtime + 300)
360
int lockfile_remove(const char *lockfile)
362
#if defined(LIB) && defined(MAILGROUP)
363
if (need_extern(lockfile))
364
return do_extern("-u", lockfile, 0, 0);
366
return (unlink(lockfile) < 0 && errno != ENOENT) ? -1 : 0;
372
int lockfile_touch(const char *lockfile)
375
return utime(lockfile, NULL);
377
return utimes(lockfile, NULL);
383
* Lock a mailfile. This looks a lot like the SVR4 function.
384
* Arguments: lusername, retries.
386
int maillock(const char *name, int retries)
391
if (islocked) return 0;
392
if (strlen(name) + sizeof(MAILDIR) + 6 > MAXPATHLEN) {
393
errno = ENAMETOOLONG;
398
* If $MAIL is for the same username as "name"
399
* then use $MAIL instead.
401
sprintf(mlockfile, "%s%s.lock", MAILDIR, name);
402
if ((mail = getenv("MAIL")) != NULL) {
403
if ((p = strrchr(mail, '/')) != NULL)
407
if (strcmp(p, name) == 0) {
408
if (strlen(mail) + 6 > MAXPATHLEN) {
409
errno = ENAMETOOLONG;
412
sprintf(mlockfile, "%s.lock", mail);
415
i = lockfile_create(mlockfile, retries, 0);
416
if (i == 0) islocked = 1;
421
void mailunlock(void)
423
if (!islocked) return;
424
lockfile_remove(mlockfile);
430
lockfile_touch(mlockfile);