/* * Copyright (C) 1997 John D. Polstra. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD: src/usr.bin/lockf/lockf.c,v 1.8 1999/08/28 01:03:07 peter Exp $ */ /* * rewritten for use on Solaris by Rojer (myself@rojer.pp.ru) * Wed Dec 31 12:59:38 2003 */ #include #include #include #include #include #include #include #include #include static int do_open(const char *); static int do_lock(int, const short); static void cleanup(void); static void killed(int sig); static void timeout(int sig); static void usage(void); static const char *lockname; static int lockfd; static int keep; static int locked; static volatile sig_atomic_t timed_out; struct flock the_lock; /* * Execute an arbitrary command while holding a file lock. */ int main(int argc, char **argv) { int ch; int silent; int waitsec; int status; pid_t child; silent = 0; keep = 0; waitsec = -1; /* Infinite. */ lockfd = -1; /* not open yet */ the_lock.l_type = F_WRLCK; the_lock.l_whence = SEEK_SET; the_lock.l_start = 0; the_lock.l_len = 0; while ((ch = getopt(argc, argv, "skt:")) != -1) { switch (ch) { case 'k': keep = 1; break; case 's': silent = 1; break; case 't': { char *endptr; waitsec = strtol(optarg, &endptr, 0); if (*optarg == '\0' || *endptr != '\0' || waitsec < 0) { fprintf(stderr, "invalid timeout \"%s\"\n", optarg); exit(EX_USAGE); } } break; default: usage(); } } if (argc - optind < 2) usage(); lockname = argv[optind++]; argc -= optind; argv += optind; if (waitsec > 0) { /* Set up a timeout. */ struct sigaction act; act.sa_handler = timeout; sigemptyset(&act.sa_mask); act.sa_flags = 0; /* Note that we do not set SA_RESTART. */ sigaction(SIGALRM, &act, NULL); alarm(waitsec); } lockfd = do_open(lockname); locked = do_lock(lockfd, F_SETLK); /* non-blocking first */ while (locked == -1 && !timed_out && waitsec != 0) { if (lockfd == 1) lockfd = do_open(lockname); if (locked == -1) locked = do_lock(lockfd, F_SETLKW); if (lockfd == -1) sleep(1); } if (waitsec > 0) alarm(0); if (locked == -1) { /* We failed to acquire the lock. */ if (!silent) fprintf(stderr, "%s: already locked\n", lockname); exit(EX_TEMPFAIL); } /* At this point, we own the lock. */ if (atexit(cleanup) == -1) { perror("atexit failed"); exit(EX_OSERR); } if ((child = fork()) == -1) { perror("cannot fork"); exit(EX_OSERR); } if (child == 0) { /* The child process. */ close(lockfd); execvp(argv[0], argv); perror(argv[0]); _exit(1); } /* This is the parent process. */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, killed); if (waitpid(child, &status, 0) == -1) { perror("waitpid failed"); exit(EX_OSERR); } return WIFEXITED(status) ? WEXITSTATUS(status) : 1; } static int do_open(const char *name) { int fd = open(name, O_RDWR|O_CREAT|O_NONBLOCK, 0666); if (fd == -1 && errno != EAGAIN && errno != EINTR) { perror("open() failed"); exit(EX_CANTCREAT); } return fd; } static int do_lock(int fd, const short lock_cmd) { int locked; if (fd == -1) return -1; locked = fcntl(fd, lock_cmd, &the_lock); if (locked == -1 && errno != EAGAIN && errno != EINTR) { perror("fcntl(F_SETLK) failed abnormally"); exit(EX_OSERR); } return locked; } /* * Remove the lock file. */ static void cleanup(void) { if (keep) { the_lock.l_type = F_UNLCK; fcntl(lockfd, F_SETLK, &the_lock); } else unlink(lockname); } /* * Signal handler for SIGTERM. Cleans up the lock file, then re-raises * the signal. */ static void killed(int sig) { cleanup(); signal(sig, SIG_DFL); if (kill(getpid(), sig) == -1) { perror("kill failed"); exit(EX_OSERR); } } /* * Signal handler for SIGALRM. */ static void timeout(int sig) { timed_out = 1; } static void usage(void) { fprintf(stderr, "usage: lockf [-ks] [-t seconds] file command [arguments]\n"); exit(EX_USAGE); }