t_lockf.c revision 1.3 1 /* $NetBSD: t_lockf.c,v 1.3 2013/02/19 00:54:47 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <atf-c.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <sys/ptrace.h>
42
43 /*
44 * lockf1 regression test:
45 *
46 * Tests:
47 * Fork N child processes, each of which gets M random byte range locks
48 * on a common file. We ignore all lock errors (practically speaking,
49 * this means EDEADLK or ENOLOCK), but we make numerous passes over all
50 * the children to make sure that they are still awake. (We do this by
51 * verifying that we can ptrace(ATTACH/DETACH) to the children and get
52 * their status via waitpid().)
53 * When finished, reap all the children.
54 */
55
56 int nlocks = 500; /* number of locks per thread */
57 int nprocs = 10; /* number of processes to spawn */
58 int sleeptime = 150000; /* sleep time between locks, usec */
59 off_t size = 8192; /* size of file to lock */
60
61 const char *lockfile = "lockf_test";
62
63 static u_int32_t
64 random_uint32(void)
65 {
66 return lrand48();
67 }
68
69 static void
70 trylocks(int id)
71 {
72 int i, ret, fd;
73
74 srand48(getpid());
75
76 fd = open (lockfile, O_RDWR, 0);
77
78 if (fd < 0)
79 err(1, "%s", lockfile);
80
81 printf("%d: start\n", id);
82
83 for (i=0; i<nlocks; i++) {
84 struct flock fl;
85
86 fl.l_start = random_uint32() % size;
87 fl.l_len = random_uint32() % size;
88 switch (random_uint32() % 3) {
89 case 0:
90 fl.l_type = F_RDLCK;
91 break;
92 case 1:
93 fl.l_type = F_WRLCK;
94 break;
95 case 2:
96 fl.l_type = F_UNLCK;
97 break;
98 }
99 fl.l_whence = SEEK_SET;
100
101 ret = fcntl(fd, F_SETLKW, &fl);
102
103 if (usleep(sleeptime) < 0)
104 err(1, "usleep");
105 }
106 printf("%d: done\n", id);
107 close (fd);
108 }
109
110 ATF_TC(randlock);
111 ATF_TC_HEAD(randlock, tc)
112 {
113
114 atf_tc_set_md_var(tc, "timeout", "300");
115 atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) locking");
116 }
117
118 ATF_TC_BODY(randlock, tc)
119 {
120 int i, j, fd;
121 pid_t *pid;
122 int status;
123
124 (void)unlink(lockfile);
125
126 fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
127 ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
128
129 ATF_REQUIRE_MSG(ftruncate(fd, size) >= 0,
130 "ftruncate(%s): %s", lockfile, strerror(errno));
131
132 fsync(fd);
133 close(fd);
134
135 pid = malloc(nprocs * sizeof(pid_t));
136
137 for (i=0; i<nprocs; i++) {
138 pid[i] = fork();
139 switch (pid[i]) {
140 case 0:
141 trylocks(i);
142 _exit(0);
143 break;
144 case -1:
145 atf_tc_fail("fork %d failed", i);
146 break;
147 default:
148 break;
149 }
150 }
151 usleep(sleeptime/10);
152 for (j=0; j<100; j++) {
153 printf("parent: run %i\n", j+1);
154 for (i=0; i<nprocs; i++) {
155 ATF_REQUIRE_MSG(ptrace(PT_ATTACH, pid[i], 0, 0) >= 0,
156 "ptrace attach %d", pid[i]);
157 ATF_REQUIRE_MSG(waitpid(pid[i], &status, WUNTRACED) >= 0,
158 "waitpid(ptrace)");
159 usleep(sleeptime/3);
160 ATF_REQUIRE_MSG(ptrace(PT_DETACH, pid[i], (caddr_t)1,
161 0) >= 0,
162 "ptrace detach %d", pid[i]);
163 usleep(sleeptime/3);
164 }
165 }
166 for (i=0; i<nprocs; i++) {
167 printf("reap %d: ", i);
168 fflush(stdout);
169 kill(pid[i], SIGINT);
170 waitpid(pid[i], &status, 0);
171 printf(" status %d\n", status);
172 }
173 atf_tc_pass();
174 }
175
176 static int
177 dolock(int fd, int op, off_t lk_off, off_t lk_size)
178 {
179 off_t result;
180 int ret;
181
182 result = lseek(fd, lk_off, SEEK_SET);
183 if (result == -1) {
184 return errno;
185 }
186 ATF_REQUIRE_MSG(result == lk_off, "lseek to wrong offset");
187 ret = lockf(fd, op, lk_size);
188 if (ret == -1) {
189 return errno;
190 }
191 return 0;
192 }
193
194 ATF_TC(deadlock);
195 ATF_TC_HEAD(deadlock, tc)
196 {
197
198 atf_tc_set_md_var(tc, "timeout", "30");
199 atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) deadlock detection");
200 }
201
202 ATF_TC_BODY(deadlock, tc)
203 {
204 int fd;
205 int error;
206 int ret;
207 pid_t pid;
208
209 (void)unlink(lockfile);
210
211 fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666);
212 ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno));
213
214 ATF_REQUIRE_MSG(ftruncate(fd, size) >= 0,
215 "ftruncate(%s): %s", lockfile, strerror(errno));
216
217 fsync(fd);
218
219 error = dolock(fd, F_LOCK, 0, 1);
220 ATF_REQUIRE_MSG(error == 0, "initial dolock: %s", strerror(errno));
221
222 pid = fork();
223 ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
224 if (pid == 0) {
225 error = dolock(fd, F_LOCK, 1, 1);
226 ATF_REQUIRE_MSG(error == 0, "child dolock: %s",
227 strerror(errno));
228 dolock(fd, F_LOCK, 0, 1); /* will block */
229 atf_tc_fail("child did not block");
230 }
231 sleep(1); /* give child time to grab its lock then block */
232
233 error = dolock(fd, F_LOCK, 1, 1);
234 ATF_REQUIRE_MSG(error == EDEADLK, "parent did not detect deadlock: %s",
235 strerror(errno));
236 ret = kill(pid, SIGKILL);
237 ATF_REQUIRE_MSG(ret != -1, "failed to kill child: %s", strerror(errno));
238
239 atf_tc_pass();
240 }
241
242 ATF_TP_ADD_TCS(tp)
243 {
244 ATF_TP_ADD_TC(tp, randlock);
245 ATF_TP_ADD_TC(tp, deadlock);
246
247 return atf_no_error();
248 }
249