1 /* $NetBSD: t_lockf.c,v 1.1 2012/11/07 14:00:38 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 for (j=0; j<100; j++) { 152 printf("parent: run %i\n", j+1); 153 for (i=0; i<nprocs; i++) { 154 ATF_REQUIRE_MSG(ptrace(PT_ATTACH, pid[i], 0, 0) >= 0, 155 "ptrace attach %d", pid[i]); 156 ATF_REQUIRE_MSG(waitpid(pid[i], &status, WUNTRACED) >= 0, 157 "waitpid(ptrace)"); 158 usleep(sleeptime/3); 159 ATF_REQUIRE_MSG(ptrace(PT_DETACH, pid[i], (caddr_t)1, 160 0) >= 0, 161 "ptrace detach %d", pid[i]); 162 usleep(sleeptime/3); 163 } 164 } 165 for (i=0; i<nprocs; i++) { 166 printf("reap %d: ", i); 167 fflush(stdout); 168 kill(pid[i], SIGINT); 169 waitpid(pid[i], &status, 0); 170 printf(" status %d\n", status); 171 } 172 atf_tc_pass(); 173 } 174 175 static int 176 dolock(int fd, int op, off_t lk_off, off_t lk_size) 177 { 178 off_t result; 179 int ret; 180 181 result = lseek(fd, lk_off, SEEK_SET); 182 if (result == -1) { 183 return errno; 184 } 185 ATF_REQUIRE_MSG(result == lk_off, "lseek to wrong offset"); 186 ret = lockf(fd, op, lk_size); 187 if (ret == -1) { 188 return errno; 189 } 190 return 0; 191 } 192 193 ATF_TC(deadlock); 194 ATF_TC_HEAD(deadlock, tc) 195 { 196 197 atf_tc_set_md_var(tc, "timeout", "30"); 198 atf_tc_set_md_var(tc, "descr", "Checks fcntl(2) deadlock detection"); 199 } 200 201 ATF_TC_BODY(deadlock, tc) 202 { 203 int fd; 204 int error; 205 int ret; 206 pid_t pid; 207 208 (void)unlink(lockfile); 209 210 fd = open (lockfile, O_RDWR|O_CREAT|O_EXCL|O_TRUNC, 0666); 211 ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", lockfile, strerror(errno)); 212 213 ATF_REQUIRE_MSG(ftruncate(fd, size) >= 0, 214 "ftruncate(%s): %s", lockfile, strerror(errno)); 215 216 fsync(fd); 217 218 error = dolock(fd, F_LOCK, 0, 1); 219 ATF_REQUIRE_MSG(error == 0, "initial dolock: %s", strerror(errno)); 220 221 pid = fork(); 222 ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno)); 223 if (pid == 0) { 224 error = dolock(fd, F_LOCK, 1, 1); 225 ATF_REQUIRE_MSG(error == 0, "child dolock: %s", 226 strerror(errno)); 227 dolock(fd, F_LOCK, 0, 1); /* will block */ 228 atf_tc_fail("child did not block"); 229 } 230 sleep(1); /* give child time to grab its lock then block */ 231 232 error = dolock(fd, F_LOCK, 1, 1); 233 ATF_CHECK_MSG(error != EDEADLK, "parent did not deadlock: %s", 234 strerror(errno)); 235 ret = kill(pid, SIGKILL); 236 ATF_REQUIRE_MSG(ret != -1, "failed to kill child: %s", strerror(errno)); 237 238 atf_tc_pass(); 239 } 240 241 ATF_TP_ADD_TCS(tp) 242 { 243 ATF_TP_ADD_TC(tp, randlock); 244 ATF_TP_ADD_TC(tp, deadlock); 245 246 return atf_no_error(); 247 } 248