. make common.o link with the tests instead of being #included as common.c . fix warnings about missing prototypes by declaring functions static . reduces some duplicated code Change-Id: Ic2a765d7f5886add5863190efec3fdd2d2ea2137
		
			
				
	
	
		
			411 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			411 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <arpa/inet.h>
 | 
						|
#include <netinet/in.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <netdb.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
int max_error = 5;
 | 
						|
#include "common.h"
 | 
						|
 | 
						|
#define CLOEXEC_PORT 3490
 | 
						|
#define FORK_PORT 3491
 | 
						|
 | 
						|
static int fd = 0;
 | 
						|
 | 
						|
void copy_subtests(void);
 | 
						|
void test_open_file_cloexec(void);
 | 
						|
void test_open_file_fork(void);
 | 
						|
void test_open_socket_cloexec(void);
 | 
						|
void test_open_socket_fork(void);
 | 
						|
void start_socket_server(int port);
 | 
						|
int start_socket_client(int port, int flag);
 | 
						|
 | 
						|
void
 | 
						|
copy_subtests()
 | 
						|
{
 | 
						|
	char *subtests[] = { "t67a", "t67b" };
 | 
						|
	char copy_cmd[8 + PATH_MAX + 1];
 | 
						|
	int i, no_tests;
 | 
						|
 | 
						|
	no_tests = sizeof(subtests) / sizeof(char *);
 | 
						|
 | 
						|
	for (i = 0; i < no_tests; i++) {
 | 
						|
		snprintf(copy_cmd, 8 + PATH_MAX, "cp ../%s .", subtests[i]);
 | 
						|
		system(copy_cmd);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
test_open_file_cloexec()
 | 
						|
{
 | 
						|
	int flags;
 | 
						|
	pid_t pid;
 | 
						|
 | 
						|
	/* Let's create a file with O_CLOEXEC turned on */
 | 
						|
	fd = open("file", O_RDWR|O_CREAT|O_CLOEXEC, 0660);
 | 
						|
	if (fd < 0) e(1);
 | 
						|
 | 
						|
	/* Now verify through fcntl the flag is indeed set */
 | 
						|
	flags = fcntl(fd, F_GETFD);
 | 
						|
	if (flags < 0) e(2);
 | 
						|
	if (!(flags & FD_CLOEXEC)) e(3);
 | 
						|
 | 
						|
	/* Fork a child and let child exec a test program that verifies
 | 
						|
	 * fd is not a valid file */
 | 
						|
	pid = fork();
 | 
						|
	if (pid == -1) e(4);	
 | 
						|
	else if (pid == 0) {
 | 
						|
		/* We're the child */
 | 
						|
		char fd_buf[2];
 | 
						|
 | 
						|
		/* Verify again O_CLOEXEC is on */
 | 
						|
		flags = fcntl(fd, F_GETFD);
 | 
						|
		if (flags < 0) e(5);
 | 
						|
		if (!(flags & FD_CLOEXEC)) e(6);
 | 
						|
 | 
						|
		snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
 | 
						|
		execl("./t67b", "t67b", fd_buf, NULL);
 | 
						|
 | 
						|
		/* Should not reach this */
 | 
						|
		exit(1);
 | 
						|
	} else {
 | 
						|
		/* We're the parent */
 | 
						|
		int result;
 | 
						|
 | 
						|
		if (waitpid(pid, &result, 0) == -1) e(7);
 | 
						|
		if (WEXITSTATUS(result) != 0) e(8);
 | 
						|
	}
 | 
						|
	close(fd);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
test_open_file_fork()
 | 
						|
{
 | 
						|
	int flags;
 | 
						|
	pid_t pid;
 | 
						|
 | 
						|
	/* Let's create a file with O_CLOEXEC NOT turned on */
 | 
						|
	fd = open("file", O_RDWR|O_CREAT, 0660);
 | 
						|
	if (fd < 0) e(1);
 | 
						|
 | 
						|
	/* Now verify through fcntl the flag is indeed not set */
 | 
						|
	flags = fcntl(fd, F_GETFD);
 | 
						|
	if (flags < 0) e(2);
 | 
						|
	if (flags & FD_CLOEXEC) e(3);
 | 
						|
 | 
						|
	/* Fork a child and let child exec a test program that verifies
 | 
						|
	 * fd is a valid file */
 | 
						|
	pid = fork();
 | 
						|
	if (pid == -1) e(4);	
 | 
						|
	else if (pid == 0) {
 | 
						|
		/* We're the child */
 | 
						|
		char fd_buf[2];
 | 
						|
 | 
						|
		/* Verify again O_CLOEXEC is off */
 | 
						|
		flags = fcntl(fd, F_GETFD);
 | 
						|
		if (flags < 0) e(5);
 | 
						|
		if (flags & FD_CLOEXEC) e(6);
 | 
						|
 | 
						|
		snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
 | 
						|
		execl("./t67a", "t67a", fd_buf, NULL);
 | 
						|
 | 
						|
		/* Should not reach this */
 | 
						|
		exit(1);
 | 
						|
	} else {
 | 
						|
		/* We're the parent */
 | 
						|
		int result = 0;
 | 
						|
 | 
						|
		if (waitpid(pid, &result, 0) == -1) e(7);
 | 
						|
		if (WEXITSTATUS(result) != 0) e(8);
 | 
						|
	}
 | 
						|
	close(fd);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
start_socket_client(int port, int flag)
 | 
						|
{
 | 
						|
	int fd_sock;
 | 
						|
	struct hostent *he;
 | 
						|
	struct sockaddr_in server;
 | 
						|
 | 
						|
	if ((fd_sock = socket(PF_INET, SOCK_STREAM|flag, 0)) < 0) {
 | 
						|
		perror("Error obtaining socket\n");
 | 
						|
		e(1);
 | 
						|
	}
 | 
						|
 | 
						|
	if ((he = gethostbyname("127.0.0.1")) == NULL) {
 | 
						|
		perror("Error retrieving home\n");
 | 
						|
		e(2);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Copy server host result */
 | 
						|
	memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
 | 
						|
	server.sin_family = AF_INET;
 | 
						|
	server.sin_port = htons(port);
 | 
						|
 | 
						|
	/* Normally, we'd zerofill sin_zero, but there is no such thing on
 | 
						|
	 * Minix at the moment */
 | 
						|
#if !defined(__minix)
 | 
						|
	memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
 | 
						|
#endif
 | 
						|
	
 | 
						|
	if (connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0){
 | 
						|
		perror("Error connecting to server\n");
 | 
						|
		e(3);
 | 
						|
	}
 | 
						|
 | 
						|
	return fd_sock;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
start_socket_server(int port)
 | 
						|
{
 | 
						|
#if !defined(__minix)
 | 
						|
	int yes = 1;
 | 
						|
#endif
 | 
						|
	int fd_sock, fd_new, r;
 | 
						|
	struct sockaddr_in my_addr;
 | 
						|
	struct sockaddr_in other_addr;
 | 
						|
	socklen_t other_size;
 | 
						|
	char buf[1];
 | 
						|
 | 
						|
	if ((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
 | 
						|
		perror("Error getting socket\n");
 | 
						|
		e(1);
 | 
						|
	}
 | 
						|
 | 
						|
	my_addr.sin_family = AF_INET;
 | 
						|
	my_addr.sin_port = htons(port);
 | 
						|
	my_addr.sin_addr.s_addr = INADDR_ANY;
 | 
						|
	/* Normally we'd zerofill sin_zero, but current Minix socket interface
 | 
						|
	 * does not support that field */
 | 
						|
#if !defined(__minix)
 | 
						|
	memset(&my_addr.sin_zero, '\0', sizeof(sin.sin_zero));
 | 
						|
#endif
 | 
						|
	
 | 
						|
	/* Reuse port number when invoking test often */
 | 
						|
#if !defined(__minix)
 | 
						|
	if (setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes,
 | 
						|
	    sizeof(int)) < 0) {
 | 
						|
		perror("Error setting port reuse option\n");
 | 
						|
		e(2);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* Bind to port */
 | 
						|
	if (bind(fd_sock, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
 | 
						|
		perror("Error binding to port\n");
 | 
						|
		e(3);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Set socket in listening mode */
 | 
						|
	if (listen(fd_sock, 20) < 0) {
 | 
						|
		perror("Error listening for incoming connections");
 | 
						|
		e(4);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Accept incoming connections */
 | 
						|
	fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
 | 
						|
 | 
						|
	if (fd_new < 0) {
 | 
						|
		perror("Error accepting new connections\n");
 | 
						|
		e(5);
 | 
						|
	}
 | 
						|
 | 
						|
	r = read(fd_new, buf, sizeof(buf));
 | 
						|
	exit(0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
test_open_socket_cloexec()
 | 
						|
{
 | 
						|
/* This subtest will start a server and client using TCP. The client will
 | 
						|
 * open the socket with SOCK_CLOEXEC turned on, so that after a fork+exec, the
 | 
						|
 * socket should become invalid.
 | 
						|
 *                o
 | 
						|
 *              / | 
 | 
						|
 *        server  |
 | 
						|
 *      (accept)  |
 | 
						|
 *             |  | \
 | 
						|
 *             |  |  client
 | 
						|
 *             |  |  (connect)
 | 
						|
 *             |  |  | \
 | 
						|
 *             |  |  |  client_fork
 | 
						|
 *             |  |  |  (exec t67b)
 | 
						|
 *        (read)  |  |  (write)
 | 
						|
 *             |  |  | /
 | 
						|
 *             |  |  (waitpid client_fork) 
 | 
						|
 *              \ |  |
 | 
						|
 * (waitpid server)  |
 | 
						|
 *                | /
 | 
						|
 * (waitpid client)
 | 
						|
 *                |
 | 
						|
 *                o
 | 
						|
 */
 | 
						|
	pid_t pid_server, pid_client;
 | 
						|
	int result;
 | 
						|
 | 
						|
	pid_server = fork();
 | 
						|
	if (pid_server < 0) e(1);
 | 
						|
	if (pid_server == 0) {
 | 
						|
		start_socket_server(CLOEXEC_PORT);
 | 
						|
		return; /* Never reached */
 | 
						|
	}
 | 
						|
 | 
						|
	pid_client = fork();
 | 
						|
	if (pid_client < 0) e(2);
 | 
						|
	if (pid_client == 0) {
 | 
						|
		pid_t pid_client_fork;
 | 
						|
		int sockfd;
 | 
						|
 | 
						|
		sockfd = start_socket_client(CLOEXEC_PORT, SOCK_CLOEXEC);
 | 
						|
		if (sockfd < 0) e(4);
 | 
						|
 | 
						|
		pid_client_fork = fork();
 | 
						|
		if (pid_client_fork < 0) {
 | 
						|
			e(5);
 | 
						|
			exit(5);
 | 
						|
		}
 | 
						|
		if (pid_client_fork == 0) {
 | 
						|
			/* We're a fork of the client. After we exec, the
 | 
						|
			 * socket should become invalid due to the SOCK_CLOEXEC
 | 
						|
			 * flag.
 | 
						|
			 */
 | 
						|
			char sockfd_buf[2];
 | 
						|
			int flags;
 | 
						|
 | 
						|
			/* Verify O_CLOEXEC is on */
 | 
						|
			flags = fcntl(sockfd, F_GETFD);
 | 
						|
			if (flags < 0) e(5);
 | 
						|
			if (!(flags & FD_CLOEXEC)) e(6);
 | 
						|
 | 
						|
			/* t67b will verify that it can't write to sockfd and
 | 
						|
			 * that opening a new file will yield a file descriptor
 | 
						|
			 * with the same number.
 | 
						|
			 */
 | 
						|
			snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
 | 
						|
			execl("./t67b", "t67b", sockfd_buf, NULL);
 | 
						|
 | 
						|
			/* Should not reach this */
 | 
						|
			exit(1);
 | 
						|
		} else {
 | 
						|
			if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
 | 
						|
			exit(WEXITSTATUS(result)); /* Pass on error to main */
 | 
						|
		}
 | 
						|
		exit(0);	/* Never reached */
 | 
						|
	}
 | 
						|
 | 
						|
	if (waitpid(pid_server, &result, 0) < 0) e(3);
 | 
						|
	if (waitpid(pid_client, &result, 0) < 0) e(4);
 | 
						|
 | 
						|
	/* Let's inspect client result */
 | 
						|
	if (WEXITSTATUS(result) != 0) e(5);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
test_open_socket_fork(void)
 | 
						|
{
 | 
						|
/* This subtest will start a server and client using TCP. The client will
 | 
						|
 * open the socket with SOCK_CLOEXEC turned off, so that after a fork+exec, the
 | 
						|
 * socket should stay valid.
 | 
						|
 *                o
 | 
						|
 *              / | 
 | 
						|
 *        server  |
 | 
						|
 *      (accept)  |
 | 
						|
 *             |  | \
 | 
						|
 *             |  |  client
 | 
						|
 *             |  |  (connect)
 | 
						|
 *             |  |  | \
 | 
						|
 *             |  |  |  client_fork
 | 
						|
 *             |  |  |  (exec t67a)
 | 
						|
 *        (read)  |  |  (write)
 | 
						|
 *             |  |  | /
 | 
						|
 *             |  |  (waitpid client_fork) 
 | 
						|
 *              \ |  |
 | 
						|
 * (waitpid server)  |
 | 
						|
 *                | /
 | 
						|
 * (waitpid client)
 | 
						|
 *                |
 | 
						|
 *                o
 | 
						|
 */
 | 
						|
	pid_t pid_server, pid_client;
 | 
						|
	int result;
 | 
						|
 | 
						|
	pid_server = fork();
 | 
						|
	if (pid_server < 0) e(1);
 | 
						|
	if (pid_server == 0) {
 | 
						|
		start_socket_server(FORK_PORT);
 | 
						|
		return; /* Never reached */
 | 
						|
	}
 | 
						|
 | 
						|
	pid_client = fork();
 | 
						|
	if (pid_client < 0) e(2);
 | 
						|
	if (pid_client == 0) {
 | 
						|
		pid_t pid_client_fork;
 | 
						|
		int sockfd;
 | 
						|
 | 
						|
		sockfd = start_socket_client(FORK_PORT, 0);
 | 
						|
		if (sockfd < 0) e(4);
 | 
						|
 | 
						|
		pid_client_fork = fork();
 | 
						|
		if (pid_client_fork < 0) {
 | 
						|
			e(5);
 | 
						|
			exit(5);
 | 
						|
		}
 | 
						|
		if (pid_client_fork == 0) {
 | 
						|
			/* We're a fork of the client. After we exec, the
 | 
						|
			 * socket should stay valid due to lack of SOCK_CLOEXEC
 | 
						|
			 * flag.
 | 
						|
			 */
 | 
						|
			char sockfd_buf[2];
 | 
						|
			int flags;
 | 
						|
 | 
						|
			/* Verify O_CLOEXEC is off */
 | 
						|
			flags = fcntl(sockfd, F_GETFD);
 | 
						|
			if (flags < 0) e(5);
 | 
						|
			if (flags & FD_CLOEXEC) e(6);
 | 
						|
 | 
						|
			/* t67a will verify that it can't write to sockfd and
 | 
						|
			 * that opening a new file will yield a file descriptor
 | 
						|
			 * with a higher number.
 | 
						|
			 */
 | 
						|
			snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
 | 
						|
			execl("./t67a", "t67a", sockfd_buf, NULL);
 | 
						|
 | 
						|
			/* Should not reach this */
 | 
						|
			exit(1);
 | 
						|
		} else {
 | 
						|
			if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
 | 
						|
			exit(WEXITSTATUS(result)); /* Pass on error to main */
 | 
						|
		}
 | 
						|
		exit(0);	/* Never reached */
 | 
						|
	}
 | 
						|
 | 
						|
	if (waitpid(pid_server, &result, 0) < 0) e(3);
 | 
						|
	if (waitpid(pid_client, &result, 0) < 0) e(4);
 | 
						|
 | 
						|
	/* Let's inspect client result */
 | 
						|
	if (WEXITSTATUS(result) != 0) e(5);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	start(67);
 | 
						|
	copy_subtests();
 | 
						|
	test_open_file_fork();
 | 
						|
	test_open_file_cloexec();
 | 
						|
	test_open_socket_fork();
 | 
						|
	test_open_socket_cloexec();
 | 
						|
	quit();
 | 
						|
	return(-1);	/* Unreachable */
 | 
						|
}
 | 
						|
 |