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
);