config root man

Current Path : /usr/src/tools/regression/file/flock/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //usr/src/tools/regression/file/flock/flock.c

/*-
 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
 * Authors: Doug Rabson <dfr@rabson.org>
 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
 *
 * 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 AUTHOR 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 AUTHOR 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.
 *
 * $FreeBSD: release/9.1.0/tools/regression/file/flock/flock.c 192949 2009-05-28 02:39:07Z zml $
 */

#include <sys/time.h>
#ifdef __FreeBSD__
#include <sys/mount.h>
#endif
#include <sys/stat.h>
#include <sys/wait.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef __FreeBSD__
#if __FreeBSD_version >= 800028
#define HAVE_SYSID
#endif
#include <sys/cdefs.h>
#else
#ifndef __unused
#define __unused
#endif
#endif

int verbose = 0;

static int
make_file(const char *pathname, off_t sz)
{
	struct stat st;
	const char *template = "/flocktempXXXXXX";
	size_t len;
	char *filename;
	int fd;

	if (stat(pathname, &st) == 0) {
		if (S_ISREG(st.st_mode)) {
			fd = open(pathname, O_RDWR);
			if (fd < 0)
				err(1, "open(%s)", pathname);
			if (ftruncate(fd, sz) < 0)
				err(1, "ftruncate");
			return (fd);
		}
	}

	len = strlen(pathname) + strlen(template) + 1;
	filename = malloc(len);
	strcpy(filename, pathname);
	strcat(filename, template);
	fd = mkstemp(filename);
	if (fd < 0)
		err(1, "mkstemp");
	if (ftruncate(fd, sz) < 0)
		err(1, "ftruncate");
	if (unlink(filename) < 0)
		err(1, "unlink");
	free(filename);

	return (fd);
}

static void
ignore_alarm(int __unused sig)
{
}

static int
safe_waitpid(pid_t pid)
{
	int save_errno;
	int status;

	save_errno = errno;
	errno = 0;
	while (waitpid(pid, &status, 0) != pid) {
		if (errno == EINTR)
			continue;
		err(1, "waitpid");
	}
	errno = save_errno;

	return (status);
}

#define FAIL(test)					\
	do {						\
		if (test) {				\
			printf("FAIL (%s)\n", #test);	\
			return -1;			\
		}					\
	} while (0)

#define SUCCEED \
	do { printf("SUCCEED\n"); return 0; } while (0)

/*
 * Test 1 - F_GETLK on unlocked region
 *
 * If no lock is found that would prevent this lock from being
 * created, the structure is left unchanged by this function call
 * except for the lock type which is set to F_UNLCK.
 */
static int
test1(int fd, __unused int argc, const __unused char **argv)
{
	struct flock fl1, fl2;

	memset(&fl1, 1, sizeof(fl1));
	fl1.l_type = F_WRLCK;
	fl1.l_whence = SEEK_SET;
	fl2 = fl1;

	if (fcntl(fd, F_GETLK, &fl1) < 0)
		err(1, "F_GETLK");

	printf("1 - F_GETLK on unlocked region: ");
	FAIL(fl1.l_start != fl2.l_start);
	FAIL(fl1.l_len != fl2.l_len);
	FAIL(fl1.l_pid != fl2.l_pid);
	FAIL(fl1.l_type != F_UNLCK);
	FAIL(fl1.l_whence != fl2.l_whence);
#ifdef HAVE_SYSID
	FAIL(fl1.l_sysid != fl2.l_sysid);
#endif

	SUCCEED;
}

/*
 * Test 2 - F_SETLK on locked region
 *
 * If a shared or exclusive lock cannot be set, fcntl returns
 * immediately with EACCES or EAGAIN.
 */
static int
test2(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should return -1 with errno set to either EACCES or
	 * EAGAIN.
	 */
	printf("2 - F_SETLK on locked region: ");
	res = fcntl(fd, F_SETLK, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);
	FAIL(res == 0);
	FAIL(errno != EACCES && errno != EAGAIN);

	SUCCEED;
}

/*
 * Test 3 - F_SETLKW on locked region
 *
 * If a shared or exclusive lock is blocked by other locks, the
 * process waits until the request can be satisfied.
 *
 * XXX this test hangs on FreeBSD NFS filesystems due to limitations
 * in FreeBSD's client (and server) lockd implementation.
 */
static int
test3(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("3 - F_SETLKW on locked region: ");

	alarm(1);

	res = fcntl(fd, F_SETLKW, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);
	FAIL(res == 0);
	FAIL(errno != EINTR);

	SUCCEED;
}

/*
 * Test 4 - F_GETLK on locked region
 *
 * Get the first lock that blocks the lock.
 */
static int
test4(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 99;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should return a lock structure reflecting the lock we
	 * made in the child process.
	 */
	if (fcntl(fd, F_GETLK, &fl) < 0)
		err(1, "F_GETLK");

	printf("4 - F_GETLK on locked region: ");
	FAIL(fl.l_start != 0);
	FAIL(fl.l_len != 99);
	FAIL(fl.l_type != F_WRLCK);
	FAIL(fl.l_pid != pid);
#ifdef HAVE_SYSID
	FAIL(fl.l_sysid != 0);
#endif

	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	SUCCEED;
}

/*
 * Test 5 - F_SETLKW simple deadlock
 *
 * If a blocking shared lock request would cause a deadlock (i.e. the
 * lock request is blocked by a process which is itself blocked on a
 * lock currently owned by the process making the new request),
 * EDEADLK is returned.
 */
static int
test5(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. Because our test relies on the child process being
	 * blocked on the parent's lock, we can't easily use a pipe to
	 * synchronize so we just sleep in the parent to given the
	 * child a chance to setup.
	 *
	 * To create the deadlock condition, we arrange for the parent
	 * to lock the first byte of the file and the child to lock
	 * the second byte.  After locking the second byte, the child
	 * will attempt to lock the first byte of the file, and
	 * block. The parent will then attempt to lock the second byte
	 * (owned by the child) which should cause deadlock.
	 */
	int pid;
	struct flock fl;
	int res;

	/*
	 * Lock the first byte in the parent.
	 */
	fl.l_start = 0;
	fl.l_len = 1;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_SETLK 1 (parent)");

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * Lock the second byte in the child and then block on
		 * the parent's lock.
		 */
		fl.l_start = 1;
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		fl.l_start = 0;
		if (fcntl(fd, F_SETLKW, &fl) < 0)
			err(1, "F_SETLKW (child)");
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	sleep(1);

	/*
	 * fcntl should immediately return -1 with errno set to
	 * EDEADLK. If the alarm fires, we failed to detect the
	 * deadlock.
	 */
	alarm(1);
	printf("5 - F_SETLKW simple deadlock: ");

	fl.l_start = 1;
	res = fcntl(fd, F_SETLKW, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	
	FAIL(res == 0);
	FAIL(errno != EDEADLK);

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_UNLCK;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_UNLCK");

	/*
	 * Cancel the alarm to avoid confusing later tests.
	 */
	alarm(0);

	SUCCEED;
}

/*
 * Test 6 - F_SETLKW complex deadlock.
 *
 * This test involves three process, P, C1 and C2. We set things up so
 * that P locks byte zero, C1 locks byte 1 and C2 locks byte 2. We
 * also block C2 by attempting to lock byte zero. Lastly, P attempts
 * to lock a range including byte 1 and 2. This represents a deadlock
 * (due to C2's blocking attempt to lock byte zero).
 */
static int
test6(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * Because our test relies on the child process being blocked
	 * on the parent's lock, we can't easily use a pipe to
	 * synchronize so we just sleep in the parent to given the
	 * children a chance to setup.
	 */
	int pid1, pid2;
	struct flock fl;
	int res;

	/*
	 * Lock the first byte in the parent.
	 */
	fl.l_start = 0;
	fl.l_len = 1;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_SETLK 1 (parent)");

	pid1 = fork();
	if (pid1 < 0)
		err(1, "fork");

	if (pid1 == 0) {
		/*
		 * C1
		 * Lock the second byte in the child and then sleep
		 */
		fl.l_start = 1;
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child1)");
		pause();
		exit(0);
	}

	pid2 = fork();
	if (pid2 < 0)
		err(1, "fork");

	if (pid2 == 0) {
		/*
		 * C2
		 * Lock the third byte in the child and then block on
		 * the parent's lock.
		 */
		fl.l_start = 2;
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child2)");
		fl.l_start = 0;
		if (fcntl(fd, F_SETLKW, &fl) < 0)
			err(1, "F_SETLKW (child2)");
		exit(0);
	}

	/*
	 * Wait until the children have set their locks and then
	 * perform the test.
	 */
	sleep(1);

	/*
	 * fcntl should immediately return -1 with errno set to
	 * EDEADLK. If the alarm fires, we failed to detect the
	 * deadlock.
	 */
	alarm(1);
	printf("6 - F_SETLKW complex deadlock: ");

	fl.l_start = 1;
	fl.l_len = 2;
	res = fcntl(fd, F_SETLKW, &fl);
	kill(pid1, SIGTERM);
	safe_waitpid(pid1);
	kill(pid2, SIGTERM);
	safe_waitpid(pid2);

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_UNLCK;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_UNLCK");

	FAIL(res == 0);
	FAIL(errno != EDEADLK);

	/*
	 * Cancel the alarm to avoid confusing later tests.
	 */
	alarm(0);

	SUCCEED;
}

/*
 * Test 7 - F_SETLK shared lock on exclusive locked region
 *
 * If a shared or exclusive lock cannot be set, fcntl returns
 * immediately with EACCES or EAGAIN.
 */
static int
test7(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("7 - F_SETLK shared lock on exclusive locked region: ");

	fl.l_type = F_RDLCK;
	res = fcntl(fd, F_SETLK, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	FAIL(res == 0);
	FAIL(errno != EACCES && errno != EAGAIN);

	SUCCEED;
}

/*
 * Test 8 - F_SETLK shared lock on share locked region
 *
 * When a shared lock is set on a segment of a file, other processes
 * shall be able to set shared locks on that segment or a portion of
 * it.
 */
static int
test8(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_RDLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("8 - F_SETLK shared lock on share locked region: ");

	fl.l_type = F_RDLCK;
	res = fcntl(fd, F_SETLK, &fl);

	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_UNLCK;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_UNLCK");

	FAIL(res != 0);

	SUCCEED;
}

/*
 * Test 9 - F_SETLK exclusive lock on share locked region
 *
 * If a shared or exclusive lock cannot be set, fcntl returns
 * immediately with EACCES or EAGAIN.
 */
static int
test9(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_RDLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("9 - F_SETLK exclusive lock on share locked region: ");

	fl.l_type = F_WRLCK;
	res = fcntl(fd, F_SETLK, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	FAIL(res == 0);
	FAIL(errno != EACCES && errno != EAGAIN);

	SUCCEED;
}

/*
 * Test 10 - trying to set bogus pid or sysid values
 *
 * The l_pid and l_sysid fields are only used with F_GETLK to return
 * the process ID of the process holding a blocking lock and the
 * system ID of the system that owns that process
 */
static int
test10(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_pid = 9999;
#ifdef HAVE_SYSID
	fl.l_sysid = 9999;
#endif

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	printf("10 - trying to set bogus pid or sysid values: ");

	if (fcntl(fd, F_GETLK, &fl) < 0)
		err(1, "F_GETLK");

	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	FAIL(fl.l_pid != pid);
#ifdef HAVE_SYSID
	FAIL(fl.l_sysid != 0);
#endif

	SUCCEED;
}

/*
 * Test 11 - remote locks
 *
 * XXX temporary interface which will be removed when the kernel lockd
 * is added.
 */
static int
test11(int fd, __unused int argc, const __unused char **argv)
{
#ifdef F_SETLK_REMOTE
	struct flock fl;
	int res;

	if (geteuid() != 0)
		return 0;

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	fl.l_pid = 9999;
	fl.l_sysid = 1001;

	printf("11 - remote locks: ");

	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	fl.l_sysid = 1002;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res == 0);
	FAIL(errno != EACCES && errno != EAGAIN);

	res = fcntl(fd, F_GETLK, &fl);
	FAIL(res != 0);
	FAIL(fl.l_pid != 9999);
	FAIL(fl.l_sysid != 1001);

	fl.l_type = F_UNLCK;
	fl.l_sysid = 1001;
	fl.l_start = 0;
	fl.l_len = 0;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	fl.l_pid = 1234;
	fl.l_sysid = 1001;
	fl.l_start = 0;
	fl.l_len = 1;
	fl.l_whence = SEEK_SET;
	fl.l_type = F_RDLCK;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	fl.l_sysid = 1002;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	fl.l_type = F_UNLCKSYS;
	fl.l_sysid = 1001;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	fl.l_type = F_WRLCK;
	res = fcntl(fd, F_GETLK, &fl);
	FAIL(res != 0);
	FAIL(fl.l_pid != 1234);
	FAIL(fl.l_sysid != 1002);

	fl.l_type = F_UNLCKSYS;
	fl.l_sysid = 1002;
	res = fcntl(fd, F_SETLK_REMOTE, &fl);
	FAIL(res != 0);

	SUCCEED;
#else
	return 0;
#endif
}

/*
 * Test 12 - F_SETLKW on locked region which is then unlocked
 *
 * If a shared or exclusive lock is blocked by other locks, the
 * process waits until the request can be satisfied.
 */
static int
test12(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");

		sleep(1);
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("12 - F_SETLKW on locked region which is then unlocked: ");

	//alarm(1);

	res = fcntl(fd, F_SETLKW, &fl);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);
	FAIL(res != 0);

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_UNLCK;
	if (fcntl(fd, F_SETLK, &fl) < 0)
		err(1, "F_UNLCK");

	SUCCEED;
}

/*
 * Test 13 - F_SETLKW on locked region, race with owner
 *
 * If a shared or exclusive lock is blocked by other locks, the
 * process waits until the request can be satisfied.
 */
static int
test13(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int i;
	int pid;
	int pfd[2];
	struct flock fl;
	char ch;
	int res;
	struct itimerval itv;

	printf("13 - F_SETLKW on locked region, race with owner: ");
	fflush(stdout);

	for (i = 0; i < 100; i++) {
		if (pipe(pfd) < 0)
			err(1, "pipe");

		fl.l_start = 0;
		fl.l_len = 0;
		fl.l_type = F_WRLCK;
		fl.l_whence = SEEK_SET;

		pid = fork();
		if (pid < 0)
			err(1, "fork");

		if (pid == 0) {
			/*
			 * We are the child. We set a write lock and then
			 * write one byte back to the parent to tell it. The
			 * parent will kill us when its done.
			 */
			if (fcntl(fd, F_SETLK, &fl) < 0)
				err(1, "F_SETLK (child)");
			if (write(pfd[1], "a", 1) < 0)
				err(1, "writing to pipe (child)");

			usleep(1);
			exit(0);
		}

		/*
		 * Wait until the child has set its lock and then perform the
		 * test.
		 */
		while (read(pfd[0], &ch, 1) != 1) {
			if (errno == EINTR)
				continue;
			err(1, "reading from pipe (child)");
		}

		/*
		 * fcntl should wait until the alarm and then return -1 with
		 * errno set to EINTR.
		 */
		itv.it_interval.tv_sec = 0;
		itv.it_interval.tv_usec = 0;
		itv.it_value.tv_sec = 0;
		itv.it_value.tv_usec = 2;
		setitimer(ITIMER_REAL, &itv, NULL);

		res = fcntl(fd, F_SETLKW, &fl);
		kill(pid, SIGTERM);
		safe_waitpid(pid);
		close(pfd[0]);
		close(pfd[1]);
		FAIL(!(res == 0 || (res == -1 && errno == EINTR)));

		fl.l_start = 0;
		fl.l_len = 0;
		fl.l_type = F_UNLCK;
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "F_UNLCK");
	}
	SUCCEED;
}

/*
 * Test 14 - soak test
 */
static int
test14(int fd, int argc, const char **argv)
{
#define CHILD_COUNT 20
	/*
	 * We create a set of child processes and let each one run
	 * through a random sequence of locks and unlocks.
	 */
	int i, j, id, id_base;
	int pids[CHILD_COUNT], pid;
	char buf[128];
	char tbuf[128];
	int map[128];
	char outbuf[512];
	struct flock fl;
	struct itimerval itv;
	int status;

	id_base = 0;
	if (argc >= 2)
		id_base = strtol(argv[1], NULL, 0);

	printf("14 - soak test: ");
	fflush(stdout);

	for (i = 0; i < 128; i++)
		map[i] = F_UNLCK;

	for (i = 0; i < CHILD_COUNT; i++) {

		pid = fork();
		if (pid < 0)
			err(1, "fork");
		if (pid) {
			/*
			 * Parent - record the pid and continue.
			 */
			pids[i] = pid;
			continue;
		}

		/*
		 * Child - do some work and exit.
		 */
		id = id_base + i;
		srandom(getpid());

		for (j = 0; j < 50; j++) {
			int start, end, len;
			int set, wrlock;

			do {
				start = random() & 127;
				end = random() & 127;
			} while (end <= start);

			set = random() & 1;
			wrlock = random() & 1;

			len = end - start;
			fl.l_start = start;
			fl.l_len = len;
			fl.l_whence = SEEK_SET;
			if (set)
				fl.l_type = wrlock ? F_WRLCK : F_RDLCK;
			else
				fl.l_type = F_UNLCK;

			itv.it_interval.tv_sec = 0;
			itv.it_interval.tv_usec = 0;
			itv.it_value.tv_sec = 0;
			itv.it_value.tv_usec = 3000;
			setitimer(ITIMER_REAL, &itv, NULL);

			if (fcntl(fd, F_SETLKW, &fl) < 0) {
				if (errno == EDEADLK || errno == EINTR) {
					if (verbose) {
						snprintf(outbuf, sizeof(outbuf),
						    "%d[%d]: %s [%d .. %d] %s\n",
						    id, j,
						    set ? (wrlock ? "write lock"
							: "read lock")
						    : "unlock", start, end,
						    errno == EDEADLK
						    ? "deadlock"
						    : "interrupted");
						write(1, outbuf,
						    strlen(outbuf));
					}
					continue;
				} else {
					perror("fcntl");
				}
			}

			itv.it_interval.tv_sec = 0;
			itv.it_interval.tv_usec = 0;
			itv.it_value.tv_sec = 0;
			itv.it_value.tv_usec = 0;
			setitimer(ITIMER_REAL, &itv, NULL);

			if (verbose) {
				snprintf(outbuf, sizeof(outbuf),
				    "%d[%d]: %s [%d .. %d] succeeded\n",
				    id, j,
				    set ? (wrlock ? "write lock" : "read lock")
				    : "unlock", start, end);
				write(1, outbuf, strlen(outbuf));
			}

			if (set) {
				if (wrlock) {
					/*
					 * We got a write lock - write
					 * our ID to each byte that we
					 * managed to claim.
					 */
					for (i = start; i < end; i++)
						map[i] = F_WRLCK;
					memset(&buf[start], id, len);
					if (pwrite(fd, &buf[start], len,
						start) != len) {
						printf("%d: short write\n", id);
						exit(1);
					}
				} else {
					/*
					 * We got a read lock - read
					 * the bytes which we claimed
					 * so that we can check that
					 * they don't change
					 * unexpectedly.
					 */
					for (i = start; i < end; i++)
						map[i] = F_RDLCK;
					if (pread(fd, &buf[start], len,
						start) != len) {
						printf("%d: short read\n", id);
						exit(1);
					}
				}
			} else {
				for (i = start; i < end; i++)
					map[i] = F_UNLCK;
			}

			usleep(1000);

			/*
			 * Read back the whole region so that we can
			 * check that all the bytes we have some kind
			 * of claim to have the correct value.
			 */
			if (pread(fd, tbuf, sizeof(tbuf), 0) != sizeof(tbuf)) {
				printf("%d: short read\n", id);
				exit(1);
			}

			for (i = 0; i < 128; i++) {
				if (map[i] != F_UNLCK && buf[i] != tbuf[i]) {
					snprintf(outbuf, sizeof(outbuf),
					    "%d: byte %d expected %d, "
					    "got %d\n", id, i, buf[i], tbuf[i]);
					write(1, outbuf, strlen(outbuf));
					exit(1);
				}
			}
		}
		if (verbose)
			printf("%d[%d]: done\n", id, j);

		exit(0);
	}

	status = 0;
	for (i = 0; i < CHILD_COUNT; i++) {
		status += safe_waitpid(pids[i]);
	}
	if (status)
		FAIL(status != 0);

	SUCCEED;
}

/*
 * Test 15 - flock(2) semantcs
 *
 * When a lock holder has a shared lock and attempts to upgrade that
 * shared lock to exclusive, it must drop the shared lock before
 * blocking on the exclusive lock.
 *
 * To test this, we first arrange for two shared locks on the file,
 * and then attempt to upgrade one of them to exclusive. This should
 * drop one of the shared locks and block. We interrupt the blocking
 * lock request and examine the lock state of the file after dropping
 * the other shared lock - there should be no active locks at this
 * point.
 */
static int
test15(int fd, __unused int argc, const __unused char **argv)
{
#ifdef LOCK_EX
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 *
	 * Since we only have one file descriptors and lock ownership
	 * for flock(2) goes with the file descriptor, we use fcntl to
	 * set the child's shared lock.
	 */
	int pid;
	int pfd[2];
	int fd2;
	struct flock fl;
	char ch;
	int res;

	if (pipe(pfd) < 0)
		err(1, "pipe");

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a shared lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		fl.l_start = 0;
		fl.l_len = 0;
		fl.l_type = F_RDLCK;
		fl.l_whence = SEEK_SET;
		if (fcntl(fd, F_SETLK, &fl) < 0)
			err(1, "fcntl(F_SETLK) (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	fd2 = dup(fd);
	if (flock(fd, LOCK_SH) < 0)
		err(1, "flock shared");

	/*
	 * flock should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("15 - flock(2) semantics: ");

	alarm(1);
	flock(fd, LOCK_EX);

	/*
	 * Kill the child to force it to drop its locks.
	 */
	kill(pid, SIGTERM);
	safe_waitpid(pid);

	fl.l_start = 0;
	fl.l_len = 0;
	fl.l_type = F_WRLCK;
	fl.l_whence = SEEK_SET;
	res = fcntl(fd, F_GETLK, &fl);

	close(pfd[0]);
	close(pfd[1]);
	FAIL(res != 0);
	FAIL(fl.l_type != F_UNLCK);

	SUCCEED;
#else
	return 0;
#endif
}

struct test_ctx {
	struct flock tc_fl;
	int tc_fd;
};

static void *
test16_func(void *tc_in)
{
	uintptr_t error;
	struct test_ctx *tc = tc_in;

	error = fcntl(tc->tc_fd, F_SETLKW, &tc->tc_fl);

	pthread_exit((void *)error);
}

#define THREADS 10

/*
 * Test 16 - F_SETLKW from two threads
 *
 * If two threads within a process are blocked on a lock and the lock
 * is granted, make sure things are sane.
 */
static int
test16(int fd, __unused int argc, const __unused char **argv)
{
	/*
	 * We create a child process to hold the lock which we will
	 * test. We use a pipe to communicate with the child.
	 */
	int pid;
	int pfd[2];
	struct test_ctx tc = { .tc_fd = fd };
	char ch;
	int i;
	int error;
	pthread_t thr[THREADS];

	if (pipe(pfd) < 0)
		err(1, "pipe");

	tc.tc_fl.l_start = 0;
	tc.tc_fl.l_len = 0;
	tc.tc_fl.l_type = F_WRLCK;
	tc.tc_fl.l_whence = SEEK_SET;

	pid = fork();
	if (pid < 0)
		err(1, "fork");

	if (pid == 0) {
		/*
		 * We are the child. We set a write lock and then
		 * write one byte back to the parent to tell it. The
		 * parent will kill us when its done.
		 */
		if (fcntl(fd, F_SETLK, &tc.tc_fl) < 0)
			err(1, "F_SETLK (child)");
		if (write(pfd[1], "a", 1) < 0)
			err(1, "writing to pipe (child)");
		pause();
		exit(0);
	}

	/*
	 * Wait until the child has set its lock and then perform the
	 * test.
	 */
	if (read(pfd[0], &ch, 1) != 1)
		err(1, "reading from pipe (child)");

	/*
	 * fcntl should wait until the alarm and then return -1 with
	 * errno set to EINTR.
	 */
	printf("16 - F_SETLKW on locked region by two threads: ");

	for (i = 0; i < THREADS; i++) {
		error = pthread_create(&thr[i], NULL, test16_func, &tc);
		if (error)
			err(1, "pthread_create");
	}

	/*
	 * Sleep, then kill the child. This makes me a little sad, but it's
	 * tricky to tell whether the threads are all really blocked by this
	 * point.
	 */
	sleep(1);
	kill(pid, SIGTERM);
	safe_waitpid(pid);
	close(pfd[0]);
	close(pfd[1]);

	for (i = 0; i < THREADS; i++) {
		void *res;
		error = pthread_join(thr[i], &res);
		if (error)
			err(1, "pthread_join");
		FAIL((uintptr_t)res != 0);
	}

	SUCCEED;
}

struct test {
	int (*testfn)(int, int, const char **);	/* function to perform the test */
	int num;		/* test number */
	int intr;		/* non-zero if the test interrupts a lock */
};

struct test tests[] = {
	{	test1,		1,	0	},
	{	test2,		2,	0	},
	{	test3,		3,	1	},
	{	test4,		4,	0	},
	{	test5,		5,	1	},
	{	test6,		6,	1	},
	{	test7,		7,	0	},
	{	test8,		8,	0	},
	{	test9,		9,	0	},
	{	test10,		10,	0	},
	{	test11,		11,	1	},
	{	test12,		12,	0	},
	{	test13,		13,	1	},
	{	test14,		14,	0	},
	{	test15,		15,	1	},
	{	test16,		16,	1	},
};
int test_count = sizeof(tests) / sizeof(tests[0]);

int
main(int argc, const char *argv[])
{
	int testnum;
	int fd;
	int nointr;
	int i;
	struct sigaction sa;
	int test_argc;
	const char **test_argv;

	if (argc < 2) {
		errx(1, "usage: flock <directory> [test number] ...");
	}

	fd = make_file(argv[1], 1024);
	if (argc >= 3) {
		testnum = strtol(argv[2], NULL, 0);
		test_argc = argc - 2;
		test_argv = argv + 2;
	} else {
		testnum = 0;
		test_argc = 0;
		test_argv = 0;
	}

	sa.sa_handler = ignore_alarm;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sigaction(SIGALRM, &sa, 0);

	nointr = 0;
#if defined(__FreeBSD__) && __FreeBSD_version < 800040
	{
		/*
		 * FreeBSD with userland NLM can't interrupt a blocked
		 * lock request on an NFS mounted filesystem.
		 */
		struct statfs st;
		fstatfs(fd, &st);
		nointr = !strcmp(st.f_fstypename, "nfs");
	}
#endif

	for (i = 0; i < test_count; i++) {
		if (tests[i].intr && nointr)
			continue;
		if (!testnum || tests[i].num == testnum)
			tests[i].testfn(fd, test_argc, test_argv);
	}

	return 0;
}

Man Man