| /* |
| * |
| * 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(); |
| } |