X-Git-Url: https://git.distorted.org.uk/~mdw/misc/blobdiff_plain/c9a198787de95321b2b8cf69bacf012834acfdf1..HEAD:/locking.c diff --git a/locking.c b/locking.c index 9435318..6317b2e 100644 --- a/locking.c +++ b/locking.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ /*----- Static variables --------------------------------------------------*/ static jmp_buf jmp; +static int err; /*----- Main code ---------------------------------------------------------*/ @@ -88,14 +90,16 @@ int main(int argc, char *argv[]) const char *prog = 0; char *const *av; void (*oalrm)(int) = 0; - int fd; + int fd = -1; struct flock l; char *p; int t = -1; + int oflag; unsigned int ot = 0; + struct stat st, nst; time_t nt; pid_t kid; - int st; + int rc; #define f_bogus 1u #define f_wait 2u @@ -167,24 +171,41 @@ doneopts: av = &argv[optind]; if (!prog) prog = av[0]; - if ((fd = open(file, - ((f & f_create ? O_CREAT : 0) | - (f & f_excl ? O_RDWR : O_RDONLY)), 0666)) < 0) - die(111, "error opening `%s': %s", file, strerror(errno)); + oflag = f & f_excl ? O_RDWR : O_RDONLY; + if (f & f_create) oflag |= O_CREAT; l.l_type = f & f_excl ? F_WRLCK : F_RDLCK; l.l_whence = SEEK_SET; l.l_start = 0; l.l_len = 0; nt = time(0); if (setjmp(jmp)) { - errno = EAGAIN; + err = EAGAIN; nt = t; - } else { - ot = alarm(0); - oalrm = signal(SIGALRM, alrm); - if (t >= 0) alarm(t); - if (fcntl(fd, f & f_wait ? F_SETLKW : F_SETLK, &l) >= 0) errno = 0; + goto done; + } + ot = alarm(0); + oalrm = signal(SIGALRM, alrm); + if (t >= 0) alarm(t); +again: + if ((fd = open(file, oflag, 0666)) < 0) + die(111, "error opening `%s': %s", file, strerror(errno)); + if (fstat(fd, &st)) + die(111, "error from fstat on `%s': %s", file, strerror(errno)); + err = fcntl(fd, f & f_wait ? F_SETLKW : F_SETLK, &l) >= 0 ? 0 : errno; + /* It's tempting to `optimize' this code by opening a new file descriptor + * here so as to elide the additional call to fstat(2) above. But this + * doesn't work: if we successfully acquire the lock, we then have two file + * descriptors open on the lock file, so we have to close one -- but, under + * the daft fcntl(2) rules, even closing `nfd' will release the lock + * immediately. + */ + if (stat(file, &nst)) { + if (errno == ENOENT) { close(fd); goto again; } + else die(111, "error from stat on `%s': %s", file, strerror(errno)); } + if (st.st_dev != nst.st_dev || st.st_ino != nst.st_ino) + { close(fd); goto again; } +done: signal(SIGALRM, oalrm); if (!ot) alarm(0); @@ -193,28 +214,33 @@ doneopts: if (nt > ot) raise(SIGALRM); else alarm(ot - nt); } - if (errno && - ((errno != EAGAIN && errno != EWOULDBLOCK && errno != EACCES) || - (f & f_fail))) - die(111, "error locking `%s': %s", file, strerror(errno)); - if (errno) exit(0); - + switch (err) { + case 0: break; + case EAGAIN: +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EACCES: + if (!(f & f_fail)) exit(0); + default: + die(111, "error locking `%s': %s", file, strerror(errno)); + } if ((kid = fork()) < 0) die(111, "error from fork: %s", strerror(errno)); if (!kid) { close(fd); execvp(prog, av); die(111, "couldn't exec `%s': %s", prog, strerror(errno)); } - if (waitpid(kid, &st, 0) < 0) + if (waitpid(kid, &rc, 0) < 0) die(EXIT_FAILURE, "error from wait: %s", strerror(errno)); l.l_type = F_UNLCK; l.l_whence = SEEK_SET; l.l_start = 0; l.l_len = 0; fcntl(fd, F_SETLK, &l); - close(fd); - if (WIFEXITED(st)) exit(WEXITSTATUS(st)); - else exit(255); + if (fd >= 0) close(fd); + if (WIFEXITED(rc)) exit(WEXITSTATUS(rc)); + else exit(128 + WTERMSIG(rc)); } /*----- That's all, folks -------------------------------------------------*/