blob: 6072bfa96305c5f6172b79b24d418c59fd21bc97 [file] [log] [blame]
/*
*
* Copyright (C) 2008, Linux Foundation,
* written by Michael Kerrisk <mtk.manpages@gmail.com>
* Initial Porting to LTP by Subrata <subrata@linux.vnet.ibm.com>
*
* Licensed under the GNU GPLv2 or later.
* 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
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "test.h"
#include "lapi/fcntl.h"
#include "linux_syscall_numbers.h"
#define PORT_NUM 33333
#define die(msg) tst_brkm(TBROK|TERRNO, cleanup, msg)
#ifndef SOCK_CLOEXEC
#define SOCK_CLOEXEC O_CLOEXEC
#endif
#ifndef SOCK_NONBLOCK
#define SOCK_NONBLOCK O_NONBLOCK
#endif
#if defined(SYS_ACCEPT4) /* the socketcall() number */
#define USE_SOCKETCALL 1
#endif
char *TCID = "accept04_01";
int TST_TOTAL = 1;
static void setup(void)
{
TEST_PAUSE;
tst_tmpdir();
}
static void cleanup(void)
{
tst_rmdir();
}
#if !(__GLIBC_PREREQ(2, 10))
static int
accept4_01(int fd, struct sockaddr *sockaddr, socklen_t *addrlen, int flags)
{
#ifdef DEBUG
tst_resm(TINFO, "Calling accept4(): flags = %x", flags);
if (flags != 0) {
tst_resm(TINFO, " (");
if (flags & SOCK_CLOEXEC)
tst_resm(TINFO, "SOCK_CLOEXEC");
if ((flags & SOCK_CLOEXEC) && (flags & SOCK_NONBLOCK))
tst_resm(TINFO, " ");
if (flags & SOCK_NONBLOCK)
tst_resm(TINFO, "SOCK_NONBLOCK");
tst_resm(TINFO, ")");
}
tst_resm(TINFO, "\n");
#endif
#if USE_SOCKETCALL
long args[6];
args[0] = fd;
args[1] = (long)sockaddr;
args[2] = (long)addrlen;
args[3] = flags;
return ltp_syscall(__NR_socketcall, SYS_ACCEPT4, args);
#else
return ltp_syscall(__NR_accept4, fd, sockaddr, addrlen, flags);
#endif
}
#endif
static void
do_test(int lfd, struct sockaddr_in *conn_addr,
int closeonexec_flag, int nonblock_flag)
{
int connfd, acceptfd;
int fdf, flf, fdf_pass, flf_pass;
struct sockaddr_in claddr;
socklen_t addrlen;
#ifdef DEBUG
tst_resm(TINFO, "=======================================\n");
#endif
connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd == -1)
die("Socket Error");
if (connect(connfd, (struct sockaddr *)conn_addr,
sizeof(struct sockaddr_in)) == -1)
die("Connect Error");
addrlen = sizeof(struct sockaddr_in);
#if !(__GLIBC_PREREQ(2, 10))
acceptfd = accept4_01(lfd, (struct sockaddr *)&claddr, &addrlen,
closeonexec_flag | nonblock_flag);
#else
acceptfd = accept4(lfd, (struct sockaddr *)&claddr, &addrlen,
closeonexec_flag | nonblock_flag);
#endif
if (acceptfd == -1) {
if (errno == ENOSYS) {
tst_brkm(TCONF, cleanup,
"syscall __NR_accept4 not supported");
} else {
tst_brkm(TBROK | TERRNO, cleanup, "accept4 failed");
}
}
fdf = fcntl(acceptfd, F_GETFD);
if (fdf == -1)
die("fcntl:F_GETFD");
fdf_pass = ((fdf & FD_CLOEXEC) != 0) ==
((closeonexec_flag & SOCK_CLOEXEC) != 0);
#ifdef DEBUG
tst_resm(TINFO, "Close-on-exec flag is %sset (%s); ",
(fdf & FD_CLOEXEC) ? "" : "not ", fdf_pass ? "OK" : "failed");
#endif
if (!fdf_pass)
tst_resm(TFAIL,
"Close-on-exec flag mismatch, should be %x, actual %x",
fdf & FD_CLOEXEC, closeonexec_flag & SOCK_CLOEXEC);
flf = fcntl(acceptfd, F_GETFL);
if (flf == -1)
die("fcntl:F_GETFD");
flf_pass = ((flf & O_NONBLOCK) != 0) ==
((nonblock_flag & SOCK_NONBLOCK) != 0);
#ifdef DEBUG
tst_resm(TINFO, "nonblock flag is %sset (%s)\n",
(flf & O_NONBLOCK) ? "" : "not ", flf_pass ? "OK" : "failed");
#endif
if (!flf_pass)
tst_resm(TFAIL,
"nonblock flag mismatch, should be %x, actual %x",
fdf & O_NONBLOCK, nonblock_flag & SOCK_NONBLOCK);
close(acceptfd);
close(connfd);
if (fdf_pass && flf_pass)
tst_resm(TPASS, "Test passed");
}
static int create_listening_socket(int port_num)
{
struct sockaddr_in svaddr;
int lfd;
int optval;
memset(&svaddr, 0, sizeof(struct sockaddr_in));
svaddr.sin_family = AF_INET;
svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
svaddr.sin_port = htons(port_num);
lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd == -1)
die("Socket Error");
optval = 1;
if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) == -1)
die("Setsockopt Error");
if (bind(lfd, (struct sockaddr *)&svaddr,
sizeof(struct sockaddr_in)) == -1)
die("Bind Error");
if (listen(lfd, 5) == -1)
die("Listen Error");
return lfd;
}
int main(int argc, char *argv[])
{
struct sockaddr_in conn_addr;
int lfd;
int port_num;
setup();
port_num = (argc > 1) ? atoi(argv[1]) : PORT_NUM;
memset(&conn_addr, 0, sizeof(struct sockaddr_in));
conn_addr.sin_family = AF_INET;
conn_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
conn_addr.sin_port = htons(port_num);
lfd = create_listening_socket(port_num);
do_test(lfd, &conn_addr, 0, 0);
do_test(lfd, &conn_addr, SOCK_CLOEXEC, 0);
do_test(lfd, &conn_addr, 0, SOCK_NONBLOCK);
do_test(lfd, &conn_addr, SOCK_CLOEXEC, SOCK_NONBLOCK);
close(lfd);
cleanup();
tst_exit();
}