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