Home | History | Annotate | Download | only in fcntl
      1 /*
      2  *
      3  *   Copyright (c) International Business Machines  Corp., 2001
      4  *
      5  *   This program is free software;  you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 2 of the License, or
      8  *   (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  *   the GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program;  if not, write to the Free Software
     17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  */
     19 
     20 /*
     21  * NAME
     22  *	fcntl17.c
     23  *
     24  * DESCRIPTION
     25  *	Check deadlock detection for file locking
     26  *
     27  * ALGORITHM
     28  *	The parent forks off 3 children. The parent controls the children
     29  *	with messages via pipes to create a delayed deadlock between the
     30  *	second and third child.
     31  *
     32  * USAGE
     33  *	fcntl17
     34  *
     35  * HISTORY
     36  *	07/2001 Ported by Wayne Boyer
     37  *      04/2002 Minor fixes by William Jay Huie (testcase name
     38 		fcntl05 => fcntl17, check signal return for SIG_ERR)
     39  *
     40  * RESTRICTIONS
     41  *	None
     42  */
     43 
     44 #ifndef _GNU_SOURCE
     45 #define _GNU_SOURCE
     46 #endif
     47 
     48 #include <fcntl.h>
     49 #include <errno.h>
     50 #include <signal.h>
     51 #include <sys/stat.h>
     52 #include <sys/types.h>
     53 #include <sys/wait.h>
     54 #include <inttypes.h>
     55 
     56 #include "test.h"
     57 
     58 char *TCID = "fcntl17";
     59 int TST_TOTAL = 1;
     60 
     61 #define STRINGSIZE	27
     62 #define STRING		"abcdefghijklmnopqrstuvwxyz\n"
     63 #define STOP		0xFFF0
     64 #define TIME_OUT	10
     65 
     66 /* global variables */
     67 int parent_pipe[2];
     68 int child_pipe1[2];
     69 int child_pipe2[2];
     70 int child_pipe3[2];
     71 int file_fd;
     72 pid_t parent_pid, child_pid1, child_pid2, child_pid3;
     73 int child_stat;
     74 struct flock lock1 = { (short)F_WRLCK, (short)0, 2, 5, (short)0 };
     75 struct flock lock2 = { (short)F_WRLCK, (short)0, 9, 5, (short)0 };
     76 struct flock lock3 = { (short)F_WRLCK, (short)0, 17, 5, (short)0 };
     77 struct flock lock4 = { (short)F_WRLCK, (short)0, 17, 5, (short)0 };
     78 struct flock lock5 = { (short)F_WRLCK, (short)0, 2, 14, (short)0 };
     79 struct flock unlock = { (short)F_UNLCK, (short)0, 0, 0, (short)0 };
     80 
     81 /* prototype declarations */
     82 int setup();
     83 void cleanup();
     84 int parent_wait();
     85 void parent_free();
     86 void child_wait();
     87 void child_free();
     88 void do_child1();
     89 void do_child2();
     90 void do_child3();
     91 int do_test(struct flock *, pid_t);
     92 void stop_children();
     93 void catch_child();
     94 void catch_alarm();
     95 char *str_type();
     96 
     97 int setup(void)
     98 {
     99 	char *buf = STRING;
    100 	char template[PATH_MAX];
    101 	struct sigaction act;
    102 
    103 	tst_sig(FORK, DEF_HANDLER, NULL);
    104 	umask(0);
    105 	TEST_PAUSE;
    106 	tst_tmpdir();		/* make temp dir and cd to it */
    107 
    108 	if (pipe(parent_pipe) < 0) {
    109 		tst_resm(TFAIL, "Couldn't create parent_pipe! errno = %d",
    110 			 errno);
    111 		return 1;
    112 	}
    113 	if (pipe(child_pipe1) < 0) {
    114 		tst_resm(TFAIL, "Couldn't create child_pipe1! errno = %d",
    115 			 errno);
    116 		return 1;
    117 	}
    118 	if (pipe(child_pipe2) < 0) {
    119 		tst_resm(TFAIL, "Couldn't create child_pipe2! errno = %d",
    120 			 errno);
    121 		return 1;
    122 	}
    123 	if (pipe(child_pipe3) < 0) {
    124 		tst_resm(TFAIL, "Couldn't create child_pipe3! errno = %d",
    125 			 errno);
    126 		return 1;
    127 	}
    128 	parent_pid = getpid();
    129 	snprintf(template, PATH_MAX, "fcntl17XXXXXX");
    130 
    131 	if ((file_fd = mkstemp(template)) < 0) {
    132 		tst_resm(TFAIL, "Couldn't open temp file! errno = %d", errno);
    133 	}
    134 
    135 	if (write(file_fd, buf, STRINGSIZE) < 0) {
    136 		tst_resm(TFAIL, "Couldn't write to temp file! errno = %d",
    137 			 errno);
    138 	}
    139 
    140 	memset(&act, 0, sizeof(act));
    141 	act.sa_handler = catch_alarm;
    142 	sigemptyset(&act.sa_mask);
    143 	sigaddset(&act.sa_mask, SIGALRM);
    144 	if (sigaction(SIGALRM, &act, NULL) < 0) {
    145 		tst_resm(TFAIL, "SIGALRM signal setup failed, errno: %d",
    146 			 errno);
    147 		return 1;
    148 	}
    149 
    150 	memset(&act, 0, sizeof(act));
    151 	act.sa_handler = catch_child;
    152 	sigemptyset(&act.sa_mask);
    153 	sigaddset(&act.sa_mask, SIGCHLD);
    154 	if (sigaction(SIGCHLD, &act, NULL) < 0) {
    155 		tst_resm(TFAIL, "SIGCHLD signal setup failed, errno: %d", errno);
    156 		return 1;
    157 	}
    158 	return 0;
    159 }
    160 
    161 void cleanup(void)
    162 {
    163 	if (child_pid1 > 0)
    164 		kill(child_pid1, 9);
    165 
    166 	if (child_pid2 > 0)
    167 		kill(child_pid2, 9);
    168 
    169 	if (child_pid3 > 0)
    170 		kill(child_pid3, 9);
    171 
    172 	close(file_fd);
    173 	tst_rmdir();
    174 
    175 }
    176 
    177 void do_child1(void)
    178 {
    179 	int err;
    180 
    181 	close(parent_pipe[0]);
    182 	close(child_pipe1[1]);
    183 	close(child_pipe2[0]);
    184 	close(child_pipe2[1]);
    185 	close(child_pipe3[0]);
    186 	close(child_pipe3[1]);
    187 
    188 	child_wait(child_pipe1[0]);
    189 	tst_resm(TINFO, "child 1 starting");
    190 	if (fcntl(file_fd, F_SETLK, &lock1) < 0) {
    191 		err = errno;
    192 		tst_resm(TINFO, "child 1 lock err %d", err);
    193 		parent_free(err);
    194 	} else {
    195 		tst_resm(TINFO, "child 1 pid %d locked", getpid());
    196 		parent_free(0);
    197 	}
    198 
    199 	child_wait(child_pipe1[0]);
    200 	tst_resm(TINFO, "child 1 resuming");
    201 	fcntl(file_fd, F_SETLK, &unlock);
    202 	tst_resm(TINFO, "child 1 unlocked");
    203 
    204 	child_wait(child_pipe1[0]);
    205 	tst_resm(TINFO, "child 1 exiting");
    206 	exit(1);
    207 }
    208 
    209 void do_child2(void)
    210 {
    211 	int err;
    212 
    213 	close(parent_pipe[0]);
    214 	close(child_pipe1[0]);
    215 	close(child_pipe1[1]);
    216 	close(child_pipe2[1]);
    217 	close(child_pipe3[0]);
    218 	close(child_pipe3[1]);
    219 
    220 	child_wait(child_pipe2[0]);
    221 	tst_resm(TINFO, "child 2 starting");
    222 	if (fcntl(file_fd, F_SETLK, &lock2) < 0) {
    223 		err = errno;
    224 		tst_resm(TINFO, "child 2 lock err %d", err);
    225 		parent_free(err);
    226 	} else {
    227 		tst_resm(TINFO, "child 2 pid %d locked", getpid());
    228 		parent_free(0);
    229 	}
    230 
    231 	child_wait(child_pipe2[0]);
    232 	tst_resm(TINFO, "child 2 resuming");
    233 	if (fcntl(file_fd, F_SETLKW, &lock4) < 0) {
    234 		err = errno;
    235 		tst_resm(TINFO, "child 2 lockw err %d", err);
    236 		parent_free(err);
    237 	} else {
    238 		tst_resm(TINFO, "child 2 lockw locked");
    239 		parent_free(0);
    240 	}
    241 
    242 	child_wait(child_pipe2[0]);
    243 	tst_resm(TINFO, "child 2 exiting");
    244 	exit(1);
    245 }
    246 
    247 void do_child3(void)
    248 {
    249 	int err;
    250 
    251 	close(parent_pipe[0]);
    252 	close(child_pipe1[0]);
    253 	close(child_pipe1[1]);
    254 	close(child_pipe2[0]);
    255 	close(child_pipe2[1]);
    256 	close(child_pipe3[1]);
    257 
    258 	child_wait(child_pipe3[0]);
    259 	tst_resm(TINFO, "child 3 starting");
    260 	if (fcntl(file_fd, F_SETLK, &lock3) < 0) {
    261 		err = errno;
    262 		tst_resm(TINFO, "child 3 lock err %d", err);
    263 		parent_free(err);
    264 	} else {
    265 		tst_resm(TINFO, "child 3 pid %d locked", getpid());
    266 		parent_free(0);
    267 	}
    268 
    269 	child_wait(child_pipe3[0]);
    270 	tst_resm(TINFO, "child 3 resuming");
    271 	if (fcntl(file_fd, F_SETLKW, &lock5) < 0) {
    272 		err = errno;
    273 		tst_resm(TINFO, "child 3 lockw err %d", err);
    274 		parent_free(err);
    275 	} else {
    276 		tst_resm(TINFO, "child 3 lockw locked");
    277 		parent_free(0);
    278 	}
    279 
    280 	child_wait(child_pipe3[0]);
    281 	tst_resm(TINFO, "child 3 exiting");
    282 	exit(1);
    283 }
    284 
    285 int do_test(struct flock *lock, pid_t pid)
    286 {
    287 	struct flock fl;
    288 
    289 	fl.l_type = /* lock->l_type */ F_RDLCK;
    290 	fl.l_whence = lock->l_whence;
    291 	fl.l_start = lock->l_start;
    292 	fl.l_len = lock->l_len;
    293 	fl.l_pid = (short)0;
    294 	if (fcntl(file_fd, F_GETLK, &fl) < 0) {
    295 		tst_resm(TFAIL, "fcntl on file failed, errno =%d", errno);
    296 		return 1;
    297 	}
    298 
    299 	if (fl.l_type != lock->l_type) {
    300 		tst_resm(TFAIL, "lock type is wrong should be %s is %s",
    301 			 str_type(lock->l_type), str_type(fl.l_type));
    302 		return 1;
    303 	}
    304 
    305 	if (fl.l_whence != lock->l_whence) {
    306 		tst_resm(TFAIL, "lock whence is wrong should be %d is %d",
    307 			 lock->l_whence, fl.l_whence);
    308 		return 1;
    309 	}
    310 
    311 	if (fl.l_start != lock->l_start) {
    312 		tst_resm(TFAIL, "region starts in wrong place, "
    313 			 "should be %" PRId64 " is %" PRId64,
    314 			 (int64_t) lock->l_start, (int64_t) fl.l_start);
    315 		return 1;
    316 	}
    317 
    318 	if (fl.l_len != lock->l_len) {
    319 		tst_resm(TFAIL,
    320 			 "region length is wrong, should be %" PRId64 " is %"
    321 			 PRId64, (int64_t) lock->l_len, (int64_t) fl.l_len);
    322 		return 1;
    323 	}
    324 
    325 	if (fl.l_pid != pid) {
    326 		tst_resm(TFAIL, "locking pid is wrong, should be %d is %d",
    327 			 pid, fl.l_pid);
    328 		return 1;
    329 	}
    330 	return 0;
    331 }
    332 
    333 char *str_type(int type)
    334 {
    335 	static char buf[20];
    336 
    337 	switch (type) {
    338 	case F_RDLCK:
    339 		return ("F_RDLCK");
    340 	case F_WRLCK:
    341 		return ("F_WRLCK");
    342 	case F_UNLCK:
    343 		return ("F_UNLCK");
    344 	default:
    345 		sprintf(buf, "BAD VALUE: %d", type);
    346 		return (buf);
    347 	}
    348 }
    349 
    350 void parent_free(int arg)
    351 {
    352 	if (write(parent_pipe[1], &arg, sizeof(arg)) != sizeof(arg)) {
    353 		tst_resm(TFAIL, "couldn't send message to parent");
    354 		exit(1);
    355 	}
    356 }
    357 
    358 int parent_wait(void)
    359 {
    360 	int arg;
    361 
    362 	if (read(parent_pipe[0], &arg, sizeof(arg)) != sizeof(arg)) {
    363 		tst_resm(TFAIL, "parent_wait() failed");
    364 		return (errno);
    365 	}
    366 	return (arg);
    367 }
    368 
    369 void child_free(int fd, int arg)
    370 {
    371 	if (write(fd, &arg, sizeof(arg)) != sizeof(arg)) {
    372 		tst_resm(TFAIL, "couldn't send message to child");
    373 		exit(1);
    374 	}
    375 }
    376 
    377 void child_wait(int fd)
    378 {
    379 	int arg;
    380 
    381 	if (read(fd, &arg, sizeof(arg)) != sizeof(arg)) {
    382 		tst_resm(TFAIL, "couldn't get message from parent");
    383 		exit(1);
    384 	} else if (arg == (short)STOP) {
    385 		exit(0);
    386 	}
    387 }
    388 
    389 void stop_children(void)
    390 {
    391 	int arg;
    392 
    393 	signal(SIGCHLD, SIG_DFL);
    394 	arg = STOP;
    395 	child_free(child_pipe1[1], arg);
    396 	child_free(child_pipe2[1], arg);
    397 	child_free(child_pipe3[1], arg);
    398 	waitpid(child_pid1, &child_stat, 0);
    399 	child_pid1 = 0;
    400 	waitpid(child_pid2, &child_stat, 0);
    401 	child_pid2 = 0;
    402 	waitpid(child_pid3, &child_stat, 0);
    403 	child_pid3 = 0;
    404 }
    405 
    406 void catch_child(void)
    407 {
    408 	tst_resm(TFAIL, "Unexpected death of child process");
    409 	cleanup();
    410 }
    411 
    412 void catch_alarm(void)
    413 {
    414 	sighold(SIGCHLD);
    415 	/*
    416 	 * Timer has runout and the children have not detected the deadlock.
    417 	 * Need to kill the kids and exit
    418 	 */
    419 	if (child_pid1 != 0 && (kill(child_pid1, SIGKILL)) < 0) {
    420 		tst_resm(TFAIL, "Attempt to signal child 1 failed.");
    421 	}
    422 
    423 	if (child_pid2 != 0 && (kill(child_pid2, SIGKILL)) < 0) {
    424 		tst_resm(TFAIL, "Attempt to signal child 2 failed.");
    425 	}
    426 	if (child_pid3 != 0 && (kill(child_pid3, SIGKILL)) < 0) {
    427 		tst_resm(TFAIL, "Attempt to signal child 2 failed.");
    428 	}
    429 	tst_resm(TFAIL, "Alarm expired, deadlock not detected");
    430 	tst_resm(TWARN, "You may need to kill child processes by hand");
    431 	cleanup();
    432 }
    433 
    434 int main(int ac, char **av)
    435 {
    436 	int ans;
    437 	int lc;
    438 	int fail = 0;
    439 
    440 	tst_parse_opts(ac, av, NULL, NULL);
    441 #ifdef UCLINUX
    442 	maybe_run_child(&do_child1, "nddddddddd", 1, &file_fd,
    443 			&parent_pipe[0], &parent_pipe[1],
    444 			&child_pipe1[0], &child_pipe1[1],
    445 			&child_pipe2[0], &child_pipe2[1],
    446 			&child_pipe3[0], &child_pipe3[1]);
    447 	maybe_run_child(&do_child2, "nddddddddd", 2, &file_fd,
    448 			&parent_pipe[0], &parent_pipe[1],
    449 			&child_pipe1[0], &child_pipe1[1],
    450 			&child_pipe2[0], &child_pipe2[1],
    451 			&child_pipe3[0], &child_pipe3[1]);
    452 	maybe_run_child(&do_child3, "nddddddddd", 3, &file_fd,
    453 			&parent_pipe[0], &parent_pipe[1],
    454 			&child_pipe1[0], &child_pipe1[1],
    455 			&child_pipe2[0], &child_pipe2[1],
    456 			&child_pipe3[0], &child_pipe3[1]);
    457 #endif
    458 
    459 	if (setup()) {		/* global testup */
    460 		tst_resm(TINFO, "setup failed");
    461 		cleanup();
    462 	}
    463 
    464 	/* check for looping state if -i option is given */
    465 	for (lc = 0; TEST_LOOPING(lc); lc++) {
    466 		/* reset tst_count in case we are looping */
    467 		tst_count = 0;
    468 
    469 		tst_resm(TINFO, "Enter preparation phase");
    470 		if ((child_pid1 = FORK_OR_VFORK()) == 0) {	/* first child */
    471 #ifdef UCLINUX
    472 			if (self_exec(av[0], "nddddddddd", 1, file_fd,
    473 				      parent_pipe[0], parent_pipe[1],
    474 				      child_pipe1[0], child_pipe1[1],
    475 				      child_pipe2[0], child_pipe2[1],
    476 				      child_pipe3[0], child_pipe3[1]) < 0) {
    477 				perror("self_exec failed, child 1");
    478 				cleanup();
    479 			}
    480 #else
    481 			do_child1();
    482 #endif
    483 		} else if (child_pid1 < 0)
    484 			tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 1");
    485 
    486 		/* parent */
    487 
    488 		if ((child_pid2 = fork()) == 0) {	/* second child */
    489 #ifdef UCLINUX
    490 			if (self_exec(av[0], "nddddddddd", 2, file_fd,
    491 				      parent_pipe[0], parent_pipe[1],
    492 				      child_pipe1[0], child_pipe1[1],
    493 				      child_pipe2[0], child_pipe2[1],
    494 				      child_pipe3[0], child_pipe3[1]) < 0) {
    495 				perror("self_exec failed, child 2");
    496 				cleanup();
    497 			}
    498 #else
    499 			do_child2();
    500 #endif
    501 		} else if (child_pid2 < 0) {
    502 			tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 2");
    503 		}
    504 
    505 		/* parent */
    506 
    507 		if ((child_pid3 = fork()) == 0) {	/* third child */
    508 #ifdef UCLINUX
    509 			if (self_exec(av[0], "nddddddddd", 3, file_fd,
    510 				      parent_pipe[0], parent_pipe[1],
    511 				      child_pipe1[0], child_pipe1[1],
    512 				      child_pipe2[0], child_pipe2[1],
    513 				      child_pipe3[0], child_pipe3[1]) < 0) {
    514 				perror("self_exec failed, child 3");
    515 				cleanup();
    516 			}
    517 #else
    518 			do_child3();
    519 #endif
    520 			do_child3();
    521 		} else if (child_pid3 < 0) {
    522 			tst_brkm(TBROK|TERRNO, cleanup, "Fork failed: child 3");
    523 		}
    524 		/* parent */
    525 
    526 		close(parent_pipe[1]);
    527 		close(child_pipe1[0]);
    528 		close(child_pipe2[0]);
    529 		close(child_pipe3[0]);
    530 		tst_resm(TINFO, "Exit preparation phase");
    531 
    532 /* //block1: */
    533 		tst_resm(TINFO, "Enter block 1");
    534 		fail = 0;
    535 		/*
    536 		 * child 1 puts first lock (bytes 2-7)
    537 		 */
    538 		child_free(child_pipe1[1], 0);
    539 		if (parent_wait()) {
    540 			tst_resm(TFAIL, "didn't set first child's lock, "
    541 				 "errno: %d", errno);
    542 		}
    543 		if (do_test(&lock1, child_pid1)) {
    544 			tst_resm(TINFO, "do_test failed child 1");
    545 			fail = 1;
    546 		}
    547 
    548 		/*
    549 		 * child 2 puts second lock (bytes 9-14)
    550 		 */
    551 		child_free(child_pipe2[1], 0);
    552 		if (parent_wait()) {
    553 			tst_resm(TINFO, "didn't set second child's lock, "
    554 				 "errno: %d", errno);
    555 			fail = 1;
    556 		}
    557 		if (do_test(&lock2, child_pid2)) {
    558 			tst_resm(TINFO, "do_test failed child 2");
    559 			fail = 1;
    560 		}
    561 
    562 		/*
    563 		 * child 3 puts third lock (bytes 17-22)
    564 		 */
    565 		child_free(child_pipe3[1], 0);
    566 		if (parent_wait()) {
    567 			tst_resm(TFAIL, "didn't set third child's lock, "
    568 				 "errno: %d", errno);
    569 			fail = 1;
    570 		}
    571 		if (do_test(&lock3, child_pid3)) {
    572 			tst_resm(TINFO, "do_test failed child 3");
    573 			fail = 1;
    574 		}
    575 
    576 		/*
    577 		 * child 2 tries to lock same range as
    578 		 * child 3's first lock.
    579 		 */
    580 		child_free(child_pipe2[1], 0);
    581 
    582 		/*
    583 		 * child 3 tries to lock same range as
    584 		 * child 1 and child 2's first locks.
    585 		 */
    586 		child_free(child_pipe3[1], 0);
    587 
    588 		/*
    589 		 * Tell child 1 to release its lock. This should cause a
    590 		 * delayed deadlock between child 2 and child 3.
    591 		 */
    592 		child_free(child_pipe1[1], 0);
    593 
    594 		/*
    595 		 * Setup an alarm to go off in case the deadlock is not
    596 		 * detected
    597 		 */
    598 		alarm(TIME_OUT);
    599 
    600 		/*
    601 		 * should get a message from child 3 telling that its
    602 		 * second lock EDEADLOCK
    603 		 */
    604 		if ((ans = parent_wait()) != EDEADLK) {
    605 			tst_resm(TFAIL, "child 2 didn't deadlock, "
    606 				 "returned: %d", ans);
    607 			fail = 1;
    608 		}
    609 
    610 		/*
    611 		 * Double check that lock 2 and lock 3 are still right
    612 		 */
    613 		do_test(&lock2, child_pid2);
    614 		do_test(&lock3, child_pid3);
    615 
    616 		stop_children();
    617 
    618 		if (fail) {
    619 			tst_resm(TFAIL, "Block 1 FAILED");
    620 		} else {
    621 			tst_resm(TPASS, "Block 1 PASSED");
    622 		}
    623 		tst_resm(TINFO, "Exit block 1");
    624 	}
    625 	cleanup();
    626 	tst_exit();
    627 }
    628