| /* |
| * 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 "linux_syscall_numbers.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; |
| const char *msg; |
| |
| msg = parse_opts(ac, av, NULL, NULL); |
| if (msg != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| 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]); |
| } |