1*84d9c625SLionel Sambuc /* $NetBSD: t_lockf.c,v 1.9 2013/10/19 17:45:00 christos Exp $ */
211be35a1SLionel Sambuc
311be35a1SLionel Sambuc /*-
411be35a1SLionel Sambuc * Copyright (c) 2000 The NetBSD Foundation, Inc.
511be35a1SLionel Sambuc * All rights reserved.
611be35a1SLionel Sambuc *
711be35a1SLionel Sambuc * Redistribution and use in source and binary forms, with or without
811be35a1SLionel Sambuc * modification, are permitted provided that the following conditions
911be35a1SLionel Sambuc * are met:
1011be35a1SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
1111be35a1SLionel Sambuc * notice, this list of conditions and the following disclaimer.
1211be35a1SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
1311be35a1SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
1411be35a1SLionel Sambuc * documentation and/or other materials provided with the distribution.
1511be35a1SLionel Sambuc *
1611be35a1SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1711be35a1SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1811be35a1SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1911be35a1SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2011be35a1SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2111be35a1SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2211be35a1SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2311be35a1SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2411be35a1SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2511be35a1SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2611be35a1SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE.
2711be35a1SLionel Sambuc */
2811be35a1SLionel Sambuc
2911be35a1SLionel Sambuc #include <atf-c.h>
3011be35a1SLionel Sambuc #include <err.h>
3111be35a1SLionel Sambuc #include <errno.h>
3211be35a1SLionel Sambuc #include <fcntl.h>
3311be35a1SLionel Sambuc #include <signal.h>
3411be35a1SLionel Sambuc #include <stdio.h>
3511be35a1SLionel Sambuc #include <stdlib.h>
3611be35a1SLionel Sambuc #include <string.h>
3711be35a1SLionel Sambuc #include <unistd.h>
3811be35a1SLionel Sambuc
3911be35a1SLionel Sambuc #include <sys/types.h>
4011be35a1SLionel Sambuc #include <sys/wait.h>
4111be35a1SLionel Sambuc #include <sys/ptrace.h>
4211be35a1SLionel Sambuc
4311be35a1SLionel Sambuc /*
4411be35a1SLionel Sambuc * lockf1 regression test:
4511be35a1SLionel Sambuc *
4611be35a1SLionel Sambuc * Tests:
4711be35a1SLionel Sambuc * Fork N child processes, each of which gets M random byte range locks
4811be35a1SLionel Sambuc * on a common file. We ignore all lock errors (practically speaking,
4911be35a1SLionel Sambuc * this means EDEADLK or ENOLOCK), but we make numerous passes over all
5011be35a1SLionel Sambuc * the children to make sure that they are still awake. (We do this by
5111be35a1SLionel Sambuc * verifying that we can ptrace(ATTACH/DETACH) to the children and get
5211be35a1SLionel Sambuc * their status via waitpid().)
5311be35a1SLionel Sambuc * When finished, reap all the children.
5411be35a1SLionel Sambuc */
5511be35a1SLionel Sambuc
5611be35a1SLionel Sambuc #define nlocks 500 /* number of locks per thread */
5711be35a1SLionel Sambuc #define nprocs 10 /* number of processes to spawn */
5811be35a1SLionel Sambuc #define npasses 50 /* number of passes to make over the children */
5911be35a1SLionel Sambuc #define sleeptime 150000 /* sleep time between locks, usec */
6011be35a1SLionel Sambuc #define filesize 8192 /* size of file to lock */
6111be35a1SLionel Sambuc
6211be35a1SLionel Sambuc const char *lockfile = "lockf_test";
6311be35a1SLionel Sambuc
6411be35a1SLionel Sambuc static u_int32_t
random_uint32(void)6511be35a1SLionel Sambuc random_uint32(void)
6611be35a1SLionel Sambuc {
6711be35a1SLionel Sambuc return lrand48();
6811be35a1SLionel Sambuc }
6911be35a1SLionel Sambuc
7011be35a1SLionel Sambuc static void
trylocks(int id)7111be35a1SLionel Sambuc trylocks(int id)
7211be35a1SLionel Sambuc {
73*84d9c625SLionel Sambuc int i, fd;
7411be35a1SLionel Sambuc
7511be35a1SLionel Sambuc srand48(getpid());
7611be35a1SLionel Sambuc
7711be35a1SLionel Sambuc fd = open (lockfile, O_RDWR, 0);
7811be35a1SLionel Sambuc
7911be35a1SLionel Sambuc if (fd < 0)
8011be35a1SLionel Sambuc err(1, "%s", lockfile);
8111be35a1SLionel Sambuc
8211be35a1SLionel Sambuc printf("%d: start\n", id);
8311be35a1SLionel Sambuc
8411be35a1SLionel Sambuc for (i = 0; i < nlocks; i++) {
8511be35a1SLionel Sambuc struct flock fl;
8611be35a1SLionel Sambuc
8711be35a1SLionel Sambuc fl.l_start = random_uint32() % filesize;
8811be35a1SLionel Sambuc fl.l_len = random_uint32() % filesize;
8911be35a1SLionel Sambuc switch (random_uint32() % 3) {
9011be35a1SLionel Sambuc case 0:
9111be35a1SLionel Sambuc fl.l_type = F_RDLCK;
9211be35a1SLionel Sambuc break;
9311be35a1SLionel Sambuc case 1:
9411be35a1SLionel Sambuc fl.l_type = F_WRLCK;
9511be35a1SLionel Sambuc break;
9611be35a1SLionel Sambuc case 2:
9711be35a1SLionel Sambuc fl.l_type = F_UNLCK;
9811be35a1SLionel Sambuc break;
9911be35a1SLionel Sambuc }
10011be35a1SLionel Sambuc fl.l_whence = SEEK_SET;
10111be35a1SLionel Sambuc
102*84d9c625SLionel Sambuc (void)fcntl(fd, F_SETLKW, &fl);
10311be35a1SLionel Sambuc
10411be35a1SLionel Sambuc if (usleep(sleeptime) < 0)
10511be35a1SLionel Sambuc err(1, "usleep");
10611be35a1SLionel Sambuc }
10711be35a1SLionel Sambuc printf("%d: done\n", id);
10811be35a1SLionel Sambuc close (fd);
10911be35a1SLionel Sambuc }
11011be35a1SLionel Sambuc
11111be35a1SLionel Sambuc ATF_TC(randlock);
ATF_TC_HEAD(randlock,tc)11211be35a1SLionel Sambuc ATF_TC_HEAD(randlock, tc)
11311be35a1SLionel Sambuc {
11411be35a1SLionel Sambuc
11511be35a1SLionel Sambuc atf_tc_set_md_var(tc, "timeout", "300");
11611be35a1SLionel Sambuc atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) locking");
11711be35a1SLionel Sambuc }
11811be35a1SLionel Sambuc
ATF_TC_BODY(randlock,tc)11911be35a1SLionel Sambuc ATF_TC_BODY(randlock, tc)
12011be35a1SLionel Sambuc {
12111be35a1SLionel Sambuc int i, j, fd;
12211be35a1SLionel Sambuc int pipe_fd[2];
12311be35a1SLionel Sambuc pid_t *pid;
12411be35a1SLionel Sambuc int status;
12511be35a1SLionel Sambuc char pipe_in, pipe_out;
12611be35a1SLionel Sambuc const char pipe_errmsg[] = "child: pipe write failed\n";
12711be35a1SLionel Sambuc
12811be35a1SLionel Sambuc (void)unlink(lockfile);
12911be35a1SLionel Sambuc
13011be35a1SLionel Sambuc fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
13111be35a1SLionel Sambuc ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
13211be35a1SLionel Sambuc
13311be35a1SLionel Sambuc ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0,
13411be35a1SLionel Sambuc "ftruncate(%s): %s", lockfile, strerror(errno));
13511be35a1SLionel Sambuc
13611be35a1SLionel Sambuc ATF_REQUIRE_MSG(pipe(pipe_fd) == 0, "pipe: %s", strerror(errno));
13711be35a1SLionel Sambuc
13811be35a1SLionel Sambuc fsync(fd);
13911be35a1SLionel Sambuc close(fd);
14011be35a1SLionel Sambuc
14111be35a1SLionel Sambuc pid = malloc(nprocs * sizeof(pid_t));
14211be35a1SLionel Sambuc
14311be35a1SLionel Sambuc for (i = 0; i < nprocs; i++) {
14411be35a1SLionel Sambuc pipe_out = (char)('A' + i);
14511be35a1SLionel Sambuc pid[i] = fork();
14611be35a1SLionel Sambuc switch (pid[i]) {
14711be35a1SLionel Sambuc case 0:
14811be35a1SLionel Sambuc if (write(pipe_fd[1], &pipe_out, 1) != 1)
14911be35a1SLionel Sambuc write(STDERR_FILENO, pipe_errmsg,
15011be35a1SLionel Sambuc __arraycount(pipe_errmsg) - 1);
15111be35a1SLionel Sambuc else
15211be35a1SLionel Sambuc trylocks(i);
15311be35a1SLionel Sambuc _exit(0);
15411be35a1SLionel Sambuc break;
15511be35a1SLionel Sambuc case -1:
15611be35a1SLionel Sambuc atf_tc_fail("fork %d failed", i);
15711be35a1SLionel Sambuc break;
15811be35a1SLionel Sambuc default:
15911be35a1SLionel Sambuc ATF_REQUIRE_MSG(read(pipe_fd[0], &pipe_in, 1) == 1,
16011be35a1SLionel Sambuc "parent: read_pipe(%i): %s", i, strerror(errno));
16111be35a1SLionel Sambuc ATF_REQUIRE_MSG(pipe_in == pipe_out,
16211be35a1SLionel Sambuc "parent: pipe does not match");
16311be35a1SLionel Sambuc break;
16411be35a1SLionel Sambuc }
16511be35a1SLionel Sambuc }
16611be35a1SLionel Sambuc for (j = 0; j < npasses; j++) {
16711be35a1SLionel Sambuc printf("parent: run %i\n", j+1);
16811be35a1SLionel Sambuc for (i = 0; i < nprocs; i++) {
16911be35a1SLionel Sambuc ATF_REQUIRE_MSG(ptrace(PT_ATTACH, pid[i], 0, 0) >= 0,
17011be35a1SLionel Sambuc "ptrace attach %d", pid[i]);
17111be35a1SLionel Sambuc ATF_REQUIRE_MSG(waitpid(pid[i], &status, WUNTRACED) >= 0,
17211be35a1SLionel Sambuc "waitpid(ptrace)");
17311be35a1SLionel Sambuc usleep(sleeptime / 3);
17411be35a1SLionel Sambuc ATF_REQUIRE_MSG(ptrace(PT_DETACH, pid[i], (caddr_t)1,
17511be35a1SLionel Sambuc 0) >= 0,
17611be35a1SLionel Sambuc "ptrace detach %d", pid[i]);
17711be35a1SLionel Sambuc usleep(sleeptime / 3);
17811be35a1SLionel Sambuc }
17911be35a1SLionel Sambuc }
18011be35a1SLionel Sambuc for (i = 0; i < nprocs; i++) {
18111be35a1SLionel Sambuc printf("reap %d: ", i);
18211be35a1SLionel Sambuc fflush(stdout);
18311be35a1SLionel Sambuc kill(pid[i], SIGINT);
18411be35a1SLionel Sambuc waitpid(pid[i], &status, 0);
18511be35a1SLionel Sambuc printf(" status %d\n", status);
18611be35a1SLionel Sambuc }
18711be35a1SLionel Sambuc atf_tc_pass();
18811be35a1SLionel Sambuc }
18911be35a1SLionel Sambuc
19011be35a1SLionel Sambuc static int
dolock(int fd,int op,off_t lk_off,off_t lk_size)19111be35a1SLionel Sambuc dolock(int fd, int op, off_t lk_off, off_t lk_size)
19211be35a1SLionel Sambuc {
19311be35a1SLionel Sambuc off_t result;
19411be35a1SLionel Sambuc int ret;
19511be35a1SLionel Sambuc
19611be35a1SLionel Sambuc result = lseek(fd, lk_off, SEEK_SET);
19711be35a1SLionel Sambuc if (result == -1) {
19811be35a1SLionel Sambuc return errno;
19911be35a1SLionel Sambuc }
20011be35a1SLionel Sambuc ATF_REQUIRE_MSG(result == lk_off, "lseek to wrong offset");
20111be35a1SLionel Sambuc ret = lockf(fd, op, lk_size);
20211be35a1SLionel Sambuc if (ret == -1) {
20311be35a1SLionel Sambuc return errno;
20411be35a1SLionel Sambuc }
20511be35a1SLionel Sambuc return 0;
20611be35a1SLionel Sambuc }
20711be35a1SLionel Sambuc
20811be35a1SLionel Sambuc ATF_TC(deadlock);
ATF_TC_HEAD(deadlock,tc)20911be35a1SLionel Sambuc ATF_TC_HEAD(deadlock, tc)
21011be35a1SLionel Sambuc {
21111be35a1SLionel Sambuc
21211be35a1SLionel Sambuc atf_tc_set_md_var(tc, "timeout", "30");
21311be35a1SLionel Sambuc atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) deadlock detection");
21411be35a1SLionel Sambuc }
21511be35a1SLionel Sambuc
ATF_TC_BODY(deadlock,tc)21611be35a1SLionel Sambuc ATF_TC_BODY(deadlock, tc)
21711be35a1SLionel Sambuc {
21811be35a1SLionel Sambuc int fd;
21911be35a1SLionel Sambuc int error;
22011be35a1SLionel Sambuc int ret;
22111be35a1SLionel Sambuc pid_t pid;
22211be35a1SLionel Sambuc
22311be35a1SLionel Sambuc (void)unlink(lockfile);
22411be35a1SLionel Sambuc
22511be35a1SLionel Sambuc fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
22611be35a1SLionel Sambuc ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
22711be35a1SLionel Sambuc
22811be35a1SLionel Sambuc ATF_REQUIRE_MSG(ftruncate(fd, filesize) >= 0,
22911be35a1SLionel Sambuc "ftruncate(%s): %s", lockfile, strerror(errno));
23011be35a1SLionel Sambuc
23111be35a1SLionel Sambuc fsync(fd);
23211be35a1SLionel Sambuc
23311be35a1SLionel Sambuc error = dolock(fd, F_LOCK, 0, 1);
23411be35a1SLionel Sambuc ATF_REQUIRE_MSG(error == 0, "initial dolock: %s", strerror(errno));
23511be35a1SLionel Sambuc
23611be35a1SLionel Sambuc pid = fork();
23711be35a1SLionel Sambuc ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
23811be35a1SLionel Sambuc if (pid == 0) {
23911be35a1SLionel Sambuc error = dolock(fd, F_LOCK, 1, 1);
24011be35a1SLionel Sambuc ATF_REQUIRE_MSG(error == 0, "child dolock: %s",
24111be35a1SLionel Sambuc strerror(errno));
24211be35a1SLionel Sambuc dolock(fd, F_LOCK, 0, 1); /* will block */
24311be35a1SLionel Sambuc atf_tc_fail("child did not block");
24411be35a1SLionel Sambuc }
24511be35a1SLionel Sambuc sleep(1); /* give child time to grab its lock then block */
24611be35a1SLionel Sambuc
24711be35a1SLionel Sambuc error = dolock(fd, F_LOCK, 1, 1);
24811be35a1SLionel Sambuc ATF_REQUIRE_MSG(error == EDEADLK, "parent did not detect deadlock: %s",
24911be35a1SLionel Sambuc strerror(errno));
25011be35a1SLionel Sambuc ret = kill(pid, SIGKILL);
25111be35a1SLionel Sambuc ATF_REQUIRE_MSG(ret != -1, "failed to kill child: %s", strerror(errno));
25211be35a1SLionel Sambuc
25311be35a1SLionel Sambuc atf_tc_pass();
25411be35a1SLionel Sambuc }
25511be35a1SLionel Sambuc
ATF_TP_ADD_TCS(tp)25611be35a1SLionel Sambuc ATF_TP_ADD_TCS(tp)
25711be35a1SLionel Sambuc {
25811be35a1SLionel Sambuc ATF_TP_ADD_TC(tp, randlock);
25911be35a1SLionel Sambuc ATF_TP_ADD_TC(tp, deadlock);
26011be35a1SLionel Sambuc
26111be35a1SLionel Sambuc return atf_no_error();
26211be35a1SLionel Sambuc }
263