blob: 9ebc6e261ebfabb7944f6b1e4a880a0303b15953 [file] [log] [blame]
/******************************************************************************/
/* Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd */
/* Author(s): Takahiro Yasui <takahiro.yasui.mp@hitachi.com>, */
/* Yumiko Sugita <yumiko.sugita.yf@hitachi.com>, */
/* Satoshi Fujiwara <sa-fuji@sdl.hitachi.co.jp> */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* */
/******************************************************************************/
/******************************************************************************/
/* */
/* File: ppoll01.c */
/* */
/* Description: This tests the ppoll01() syscall */
/* */
/* */
/* */
/* */
/* */
/* */
/* Usage: <for command-line> */
/* ppoll01 [-c n] [-e][-i n] [-I x] [-p x] [-t] */
/* where, -c n : Run n copies concurrently. */
/* -e : Turn on errno logging. */
/* -i n : Execute test n times. */
/* -I x : Execute test for x seconds. */
/* -P x : Pause for x seconds between iterations. */
/* -t : Turn on syscall timing. */
/* */
/* Total Tests: 1 */
/* */
/* Test Name: ppoll01 */
/* History: Porting from Crackerjack to LTP is done by */
/* Manas Kumar Nayak maknayak@in.ibm.com> */
/******************************************************************************/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <poll.h>
#include "../utils/include_j_h.h"
#include "../utils/common_j_h.c"
#include "ltp_signal.h"
#include "test.h"
#include "linux_syscall_numbers.h"
/* Older versions of glibc don't publish this constant's value. */
#ifndef POLLRDHUP
#define POLLRDHUP 0x2000
#endif
char *TCID = "ppoll01";
int testno;
int TST_TOTAL = 1;
void sighandler(int sig)
{
if (sig == SIGINT)
return;
else
tst_brkm(TBROK, NULL, "received unexpected signal %d", sig);
}
void cleanup(void)
{
tst_rmdir();
}
void setup(void)
{
tst_sig(FORK, sighandler, cleanup);
TEST_PAUSE;
tst_tmpdir();
}
/*
* Macros
*/
#define SYSCALL_NAME "ppoll"
/*
* Global variables
*/
enum test_type {
NORMAL,
MASK_SIGNAL,
TIMEOUT,
FD_ALREADY_CLOSED,
SEND_SIGINT,
SEND_SIGINT_RACE_TEST,
INVALID_NFDS,
INVALID_FDS,
MINUS_NSEC,
TOO_LARGE_NSEC,
};
/*
* Data Structure
*/
struct test_case {
short expect_revents;
int ttype;
int ret;
int err;
};
/* Test cases
*
* test status of errors on man page
*
* EBADF can't check because EBADF never happen even though
* fd was invalid. In this case, information of invalid
* fd is set in revents
* EFAULT v ('fds' array in the invalid address space)
* EINTR v (a non blocked signal was caught)
* EINVAL v ('nfds' is over the 'RLIMIT_NOFILE' value)
* ENOMEM can't check because it's difficult to create no-memory
*/
static struct test_case tcase[] = {
{ // case00
.ttype = NORMAL,
.expect_revents = POLLIN | POLLOUT,
.ret = 0,
.err = 0,
},
{ // case01
.ttype = MASK_SIGNAL,
.expect_revents = 0, // don't care
.ret = 0,
.err = 0,
},
{ // case02
.ttype = TIMEOUT,
.expect_revents = 0, // don't care
.ret = 0,
.err = 0,
},
{ // case03
.ttype = FD_ALREADY_CLOSED,
.expect_revents = POLLNVAL,
.ret = 0,.err = 0,
},
{ // case04
.ttype = SEND_SIGINT,
.ret = -1,
.err = EINTR,
},
{ // case05
.ttype = SEND_SIGINT_RACE_TEST,
.ret = -1,
.err = EINTR,
},
{ // case06
.ttype = INVALID_NFDS,
.ret = -1,
.err = EINVAL,
},
{ // case07
.ttype = INVALID_FDS,
.ret = -1,
.err = EFAULT,},
#if 0
{ // case08
.ttype = MINUS_NSEC,
.ret = -1,
.err = EINVAL, // RHEL4U1 + 2.6.18 returns SUCCESS
},
{ // case09
.ttype = TOO_LARGE_NSEC,
.ret = -1,
.err = EINVAL, // RHEL4U1 + 2.6.18 returns SUCCESS
},
#endif
};
#define NUM_TEST_FDS 1
/*
* do_test()
*
* Input : TestCase Data
* Return : RESULT_OK(0), RESULT_NG(1)
*
*/
static int do_test(struct test_case *tc)
{
int sys_ret, sys_errno;
int result = RESULT_OK;
int fd = -1, cmp_ok = 1;
char fpath[PATH_MAX];
struct pollfd *p_fds, fds[NUM_TEST_FDS];
unsigned int nfds = NUM_TEST_FDS;
struct timespec *p_ts, ts;
sigset_t *p_sigmask, sigmask;
pid_t pid = 0;
TEST(fd = setup_file(".", "test.file", fpath));
if (fd < 0)
return 1;
fds[0].fd = fd;
fds[0].events = POLLIN | POLLPRI | POLLOUT | POLLRDHUP;
fds[0].revents = 0;
p_fds = fds;
p_ts = NULL;
p_sigmask = NULL;
switch (tc->ttype) {
case TIMEOUT:
nfds = 0;
ts.tv_sec = 0;
ts.tv_nsec = 50000000; // 50msec
p_ts = &ts;
break;
case FD_ALREADY_CLOSED:
TEST(close(fd));
fd = -1;
TEST(cleanup_file(fpath));
break;
case MASK_SIGNAL:
TEST(sigemptyset(&sigmask));
TEST(sigaddset(&sigmask, SIGINT));
p_sigmask = &sigmask;
nfds = 0;
ts.tv_sec = 0;
ts.tv_nsec = 300000000; // 300msec => need to be enough for
// waiting the signal
p_ts = &ts;
// fallthrough
case SEND_SIGINT:
nfds = 0;
pid = create_sig_proc(100000, SIGINT, UINT_MAX); // 100msec
if (pid < 0)
return 1;
break;
case SEND_SIGINT_RACE_TEST:
/* block the INT signal */
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGINT);
sigprocmask(SIG_SETMASK, &sigmask, NULL);
/* and let it be unblocked when the syscall runs */
sigemptyset(&sigmask);
p_sigmask = &sigmask;
nfds = 0;
ts.tv_sec = 0;
ts.tv_nsec = 300000000; // 300msec => need to be enough for
// waiting the signal
p_ts = &ts;
nfds = 0;
pid = create_sig_proc(0, SIGINT, 1);
if (pid < 0) {
result = 1;
goto cleanup;
}
break;
case INVALID_NFDS:
//nfds = RLIMIT_NOFILE + 1; //==> RHEL4U1 + 2.6.18 returns SUCCESS
nfds = -1;
break;
case INVALID_FDS:
p_fds = (void *)0xc0000000;
break;
case MINUS_NSEC:
ts.tv_sec = 0;
ts.tv_nsec = -1;
p_ts = &ts;
break;
case TOO_LARGE_NSEC:
ts.tv_sec = 0;
ts.tv_nsec = 1000000000;
p_ts = &ts;
break;
}
/*
* Execute system call
*/
errno = 0;
sys_ret = ltp_syscall(__NR_ppoll, p_fds, nfds, p_ts, p_sigmask,
SIGSETSIZE);
sys_errno = errno;
if (sys_ret <= 0 || tc->ret < 0)
goto TEST_END;
cmp_ok = fds[0].revents == tc->expect_revents;
tst_resm(TINFO, "EXPECT: revents=0x%04x", tc->expect_revents);
tst_resm(TINFO, "RESULT: revents=0x%04x", fds[0].revents);
TEST_END:
/*
* Check results
*/
if (tc->ttype == SEND_SIGINT_RACE_TEST) {
int sig;
sigprocmask(SIG_SETMASK, NULL, &sigmask);
for (sig = 1; sig < SIGRTMAX; sig++) {
TEST(sigismember(&sigmask, sig));
if (TEST_RETURN < 0 && TEST_ERRNO == EINVAL
&& sig != SIGINT)
continue; /* let's ignore errors if they are for other signal than SIGINT that we set */
if ((sig == SIGINT) != (TEST_RETURN != 0)) {
tst_resm(TFAIL,
"Bad value of signal mask, signal %d is %s",
sig, TEST_RETURN ? "on" : "off");
cmp_ok |= 1;
}
}
}
result |= (sys_errno != tc->err) || !cmp_ok;
PRINT_RESULT_CMP(sys_ret >= 0, tc->ret, tc->err, sys_ret, sys_errno,
cmp_ok);
cleanup:
if (fd >= 0) {
close(fd);
cleanup_file(fpath);
}
sigemptyset(&sigmask);
sigprocmask(SIG_SETMASK, &sigmask, NULL);
if (pid > 0) {
int st;
kill(pid, SIGTERM);
wait(&st);
}
return result;
}
/*
* main()
*/
int main(int ac, char **av)
{
int i;
int ret;
setup();
ret = 0;
for (i = 0; ret == 0 && i < (sizeof(tcase) / sizeof(tcase[0])); i++) {
tst_resm(TINFO, "(case%02d) START", i);
ret = do_test(&tcase[i]);
tst_resm(TINFO, "(case%02d) END => %s", i, (ret == 0) ? "OK"
: "NG");
}
cleanup();
tst_exit();
}