Import NetBSD flock(1)

Also fix the MINIX3 libc flock(3) implementation.

Change-Id: Ia80280029968786d7f029940ec02e942057701bd
This commit is contained in:
David van Moolenbroek 2015-09-15 20:42:11 +00:00
parent 875abb8724
commit 9488aa4c04
6 changed files with 442 additions and 7 deletions

View File

@ -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

View File

@ -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 **/

View File

@ -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
View 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
View 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
View 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;
}