From 70564cb95575300bd77dd8035f2d010e9030de4c Mon Sep 17 00:00:00 2001 From: joeyh Date: Thu, 6 Sep 2007 01:28:28 +0000 Subject: * Don't strip binaries for debian package if built with DEB_BUILD_OPTIONS=nostrip. Closes: #437577 * Include Michael Tokarev's lckdo program and replace / conflict with the current lckdo package. (Robert Edmonds) Closes: #432906 * lckdo: Don't clear other flags when setting close on exec. --- lckdo.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 lckdo.c (limited to 'lckdo.c') diff --git a/lckdo.c b/lckdo.c new file mode 100644 index 0000000..39b7f17 --- /dev/null +++ b/lckdo.c @@ -0,0 +1,227 @@ +/* lckdo.c: run a program with a lock held, + * to prevent multiple processes running in parallel. + * Use just like `nice' or `nohup'. + * Written by Michael Tokarev + * Public domain. + */ + +#define _GNU_SOURCE +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* compile with -DUSE_FLOCK to use flock() instead of fcntl() */ + +#if !defined(USE_FLOCK) && !defined(F_SETLKW) +# define USE_FLOCK +#endif + +#ifndef __GNUC__ +# ifndef __attribute__ +# define __attribute__(x) +# endif +#endif + +static char *progname; +static void +__attribute__((format(printf,3,4))) +__attribute__((noreturn)) +error(int errnum, int exitcode, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + if (errnum) fprintf(stderr, ": %s\n", strerror(errnum)); + else fputs("\n", stderr); + exit(exitcode); +} + +static const char *lckfile; +static int quiet; + +static void sigalarm(int sig) { + if (quiet) + _exit(EX_TEMPFAIL); + error(0, EX_TEMPFAIL, + "lock file `%s' is already locked (timeout waiting)", lckfile); + sig = sig; +} + +int main(int argc, char **argv) { + int fd; + int c; + int create = O_CREAT; + int dofork = 1; + int waittime = 0; + int shared = 0; + int test = 0; + int fdn = -1; +#ifndef USE_FLOCK + struct flock fl; +#endif + + if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; + else argv[0] = ++progname; + + if (argc == 1) { + printf( +"%s: execute a program with a lock set.\n" +"Usage: %s [options] lockfile program [arguments]\n" +"where options are:\n" +" -w - if the lock is already held by another process,\n" +" wait for it to complete instead of failing immediately\n" +" -W sec - the same as -w but wait not more than sec seconds\n" +" -e - execute the program directly, no fork/wait\n" +" (keeps extra open file descriptor)\n" +" -E nnn - set the fd# to keep open in -e case (implies -e)\n" +" -n - do not create the lock file if it does not exist\n" +" -q - produce no output if lock is already held\n" +" -s - lock in shared (read) mode\n" +" -x - lock in exclusive (write) mode (default)\n" +" -t - test for lock existence" +#ifndef USE_FLOCK + " (just prints pid if any with -q)\n" +#endif +" (implies -n)\n" + , progname, progname); + return 0; + } + + while((c = getopt(argc, argv, "+wW:neE:sxtq")) != EOF) + switch(c) { + case 'w': + if (!waittime) + waittime = -1; + break; + case 'W': + if ((waittime = atoi(optarg)) < 1) + error(0, EX_USAGE, "invalid wait time `%s'", optarg); + break; + case 't': + test = 1; + /* fall */ + case 'n': + create = 0; + break; + case 'E': + if ((fdn = atoi(optarg)) < 0 || fdn == 2) + error(0, EX_USAGE, "invalid fd# `%s'", optarg); + /* fall */ + case 'e': + dofork = 0; + break; + case 's': + shared = 1; + break; + case 'x': + shared = 0; + break; + case 'q': + quiet = 1; + break; + default: + return EX_USAGE; + } + + argc -= optind; argv += optind; + if (!argc || (!test && argc < 2)) + error(0, EX_USAGE, "too few arguments given"); + + lckfile = *argv++; + +#ifdef USE_FLOCK + create |= O_RDONLY; +#else + if (!test) + create |= shared ? O_RDONLY : O_WRONLY; +#endif + fd = open(lckfile, create, 0666); + if (fd < 0) { + if (test && errno == ENOENT) { + if (!quiet) + printf("lockfile `%s' is not locked\n", lckfile); + return 0; + } + error(errno, EX_CANTCREAT, "unable to open `%s'", lckfile); + } + + if (!test && fdn >= 0) { + /* dup it early to comply with stupid POSIX fcntl locking semantics */ + if (dup2(fd, fdn) < 0) + error(errno, EX_OSERR, "dup2(%d,%d) failed", fd, fdn); + close(fd); + fd = fdn; + } + + if (test) + waittime = 0; + else if (waittime > 0) { + alarm(waittime); + signal(SIGALRM, sigalarm); + } +#ifdef USE_FLOCK + c = flock(fd, (shared ? LOCK_SH : LOCK_EX) | (waittime ? 0 : LOCK_NB)); + if (test && c < 0 && + (errno == EWOULDBLOCK || errno == EAGAIN || errno == EACCES)) { + if (!quiet) + printf("lockfile `%s' is locked\n", lckfile); + else + printf("locked\n"); + return EX_TEMPFAIL; + } +#else + memset(&fl, 0, sizeof(fl)); + fl.l_type = shared ? F_RDLCK : F_WRLCK; + c = fcntl(fd, test ? F_GETLK : waittime ? F_SETLKW : F_SETLK, &fl); + if (test && c == 0) { + if (fl.l_type == F_UNLCK) { + if (!quiet) + printf("lockfile `%s' is not locked\n", lckfile); + return 0; + } + if (!quiet) + printf("lockfile `%s' is locked by process %d\n", lckfile, fl.l_pid); + else + printf("%d\n", fl.l_pid); + return EX_TEMPFAIL; + } +#endif + if (waittime > 0) + alarm(0); + if (c < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EACCES) + error(errno, EX_OSERR, "unable to lock `%s'", lckfile); + else if (quiet) + return EX_TEMPFAIL; + else + error(0, EX_TEMPFAIL, "lockfile `%s' is already locked", lckfile); + } + + if (dofork) { + pid_t pid; + int flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) + error(errno, EX_OSERR, "fcntl() failed"); + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + pid = fork(); + if (pid < 0) + error(errno, EX_OSERR, "unable to fork"); + else if (pid) { + if (wait(&c) < 0) + error(errno, EX_OSERR, "wait() failed"); + if (WIFSIGNALED(c)) + error(0, EX_SOFTWARE, "%s: %s", *argv, strsignal(WTERMSIG(c))); + return WEXITSTATUS(c); + } + } + execvp(*argv, argv); + error(errno, EX_OSERR, "unable to execute %s", *argv); +} -- cgit v1.2.3