/* * Copyright (c) 2014 Fujitsu Ltd. * Author: Xiaoguang Wang <wangxg.fnst@cn.fujitsu.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * Description: * Verify that: * Basic test for fcntl(2) using F_GETOWN, F_SETOWN, F_GETOWN_EX, * F_SETOWN_EX, F_GETSIG, F_SETSIG argument. */ #include <stdio.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <pwd.h> #include <sched.h> #include "test.h" #include "config.h" #include "lapi/syscalls.h" #include "safe_macros.h" #include "lapi/fcntl.h" static void setup(void); static void cleanup(void); static void setown_pid_test(void); static void setown_pgrp_test(void); #if defined(HAVE_STRUCT_F_OWNER_EX) static int ownex_enabled; static char *ownex_tconf_msg = "F_GETOWN_EX and F_SETOWN_EX only run on " "kernels that are 2.6.32 and higher"; static void setownex_tid_test(void); static void setownex_pid_test(void); static void setownex_pgrp_test(void); static struct f_owner_ex orig_own_ex; #endif static void signal_parent(void); static void check_io_signal(char *des); static void test_set_and_get_sig(int sig, char *des); static pid_t pid; static pid_t orig_pid; static pid_t pgrp_pid; static struct timespec timeout; static sigset_t newset, oldset; static int test_fd; static int pipe_fds[2]; static void (*testfunc[])(void) = { setown_pid_test, setown_pgrp_test, #if defined(HAVE_STRUCT_F_OWNER_EX) setownex_tid_test, setownex_pid_test, setownex_pgrp_test #endif }; char *TCID = "fcntl31"; int TST_TOTAL = ARRAY_SIZE(testfunc); int main(int ac, char **av) { int lc, i; tst_parse_opts(ac, av, NULL, NULL); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; for (i = 0; i < TST_TOTAL; i++) (*testfunc[i])(); } cleanup(); tst_exit(); } static void setup(void) { int ret; tst_sig(FORK, DEF_HANDLER, cleanup); TEST_PAUSE; /* we have these tests on pipe */ SAFE_PIPE(cleanup, pipe_fds); test_fd = pipe_fds[0]; if (fcntl(test_fd, F_SETFL, O_ASYNC) < 0) tst_brkm(TBROK | TERRNO, cleanup, "fcntl set O_ASYNC failed"); pid = getpid(); ret = setpgrp(); if (ret < 0) tst_brkm(TBROK | TERRNO, cleanup, "setpgrp() failed"); pgrp_pid = getpgid(0); if (pgrp_pid < 0) tst_brkm(TBROK | TERRNO, cleanup, "getpgid() failed"); #if defined(HAVE_STRUCT_F_OWNER_EX) if ((tst_kvercmp(2, 6, 32)) >= 0) { ownex_enabled = 1; /* get original f_owner_ex info */ TEST(fcntl(test_fd, F_GETOWN_EX, &orig_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl get original f_owner_ex info failed"); } } #endif /* get original pid info */ TEST(fcntl(test_fd, F_GETOWN)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl get original pid info failed"); } orig_pid = TEST_RETURN; sigemptyset(&newset); sigaddset(&newset, SIGUSR1); sigaddset(&newset, SIGIO); if (sigprocmask(SIG_SETMASK, &newset, &oldset) < 0) tst_brkm(TBROK | TERRNO, cleanup, "sigprocmask failed"); timeout.tv_sec = 5; timeout.tv_nsec = 0; } static void setown_pid_test(void) { TEST(fcntl(test_fd, F_SETOWN, pid)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(F_SETOWN) set process id failed"); } test_set_and_get_sig(SIGUSR1, "F_GETOWN, F_SETOWN for process ID"); TEST(fcntl(test_fd, F_SETOWN, orig_pid)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(F_SETOWN) restore orig_pid failed"); } } static void setown_pgrp_test(void) { TEST(fcntl(test_fd, F_SETOWN, -pgrp_pid)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(F_SETOWN) set process group id failed"); } test_set_and_get_sig(SIGUSR1, "F_GETOWN, F_SETOWN for process group ID"); TEST(fcntl(test_fd, F_SETOWN, orig_pid)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(F_SETOWN) restore orig_pid failed"); } } #if defined(HAVE_STRUCT_F_OWNER_EX) static void setownex_cleanup(void) { TEST(fcntl(test_fd, F_SETOWN_EX, &orig_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl F_SETOWN_EX restore orig_own_ex failed"); } } static void setownex_tid_test(void) { static struct f_owner_ex tst_own_ex; if (ownex_enabled == 0) { tst_resm(TCONF, "%s", ownex_tconf_msg); return; } tst_own_ex.type = F_OWNER_TID; tst_own_ex.pid = ltp_syscall(__NR_gettid); TEST(fcntl(test_fd, F_SETOWN_EX, &tst_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl F_SETOWN_EX failed"); } test_set_and_get_sig(SIGUSR1, "F_GETOWN_EX, F_SETOWN_EX for thread ID"); setownex_cleanup(); } static void setownex_pid_test(void) { static struct f_owner_ex tst_own_ex; if (ownex_enabled == 0) { tst_resm(TCONF, "%s", ownex_tconf_msg); return; } tst_own_ex.type = F_OWNER_PID; tst_own_ex.pid = pid; TEST(fcntl(test_fd, F_SETOWN_EX, &tst_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl F_SETOWN_EX failed"); } test_set_and_get_sig(SIGUSR1, "F_GETOWN_EX, F_SETOWN_EX for process ID"); setownex_cleanup(); } static void setownex_pgrp_test(void) { static struct f_owner_ex tst_own_ex; if (ownex_enabled == 0) { tst_resm(TCONF, "%s", ownex_tconf_msg); return; } tst_own_ex.type = F_OWNER_PGRP; tst_own_ex.pid = pgrp_pid; TEST(fcntl(test_fd, F_SETOWN_EX, &tst_own_ex)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl F_SETOWN_EX failed"); } test_set_and_get_sig(SIGUSR1, "F_GETOWN_EX, F_SETOWN_EX for process group ID"); setownex_cleanup(); } #endif static void test_set_and_get_sig(int sig, char *des) { int orig_sig; TEST(fcntl(test_fd, F_GETSIG)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(fd, F_GETSIG) get orig_sig failed"); } orig_sig = TEST_RETURN; if (orig_sig == 0 || orig_sig == SIGIO) tst_resm(TINFO, "default io events signal is SIGIO"); TEST(fcntl(test_fd, F_SETSIG, sig)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(fd, F_SETSIG, SIG: %d) failed", sig); } TEST(fcntl(test_fd, F_GETSIG)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl(fd, F_GETSIG) get the set signal failed"); } if (TEST_RETURN != sig) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl F_SETSIG set SIG: %d failed", sig); } check_io_signal(des); /* restore the default signal*/ TEST(fcntl(test_fd, F_SETSIG, orig_sig)); if (TEST_RETURN < 0) { tst_brkm(TFAIL | TTERRNO, cleanup, "fcntl restore default signal failed"); } } static void signal_parent(void) { int ret, fd; fd = pipe_fds[1]; close(pipe_fds[0]); ret = setpgrp(); if (ret < 0) { fprintf(stderr, "child process(%d) setpgrp() failed: %s \n", getpid(), strerror(errno)); } /* Wait for parent process to enter sigtimedwait(). */ tst_process_state_wait2(getppid(), 'S'); ret = write(fd, "c", 1); switch (ret) { case 0: fprintf(stderr, "No data written, something is wrong\n"); break; case -1: fprintf(stderr, "Failed to write to pipe: %s\n", strerror(errno)); break; } close(fd); return; } static void check_io_signal(char *des) { int ret; char c; pid_t child; child = tst_fork(); if (child < 0) tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); if (child == 0) { signal_parent(); exit(0); } else { ret = sigtimedwait(&newset, NULL, &timeout); if (ret == -1) { tst_brkm(TBROK | TERRNO, NULL, "sigtimedwait() failed."); } switch (ret) { case SIGUSR1: tst_resm(TPASS, "fcntl test %s success", des); break; case SIGIO: tst_resm(TFAIL, "received default SIGIO, fcntl test " "%s failed", des); break; default: tst_brkm(TBROK, cleanup, "fcntl io events " "signal mechanism work abnormally"); } SAFE_READ(cleanup, 1, test_fd, &c, 1); wait(NULL); } } static void cleanup(void) { if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) tst_resm(TWARN | TERRNO, "sigprocmask restore oldset failed"); if (pipe_fds[0] > 0 && close(pipe_fds[0]) == -1) tst_resm(TWARN | TERRNO, "close(%d) failed", pipe_fds[0]); if (pipe_fds[1] > 0 && close(pipe_fds[1]) == -1) tst_resm(TWARN | TERRNO, "close(%d) failed", pipe_fds[1]); }