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>
21 /* compile with -DUSE_FLOCK to use flock() instead of fcntl() */
23 #if !defined(USE_FLOCK) && !defined(F_SETLKW)
28 # ifndef __attribute__
29 # define __attribute__(x)
33 static char *progname
;
35 __attribute__((format(printf
,3,4)))
36 __attribute__((noreturn
))
37 error(int errnum
, int exitcode
, const char *fmt
, ...) {
39 fprintf(stderr
, "%s: ", progname
);
40 va_start(ap
, fmt
); vfprintf(stderr
, fmt
, ap
); va_end(ap
);
42 fprintf(stderr
, ": %s\n", strerror(errnum
));
48 static const char *lckfile
;
51 static void sigalarm(int sig
) {
55 "lock file `%s' is already locked (timeout waiting)", lckfile
);
58 int main(int argc
, char **argv
) {
71 if ((progname
= strrchr(argv
[0], '/')) == NULL
)
78 "%s: execute a program with a lock set.\n"
79 "Usage: %s [options] lockfile program [arguments]\n"
80 "where options are:\n"
81 " -w - if the lock is already held by another process,\n"
82 " wait for it to complete instead of failing immediately\n"
83 " -W sec - the same as -w but wait not more than sec seconds\n"
84 " -e - execute the program directly, no fork/wait\n"
85 " (keeps extra open file descriptor)\n"
86 " -E nnn - set the fd# to keep open in -e case (implies -e)\n"
87 " -n - do not create the lock file if it does not exist\n"
88 " -q - produce no output if lock is already held\n"
89 " -s - lock in shared (read) mode\n"
90 " -x - lock in exclusive (write) mode (default)\n"
91 " -t - test for lock existence"
93 " (just prints pid if any with -q)\n"
96 , progname
, progname
);
100 while ((c
= getopt(argc
, argv
, "+wW:neE:sxtq")) != EOF
) {
107 if ((waittime
= atoi(optarg
)) < 1)
108 error(0, EX_USAGE
, "invalid wait time `%s'", optarg
);
117 if ((fdn
= atoi(optarg
)) < 0 || fdn
== 2)
118 error(0, EX_USAGE
, "invalid fd# `%s'", optarg
);
137 argc
-= optind
; argv
+= optind
;
138 if (!argc
|| (!test
&& argc
< 2))
139 error(0, EX_USAGE
, "too few arguments given");
147 create
|= shared
? O_RDONLY
: O_WRONLY
;
149 fd
= open(lckfile
, create
, 0666);
151 if (test
&& errno
== ENOENT
) {
153 printf("lockfile `%s' is not locked\n", lckfile
);
156 error(errno
, EX_CANTCREAT
, "unable to open `%s'", lckfile
);
159 if (!test
&& fdn
>= 0) {
160 /* dup it early to comply with stupid POSIX fcntl locking
162 if (dup2(fd
, fdn
) < 0)
163 error(errno
, EX_OSERR
, "dup2(%d,%d) failed", fd
, fdn
);
170 else if (waittime
> 0) {
172 signal(SIGALRM
, sigalarm
);
175 c
= flock(fd
, (shared
? LOCK_SH
: LOCK_EX
) | (waittime
? 0 : LOCK_NB
));
177 (errno
== EWOULDBLOCK
|| errno
== EAGAIN
|| errno
== EACCES
)) {
179 printf("lockfile `%s' is locked\n", lckfile
);
185 memset(&fl
, 0, sizeof(fl
));
186 fl
.l_type
= shared
? F_RDLCK
: F_WRLCK
;
187 c
= fcntl(fd
, test
? F_GETLK
: waittime
? F_SETLKW
: F_SETLK
, &fl
);
188 if (test
&& c
== 0) {
189 if (fl
.l_type
== F_UNLCK
) {
191 printf("lockfile `%s' is not locked\n", lckfile
);
195 printf("lockfile `%s' is locked by process %d\n", lckfile
, fl
.l_pid
);
197 printf("%d\n", fl
.l_pid
);
204 if (errno
!= EWOULDBLOCK
&& errno
!= EAGAIN
&& errno
!= EACCES
)
205 error(errno
, EX_OSERR
, "unable to lock `%s'", lckfile
);
209 error(0, EX_TEMPFAIL
, "lockfile `%s' is already locked", lckfile
);
214 int flags
= fcntl(fd
, F_GETFD
, 0);
216 error(errno
, EX_OSERR
, "fcntl() failed");
217 fcntl(fd
, F_SETFD
, flags
| FD_CLOEXEC
);
220 error(errno
, EX_OSERR
, "unable to fork");
223 error(errno
, EX_OSERR
, "wait() failed");
225 error(0, EX_SOFTWARE
, "%s: %s", *argv
,
226 strsignal(WTERMSIG(c
)));
227 return WEXITSTATUS(c
);
231 error(errno
, EX_OSERR
, "unable to execute %s", *argv
);