diff mbox series

[v2,1/2] connect01: Convert to new API

Message ID 20240514045110.85640-1-xuyang2018.jy@fujitsu.com
State New
Headers show
Series [v2,1/2] connect01: Convert to new API | expand

Commit Message

Yang Xu \(Fujitsu\) May 14, 2024, 4:51 a.m. UTC
Signed-off-by: Yang Xu <xuyang2018.jy@fujitsu.com>
---
 testcases/kernel/syscalls/connect/connect01.c | 401 +++++++-----------
 1 file changed, 146 insertions(+), 255 deletions(-)
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/connect/connect01.c b/testcases/kernel/syscalls/connect/connect01.c
index 660c4f7a9..52a6f154b 100644
--- a/testcases/kernel/syscalls/connect/connect01.c
+++ b/testcases/kernel/syscalls/connect/connect01.c
@@ -1,308 +1,199 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
- *
- *   Copyright (c) International Business Machines  Corp., 2001
- *
- *   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
+ * Copyright (c) International Business Machines  Corp., 2001
+ * Copyright (c) Linux Test Project, 2006-2024
  */
 
-/*
- * Test Name: connect01
+/*\
+ * [Description]
  *
- * Test Description:
- *  Verify that connect() returns the proper errno for various failure cases
- *
- * Usage:  <for command-line>
- *  connect01 [-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.
- *
- * HISTORY
- *	07/2001 Ported by Wayne Boyer
- *
- * RESTRICTIONS:
- *  None.
+ * Verify that connect() returns the proper errno for various failure case
  *
  */
 
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/signal.h>
-#include <sys/un.h>
-
-#include <netinet/in.h>
-
-#include "test.h"
-#include "safe_macros.h"
-
-char *TCID = "connect01";
-int testno;
-
-int s, s2;			/* socket descriptor */
-struct sockaddr_in sin1, sin2, sin3, sin4;
-static int sfd;			/* shared between start_server and do_child */
-
-void setup(void), setup0(void), setup1(void), setup2(void),
-cleanup(void), cleanup0(void), cleanup1(void), do_child(void);
-
-static pid_t start_server(struct sockaddr_in *);
+#include <sys/wait.h>
+#include <pwd.h>
+#include "tst_test.h"
+#include "lapi/syscalls.h"
 
-struct test_case_t {		/* test case structure */
-	int domain;		/* PF_INET, PF_UNIX, ... */
-	int type;		/* SOCK_STREAM, SOCK_DGRAM ... */
-	int proto;		/* protocol number (usually 0 = default) */
-	struct sockaddr *sockaddr;	/* socket address buffer */
-	int salen;		/* connect's 3rd argument */
-	int retval;		/* syscall return value */
-	int experrno;		/* expected errno */
-	void (*setup) (void);
-	void (*cleanup) (void);
+static int fd_share;
+static int fd_server;
+
+static struct sockaddr_in sock1;
+static struct sockaddr_in sock2;
+static struct sockaddr_in sock3;
+
+static pid_t pid;
+
+static void setup1(unsigned int);
+static void setup2(unsigned int);
+static void setup3(unsigned int);
+static void cleanup1(void);
+
+static struct test_case_t {
+	int domain;
+	int type;
+	int proto;
+	struct sockaddr *sockaddr;
+	int salen;
+	int exp_errno;
+	void (*setup)(unsigned int i);
+	void (*cleanup)(void);
 	char *desc;
-} tdat[] = {
-	{
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1,
-		    sizeof(struct sockaddr_in), -1, EBADF, setup0,
-		    cleanup0, "bad file descriptor"},
-	{
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)-1,
-		    sizeof(struct sockaddr_in), -1, EFAULT, setup1,
-		    cleanup1, "invalid socket buffer"},
-	{
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1,
-		    3, -1, EINVAL, setup1, cleanup1, "invalid salen"}, {
-	0, 0, 0, (struct sockaddr *)&sin1,
-		    sizeof(sin1), -1, ENOTSOCK, setup0, cleanup0,
-		    "invalid socket"}
-	, {
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin1,
-		    sizeof(sin1), -1, EISCONN, setup2, cleanup1,
-		    "already connected"}
-	, {
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin2,
-		    sizeof(sin2), -1, ECONNREFUSED, setup1, cleanup1,
-		    "connection refused"}
-	, {
-	PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sin4,
-		    sizeof(sin4), -1, EAFNOSUPPORT, setup1, cleanup1,
-		    "invalid address family"}
-,};
-
-int TST_TOTAL = sizeof(tdat) / sizeof(tdat[0]);
-
-/**
- * bionic's connect() implementation calls netdClientInitConnect() before
- * sending the request to the kernel.  We need to bypass this, or the test will
- * segfault during the addr = (struct sockaddr *)-1 testcase. We had cases where
- * tests started to segfault on glibc upgrade or in special conditions where
- * libc had to convert structure layouts between 32bit/64bit userspace/kernel =>
- * safer to call the raw syscall regardless of the libc implementation.
- */
-#include "lapi/syscalls.h"
+} tcases[] = {
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sock1,
+		sizeof(struct sockaddr_in), EBADF, setup1, cleanup1,
+		"sockfd is not a valid open file descriptor"},
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)-1,
+		sizeof(struct sockaddr_in), EFAULT, setup2, cleanup1,
+		"socket structure address is outside the user's address space"},
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sock1,
+		3, EINVAL, setup2, cleanup1,
+		"addrlen is not valid"},
+	{0, 0, 0, (struct sockaddr *)&sock1,
+		sizeof(sock1), ENOTSOCK, setup1, cleanup1,
+		"file descriptor sockfd does not refer to a socket"},
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sock1,
+		sizeof(sock1), EISCONN, setup3, cleanup1,
+		"socket is already connected"},
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sock2,
+		sizeof(sock2), ECONNREFUSED, setup2, cleanup1,
+		"connect on a socket found no one listening on remote address"},
+	{PF_INET, SOCK_STREAM, 0, (struct sockaddr *)&sock3,
+		sizeof(sock3), EAFNOSUPPORT, setup2, cleanup1,
+		"address doesn't have the correct address family in sa_family"},
+};
 
 static int sys_connect(int sockfd, const struct sockaddr *addr,
-		socklen_t addrlen)
+	socklen_t addrlen)
 {
 	return tst_syscall(__NR_connect, sockfd, addr, addrlen);
 }
 
-#define connect(sockfd, addr, addrlen) sys_connect(sockfd, addr, addrlen)
-
-int main(int argc, char *argv[])
+static void do_child(void)
 {
-	int lc;
-
-	tst_parse_opts(argc, argv, NULL, NULL);
+	struct sockaddr_in sock;
+	fd_set fd_set_all;
+	fd_set fd_set_read;
+	int nfds;
+	ssize_t bytes;
+	int fd;
+	char data;
+
+	FD_ZERO(&fd_set_all);
+	FD_SET(fd_server, &fd_set_all);
+	nfds = fd_server + 1;
 
-	setup();
-
-	for (lc = 0; TEST_LOOPING(lc); ++lc) {
-		tst_count = 0;
-		for (testno = 0; testno < TST_TOTAL; ++testno) {
-			tdat[testno].setup();
+	while (1) {
+		socklen_t len;
 
-			TEST(connect
-			     (s, tdat[testno].sockaddr, tdat[testno].salen));
+		memcpy(&fd_set_read, &fd_set_all, sizeof(fd_set_read));
+		if (select(nfds, &fd_set_read, NULL, NULL, NULL) < 0)
+			if (errno != EINTR)
+				exit(1);
+		if (FD_ISSET(fd_server, &fd_set_read)) {
+			int newfd;
 
-			if (TEST_RETURN != tdat[testno].retval ||
-			    (TEST_RETURN < 0 &&
-			     TEST_ERRNO != tdat[testno].experrno)) {
-				tst_resm(TFAIL, "%s ; returned"
-					 " %ld (expected %d), errno %d (expected"
-					 " %d)", tdat[testno].desc,
-					 TEST_RETURN, tdat[testno].retval,
-					 TEST_ERRNO, tdat[testno].experrno);
-			} else {
-				tst_resm(TPASS, "%s successful",
-					 tdat[testno].desc);
+			len = sizeof(sock);
+			newfd = SAFE_ACCEPT(fd_server, (struct sockaddr *)&sock,
+				&len);
+			if (newfd >= 0) {
+				FD_SET(newfd, &fd_set_all);
+				nfds = MAX(nfds, newfd + 1);
 			}
-			tdat[testno].cleanup();
 		}
+		for (fd = 0; fd < nfds; fd++)
+			if (fd != fd_server && FD_ISSET(fd, &fd_set_read)) {
+				bytes = read(fd, &data, 1);
+				if (bytes == 0) {
+					close(fd);
+					FD_CLR(fd, &fd_set_all);
+				}
+			}
 	}
-	cleanup();
-
-	tst_exit();
 }
 
-pid_t pid;
-
-void setup(void)
+static void start_server(struct sockaddr_in *sock)
 {
-	TEST_PAUSE;		/* if -p option specified */
-
-	pid = start_server(&sin1);
+	socklen_t slen = sizeof(*sock);
 
-	sin2.sin_family = AF_INET;
-	/* this port must be unused! */
-	sin2.sin_port = TST_GET_UNUSED_PORT(NULL, AF_INET, SOCK_STREAM);
-	sin2.sin_addr.s_addr = INADDR_ANY;
+	sock->sin_family = AF_INET;
+	sock->sin_port = 0;
+	sock->sin_addr.s_addr = INADDR_ANY;
 
-	sin3.sin_family = AF_INET;
-	sin3.sin_port = 0;
-	/* assumes no route to this network! */
-	sin3.sin_addr.s_addr = htonl(0x0AFFFEFD);
-
-	sin4.sin_family = 47;	/* bogus address family */
-	sin4.sin_port = 0;
-	sin4.sin_addr.s_addr = htonl(0x0AFFFEFD);
+	fd_server = SAFE_SOCKET(PF_INET, SOCK_STREAM, 0);
+	SAFE_BIND(fd_server, (struct sockaddr *)sock, slen);
+	SAFE_LISTEN(fd_server, 10);
+	SAFE_GETSOCKNAME(fd_server, (struct sockaddr *)sock, &slen);
 
+	pid = SAFE_FORK();
+	if (!pid) {
+		do_child();
+		exit(0);
+	}
+	close(fd_server);
 }
 
-void cleanup(void)
+static void setup(void)
 {
-	(void)kill(pid, SIGKILL);
+	sock2.sin_family = AF_INET;
+	sock2.sin_port = TST_GET_UNUSED_PORT(AF_INET, SOCK_STREAM);
+	sock2.sin_addr.s_addr = INADDR_ANY;
 
+	sock3.sin_family = 47;
+	sock3.sin_port = 0;
+	sock3.sin_addr.s_addr = htonl(0x0AFFFEFD);
 }
 
-void setup0(void)
+static void setup1(unsigned int i)
 {
-	if (tdat[testno].experrno == EBADF)
-		s = 400;	/* anything not an open file */
-	else if ((s = open("/dev/null", O_WRONLY)) == -1)
-		tst_brkm(TBROK | TERRNO, cleanup, "open(/dev/null) failed");
+	struct test_case_t *tc = &tcases[i];
 
+	if (tc->exp_errno == EBADF)
+		fd_share = -1;
+	else
+		fd_share = SAFE_OPEN("/dev/null", O_WRONLY);
 }
 
-void cleanup0(void)
+static void setup2(unsigned int i)
 {
-	close(s);
-	s = -1;
-}
+	struct test_case_t *tc = &tcases[i];
 
-void setup1(void)
-{
-	s = SAFE_SOCKET(cleanup, tdat[testno].domain, tdat[testno].type,
-		        tdat[testno].proto);
+	fd_share = SAFE_SOCKET(tc->domain, tc->type, tc->proto);
 }
 
-void cleanup1(void)
+static void setup3(unsigned int i)
 {
-	(void)close(s);
-	s = -1;
+	setup2(i);
+	SAFE_CONNECT(fd_server, (const struct sockaddr *)&sock1, sizeof(sock1));
 }
 
-void setup2(void)
+static void cleanup1(void)
 {
-	setup1();		/* get a socket in s */
-	SAFE_CONNECT(cleanup, s, (const struct sockaddr *)&sin1, sizeof(sin1));
+	if (fd_share > 0)
+		SAFE_CLOSE(fd_share);
 }
 
-pid_t start_server(struct sockaddr_in *sin0)
+static void verify_connect(unsigned int i)
 {
-	pid_t pid;
-	socklen_t slen = sizeof(*sin0);
-
-	sin0->sin_family = AF_INET;
-	sin0->sin_port = 0; /* pick random free port */
-	sin0->sin_addr.s_addr = INADDR_ANY;
-
-	sfd = socket(PF_INET, SOCK_STREAM, 0);
-	if (sfd < 0) {
-		tst_brkm(TBROK | TERRNO, cleanup, "server socket failed");
-		return -1;
-	}
-	if (bind(sfd, (struct sockaddr *)sin0, sizeof(*sin0)) < 0) {
-		tst_brkm(TBROK | TERRNO, cleanup, "server bind failed");
-		return -1;
-	}
-	if (listen(sfd, 10) < 0) {
-		tst_brkm(TBROK | TERRNO, cleanup, "server listen failed");
-		return -1;
-	}
-	SAFE_GETSOCKNAME(cleanup, sfd, (struct sockaddr *)sin0, &slen);
-
-	switch ((pid = tst_fork())) {
-	case 0:		/* child */
-		do_child();
-		break;
-	case -1:
-		tst_brkm(TBROK | TERRNO, cleanup, "server fork failed");
-		/* fall through */
-	default:		/* parent */
-		(void)close(sfd);
-		return pid;
-	}
-
-	return -1;
+	struct test_case_t *tc = &tcases[i];
+
+	start_server(&sock1);
+	if (tc->setup)
+		tc->setup(i);
+	TST_EXP_FAIL(sys_connect(fd_share, tc->sockaddr, tc->salen),
+		tc->exp_errno, "%s", tc->desc);
+	SAFE_KILL(pid, SIGKILL);
+	SAFE_WAITPID(pid, NULL, 0);
+	if (tc->cleanup)
+		tc->cleanup();
 }
 
-void do_child(void)
-{
-	struct sockaddr_in fsin;
-	fd_set afds, rfds;
-	int nfds, cc, fd;
-	char c;
-
-	FD_ZERO(&afds);
-	FD_SET(sfd, &afds);
-
-	nfds = sfd + 1;
-
-	/* accept connections until killed */
-	while (1) {
-		socklen_t fromlen;
-
-		memcpy(&rfds, &afds, sizeof(rfds));
-
-		if (select(nfds, &rfds, NULL, NULL,
-			   NULL) < 0)
-			if (errno != EINTR)
-				exit(1);
-		if (FD_ISSET(sfd, &rfds)) {
-			int newfd;
-
-			fromlen = sizeof(fsin);
-			newfd = accept(sfd, (struct sockaddr *)&fsin, &fromlen);
-			if (newfd >= 0) {
-				FD_SET(newfd, &afds);
-				nfds = MAX(nfds, newfd + 1);
-			}
-		}
-		for (fd = 0; fd < nfds; ++fd)
-			if (fd != sfd && FD_ISSET(fd, &rfds)) {
-				if ((cc = read(fd, &c, 1)) == 0) {
-					(void)close(fd);
-					FD_CLR(fd, &afds);
-				}
-			}
-	}
-}
+static struct tst_test test = {
+	.setup = setup,
+	.tcnt = ARRAY_SIZE(tcases),
+	.test = verify_connect,
+	.forks_child = 1,
+};