1 /* lckdo.c: run a program with a lock held,
2 * to prevent multiple processes running in parallel.
3 * Use just like `nice' or `nohup'.
4 * Written by Michael Tokarev <mjt@tls.msk.ru>
22 /* compile with -DUSE_FLOCK to use flock() instead of fcntl() */
24 #if !defined(USE_FLOCK) && !defined(F_SETLKW)
29 # ifndef __attribute__
30 # define __attribute__(x)
34 static char *progname;
36 __attribute__((format(printf,3,4)))
37 __attribute__((noreturn))
38 error(int errnum, int exitcode, const char *fmt, ...) {
40 fprintf(stderr, "%s: ", progname);
41 va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
43 fprintf(stderr, ": %s\n", strerror(errnum));
49 static const char *lckfile;
52 static void sigalarm(int sig) {
56 "lock file `%s' is already locked (timeout waiting)", lckfile);
59 int main(int argc, char **argv) {
72 if ((progname = strrchr(argv[0], '/')) == NULL)
79 "%s: execute a program with a lock set.\n"
80 "Usage: %s [options] lockfile program [arguments]\n"
81 "where options are:\n"
82 " -w - if the lock is already held by another process,\n"
83 " wait for it to complete instead of failing immediately\n"
84 " -W sec - the same as -w but wait not more than sec seconds\n"
85 " -e - execute the program directly, no fork/wait\n"
86 " (keeps extra open file descriptor)\n"
87 " -E nnn - set the fd# to keep open in -e case (implies -e)\n"
88 " -n - do not create the lock file if it does not exist\n"
89 " -q - produce no output if lock is already held\n"
90 " -s - lock in shared (read) mode\n"
91 " -x - lock in exclusive (write) mode (default)\n"
92 " -t - test for lock existence"
94 " (just prints pid if any with -q)\n"
97 , progname, progname);
101 while ((c = getopt(argc, argv, "+wW:neE:sxtq")) != EOF) {
108 if ((waittime = atoi(optarg)) < 1)
109 error(0, EX_USAGE, "invalid wait time `%s'", optarg);
118 if ((fdn = atoi(optarg)) < 0 || fdn == 2)
119 error(0, EX_USAGE, "invalid fd# `%s'", optarg);
138 argc -= optind; argv += optind;
139 if (!argc || (!test && argc < 2))
140 error(0, EX_USAGE, "too few arguments given");
148 create |= shared ? O_RDONLY : O_WRONLY;
150 fd = open(lckfile, create, 0666);
152 if (test && errno == ENOENT) {
154 printf("lockfile `%s' is not locked\n", lckfile);
157 error(errno, EX_CANTCREAT, "unable to open `%s'", lckfile);
160 if (!test && fdn >= 0) {
161 /* dup it early to comply with stupid POSIX fcntl locking
163 if (dup2(fd, fdn) < 0)
164 error(errno, EX_OSERR, "dup2(%d,%d) failed", fd, fdn);
171 else if (waittime > 0) {
173 signal(SIGALRM, sigalarm);
176 c = flock(fd, (shared ? LOCK_SH : LOCK_EX) | (waittime ? 0 : LOCK_NB));
178 (errno == EWOULDBLOCK || errno == EAGAIN || errno == EACCES)) {
180 printf("lockfile `%s' is locked\n", lckfile);
186 memset(&fl, 0, sizeof(fl));
187 fl.l_type = shared ? F_RDLCK : F_WRLCK;
188 c = fcntl(fd, test ? F_GETLK : waittime ? F_SETLKW : F_SETLK, &fl);
189 if (test && c == 0) {
190 if (fl.l_type == F_UNLCK) {
192 printf("lockfile `%s' is not locked\n", lckfile);
196 printf("lockfile `%s' is locked by process %d\n", lckfile, fl.l_pid);
198 printf("%d\n", fl.l_pid);
205 if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EACCES)
206 error(errno, EX_OSERR, "unable to lock `%s'", lckfile);
210 error(0, EX_TEMPFAIL, "lockfile `%s' is already locked", lckfile);
215 int flags = fcntl(fd, F_GETFD, 0);
217 error(errno, EX_OSERR, "fcntl() failed");
218 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
221 error(errno, EX_OSERR, "unable to fork");
224 error(errno, EX_OSERR, "wait() failed");
226 error(0, EX_SOFTWARE, "%s: %s", *argv,
227 strsignal(WTERMSIG(c)));
228 return WEXITSTATUS(c);
232 error(errno, EX_OSERR, "unable to execute %s", *argv);