Import NetBSD flock(1)
Also fix the MINIX3 libc flock(3) implementation. Change-Id: Ia80280029968786d7f029940ec02e942057701bd
This commit is contained in:
parent
875abb8724
commit
9488aa4c04
6 changed files with 442 additions and 7 deletions
|
@ -331,6 +331,7 @@
|
|||
./usr/bin/fix minix-sys
|
||||
./usr/bin/flex++ minix-sys
|
||||
./usr/bin/flex minix-sys
|
||||
./usr/bin/flock minix-sys
|
||||
./usr/bin/fold minix-sys
|
||||
./usr/bin/format minix-sys
|
||||
./usr/bin/fpr minix-sys
|
||||
|
@ -2405,6 +2406,7 @@
|
|||
./usr/man/man1/finger.1 minix-sys
|
||||
./usr/man/man1/flex.1 minix-sys
|
||||
./usr/man/man1/flexdoc.1 minix-sys
|
||||
./usr/man/man1/flock.1 minix-sys
|
||||
./usr/man/man1/fold.1 minix-sys
|
||||
./usr/man/man1/for.1 minix-sys obsolete
|
||||
./usr/man/man1/format.1 minix-sys
|
||||
|
|
|
@ -21,14 +21,15 @@
|
|||
int flock(int fd, int mode)
|
||||
{
|
||||
struct flock lck;
|
||||
register int retcode;
|
||||
|
||||
memset((void *) &lck, 0, sizeof(struct flock));
|
||||
lck.l_type = mode & ~LOCK_NB;
|
||||
lck.l_pid = getpid();
|
||||
if ((retcode = fcntl(fd, mode & LOCK_NB ? F_SETLK : F_SETLKW, &lck)) < 0 && errno == EAGAIN)
|
||||
errno = EWOULDBLOCK;
|
||||
return retcode;
|
||||
switch (mode & ~LOCK_NB) {
|
||||
case LOCK_SH: lck.l_type = F_RDLCK; break;
|
||||
case LOCK_EX: lck.l_type = F_WRLCK; break;
|
||||
case LOCK_UN: lck.l_type = F_UNLCK; break;
|
||||
default: errno = EINVAL; return -1;
|
||||
}
|
||||
return fcntl(fd, mode & LOCK_NB ? F_SETLK : F_SETLKW, &lck);
|
||||
}
|
||||
|
||||
/** flock.c **/
|
||||
|
|
|
@ -10,7 +10,7 @@ SUBDIR= asa \
|
|||
column comm csplit ctags cut \
|
||||
deroff dirname du \
|
||||
env expand \
|
||||
false find finger fold fpr from \
|
||||
false find finger flock fold fpr from \
|
||||
fsplit ftp genassym getopt \
|
||||
head hexdump id indent infocmp join jot \
|
||||
lam last ldd leave \
|
||||
|
|
8
usr.bin/flock/Makefile
Normal file
8
usr.bin/flock/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
# $NetBSD: Makefile,v 1.1 2012/11/01 23:30:19 christos Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/6/93
|
||||
|
||||
PROG= flock
|
||||
#LDADD+= -lutil
|
||||
#DPADD+= ${LIBUTIL}
|
||||
|
||||
.include <bsd.prog.mk>
|
100
usr.bin/flock/flock.1
Normal file
100
usr.bin/flock/flock.1
Normal file
|
@ -0,0 +1,100 @@
|
|||
.\" $NetBSD: flock.1,v 1.9 2013/09/21 15:01:14 khorben Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||
.\" by Christos Zoulas.
|
||||
.\"
|
||||
.\" 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 THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION 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.
|
||||
.\"
|
||||
.\"
|
||||
.Dd November 2, 2012
|
||||
.Dt FLOCK 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm flock
|
||||
.Nd Provide locking API for shell scripts
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl dnosvx
|
||||
.Op Fl w Ar timeout
|
||||
.Ar lockfile|lockdir
|
||||
.Op Fl c Ar command
|
||||
|
|
||||
.Op Ar command ...
|
||||
.Nm
|
||||
.Op Fl dnsuvx
|
||||
.Op Fl w Ar timeout
|
||||
.Ar lockfd
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility provides
|
||||
.Xr flock 2
|
||||
access to the command line or scripts.
|
||||
The first form locks a file or directory while the command provided is executed.
|
||||
If the file or directory does not exist, then a file is created.
|
||||
.Pp
|
||||
The second form can use an arbitrary file descriptor that is provided from a
|
||||
shell script for example:
|
||||
.Bd -literal
|
||||
(
|
||||
flock -s 100
|
||||
# commands to be executed under the lock
|
||||
) 100> /path/to/lockfile
|
||||
.Ed
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width "XXXXXXXXXXXXXXXXX"
|
||||
.It Fl c Ar command
|
||||
Pass a command to a the shell.
|
||||
.It Fl d , Fl Fl debug
|
||||
Provide debugging output.
|
||||
.It Fl n , Fl Fl nb , Fl Fl nonblock
|
||||
Don't block and fail immediately if the lock could not be obtained.
|
||||
.It Fl o , Fl Fl close
|
||||
Close the file before executing the command.
|
||||
This is useful if the child forks and should not be holding the lock.
|
||||
.It Fl s , Fl Fl shared
|
||||
Obtain a shared lock.
|
||||
.It Fl u , Fl Fl unlock
|
||||
Unlock an existing lock.
|
||||
This is available only for a file descriptor.
|
||||
.It Fl v , Fl Fl verbose
|
||||
On error print an explanation of the failure.
|
||||
.It Fl w , Fl Fl wait , Fl Fl timeout Ar seconds
|
||||
Fail if the lock could not be obtained after
|
||||
.Ar seconds .
|
||||
.It Fl x , Fl Fl exclusive
|
||||
Obtain an exclusive lock.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr shlock 1 ,
|
||||
.Xr flock 2
|
||||
.Sh HISTORY
|
||||
An
|
||||
.Nm
|
||||
utility appeared in
|
||||
.Nx 6.1 .
|
324
usr.bin/flock/flock.c
Normal file
324
usr.bin/flock/flock.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
/* $NetBSD: flock.c,v 1.8 2013/10/29 16:02:15 christos Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2012 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Christos Zoulas.
|
||||
*
|
||||
* 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.
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: flock.c,v 1.8 2013/10/29 16:02:15 christos Exp $");
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <paths.h>
|
||||
#include <time.h>
|
||||
|
||||
static struct option flock_longopts[] = {
|
||||
{ "debug", no_argument, 0, 'd' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "nonblock", no_argument, 0, 'n' },
|
||||
{ "nb", no_argument, 0, 'n' },
|
||||
{ "close", no_argument, 0, 'o' },
|
||||
{ "shared", no_argument, 0, 's' },
|
||||
{ "exclusive", no_argument, 0, 'x' },
|
||||
{ "unlock", no_argument, 0, 'u' },
|
||||
{ "verbose", no_argument, 0, 'v' },
|
||||
{ "command", required_argument, 0, 'c' },
|
||||
{ "wait", required_argument, 0, 'w' },
|
||||
{ "timeout", required_argument, 0, 'w' },
|
||||
{ NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static sig_atomic_t timeout_expired;
|
||||
|
||||
static __dead void
|
||||
usage(const char *fmt, ...)
|
||||
{
|
||||
if (fmt) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "%s: ", getprogname());
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputc('\n', stderr);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Usage: %s [-dnosvx] [-w timeout] lockfile|lockdir "
|
||||
"[-c command]|command ...\n\t%s [-dnsuvx] [-w timeout] lockfd\n",
|
||||
getprogname(), getprogname());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void
|
||||
sigalrm(int sig)
|
||||
{
|
||||
timeout_expired++;
|
||||
}
|
||||
|
||||
static const char *
|
||||
lock2name(int l)
|
||||
{
|
||||
static char buf[1024];
|
||||
int nb = l & LOCK_NB;
|
||||
|
||||
l &= ~LOCK_NB;
|
||||
if (nb)
|
||||
strlcpy(buf, "LOCK_NB|", sizeof(buf));
|
||||
else
|
||||
buf[0] = '\0';
|
||||
|
||||
switch (l) {
|
||||
case LOCK_SH:
|
||||
strlcat(buf, "LOCK_SH", sizeof(buf));
|
||||
return buf;
|
||||
case LOCK_EX:
|
||||
strlcat(buf, "LOCK_EX", sizeof(buf));
|
||||
return buf;
|
||||
case LOCK_UN:
|
||||
strlcat(buf, "LOCK_UN", sizeof(buf));
|
||||
return buf;
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "*%d*", l | nb);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
static char
|
||||
lockchar(int l)
|
||||
{
|
||||
switch (l & ~LOCK_NB) {
|
||||
case LOCK_SH:
|
||||
return 's';
|
||||
case LOCK_EX:
|
||||
return 'x';
|
||||
case LOCK_UN:
|
||||
return 'u';
|
||||
default:
|
||||
return '*';
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
cmdline(char **av)
|
||||
{
|
||||
char *v = NULL;
|
||||
while (*av)
|
||||
if (v) {
|
||||
if (asprintf(&v, "%s %s", v, *av++) < 0)
|
||||
err(EXIT_FAILURE, "malloc");
|
||||
} else {
|
||||
if ((v = strdup(*av++)) == NULL)
|
||||
err(EXIT_FAILURE, "strdup");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int lock = 0;
|
||||
double timeout = 0;
|
||||
int cls = 0;
|
||||
int fd = -1;
|
||||
int debug = 0;
|
||||
int verbose = 0;
|
||||
char *mcargv[] = {
|
||||
__UNCONST(_PATH_BSHELL), __UNCONST("-c"), NULL, NULL
|
||||
};
|
||||
char **cmdargv = NULL, *v;
|
||||
#ifndef __minix
|
||||
timer_t tm;
|
||||
#else /* __minix */
|
||||
struct itimerval it;
|
||||
#endif /* __minix */
|
||||
|
||||
setprogname(argv[0]);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "+dnosuvw:x", flock_longopts, NULL))
|
||||
!= -1)
|
||||
switch (c) {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'x':
|
||||
#define T(l) (lock & ~LOCK_NB) != (l) && (lock & ~LOCK_NB) != 0
|
||||
if (T(LOCK_EX))
|
||||
goto badlock;
|
||||
lock |= LOCK_EX;
|
||||
break;
|
||||
case 'n':
|
||||
lock |= LOCK_NB;
|
||||
break;
|
||||
case 's':
|
||||
if (T(LOCK_SH))
|
||||
goto badlock;
|
||||
lock |= LOCK_SH;
|
||||
break;
|
||||
case 'u':
|
||||
if (T(LOCK_UN))
|
||||
goto badlock;
|
||||
lock |= LOCK_UN;
|
||||
break;
|
||||
case 'w':
|
||||
timeout = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'o':
|
||||
cls = 1;
|
||||
break;
|
||||
default:
|
||||
usage("Invalid option '%c'", c);
|
||||
badlock:
|
||||
usage("-%c can't be used with -%c", c, lockchar(lock));
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if ((lock & ~LOCK_NB) == 0)
|
||||
usage("Missing lock type flag");
|
||||
|
||||
switch (argc) {
|
||||
case 0:
|
||||
usage("Missing lock file argument");
|
||||
case 1:
|
||||
if (cls)
|
||||
usage("Close is valid only for descriptors");
|
||||
fd = strtol(argv[0], NULL, 0); // XXX: error checking
|
||||
if (debug) {
|
||||
fprintf(stderr, "descriptor %s lock %s\n",
|
||||
argv[0], lock2name(lock));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((lock & LOCK_NB) == LOCK_UN)
|
||||
usage("Unlock is only valid for descriptors");
|
||||
if (strcmp(argv[1], "-c") == 0 ||
|
||||
strcmp(argv[1], "--command") == 0) {
|
||||
if (argc == 2)
|
||||
usage("Missing argument to %s", strcmp(argv[1],
|
||||
"-c") == 0 ? "-c" : "--command");
|
||||
mcargv[2] = argv[2];
|
||||
cmdargv = mcargv;
|
||||
} else
|
||||
cmdargv = argv + 1;
|
||||
|
||||
if ((fd = open(argv[0], O_RDONLY)) == -1) {
|
||||
if (errno != ENOENT ||
|
||||
(fd = open(argv[0], O_RDWR|O_CREAT, 0600)) == -1)
|
||||
err(EXIT_FAILURE, "Cannot open `%s'", argv[0]);
|
||||
}
|
||||
if (debug) {
|
||||
fprintf(stderr, "file %s lock %s command %s ...\n",
|
||||
argv[0], lock2name(lock), v = cmdline(cmdargv));
|
||||
free(v);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
#ifndef __minix
|
||||
struct sigevent ev;
|
||||
struct itimerspec it;
|
||||
#endif /* !__minix */
|
||||
struct sigaction sa;
|
||||
|
||||
#ifndef __minix
|
||||
timespecclear(&it.it_interval);
|
||||
it.it_value.tv_sec = timeout;
|
||||
it.it_value.tv_nsec = (timeout - it.it_value.tv_sec) *
|
||||
1000000000;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.sigev_notify = SIGEV_SIGNAL;
|
||||
ev.sigev_signo = SIGALRM;
|
||||
|
||||
if (timer_create(CLOCK_REALTIME, &ev, &tm) == -1)
|
||||
err(EXIT_FAILURE, "timer_create");
|
||||
|
||||
if (timer_settime(tm, TIMER_RELTIME, &it, NULL) == -1)
|
||||
err(EXIT_FAILURE, "timer_settime");
|
||||
#else /* __minix */
|
||||
memset(&it.it_interval, 0, sizeof(it.it_interval));
|
||||
it.it_value.tv_sec = timeout;
|
||||
it.it_value.tv_usec = (timeout - it.it_value.tv_sec) * 1000000;
|
||||
|
||||
if (setitimer(ITIMER_REAL, &it, NULL) == -1)
|
||||
err(EXIT_FAILURE, "setitimer");
|
||||
|
||||
memset(&it, 0, sizeof(it)); /* for the reset later */
|
||||
#endif /* __minix */
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = sigalrm;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
if (sigaction(SIGALRM, &sa, NULL) == -1)
|
||||
err(EXIT_FAILURE, "sigaction");
|
||||
|
||||
if (debug)
|
||||
fprintf(stderr, "alarm %g\n", timeout);
|
||||
}
|
||||
|
||||
while (flock(fd, lock) == -1) {
|
||||
if (errno == EINTR && timeout_expired == 0)
|
||||
continue;
|
||||
if (verbose)
|
||||
err(EXIT_FAILURE, "flock(%d, %s)", fd, lock2name(lock));
|
||||
else
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (timeout)
|
||||
#ifndef __minix
|
||||
timer_delete(tm);
|
||||
#else /* __minix */
|
||||
setitimer(ITIMER_REAL, &it, NULL);
|
||||
#endif /* __minix */
|
||||
|
||||
if (cls)
|
||||
(void)close(fd);
|
||||
|
||||
if (cmdargv != NULL) {
|
||||
execvp(cmdargv[0], cmdargv);
|
||||
err(EXIT_FAILURE, "execvp '%s'", v = cmdline(cmdargv));
|
||||
free(v);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue