682 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			682 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * vsock_diag_test - vsock_diag.ko test suite
 | |
|  *
 | |
|  * Copyright (C) 2017 Red Hat, Inc.
 | |
|  *
 | |
|  * Author: Stefan Hajnoczi <stefanha@redhat.com>
 | |
|  *
 | |
|  * 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; version 2
 | |
|  * of the License.
 | |
|  */
 | |
| 
 | |
| #include <getopt.h>
 | |
| #include <stdio.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <unistd.h>
 | |
| #include <signal.h>
 | |
| #include <sys/socket.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/types.h>
 | |
| #include <linux/list.h>
 | |
| #include <linux/net.h>
 | |
| #include <linux/netlink.h>
 | |
| #include <linux/sock_diag.h>
 | |
| #include <netinet/tcp.h>
 | |
| 
 | |
| #include "../../../include/uapi/linux/vm_sockets.h"
 | |
| #include "../../../include/uapi/linux/vm_sockets_diag.h"
 | |
| 
 | |
| #include "timeout.h"
 | |
| #include "control.h"
 | |
| 
 | |
| enum test_mode {
 | |
| 	TEST_MODE_UNSET,
 | |
| 	TEST_MODE_CLIENT,
 | |
| 	TEST_MODE_SERVER
 | |
| };
 | |
| 
 | |
| /* Per-socket status */
 | |
| struct vsock_stat {
 | |
| 	struct list_head list;
 | |
| 	struct vsock_diag_msg msg;
 | |
| };
 | |
| 
 | |
| static const char *sock_type_str(int type)
 | |
| {
 | |
| 	switch (type) {
 | |
| 	case SOCK_DGRAM:
 | |
| 		return "DGRAM";
 | |
| 	case SOCK_STREAM:
 | |
| 		return "STREAM";
 | |
| 	default:
 | |
| 		return "INVALID TYPE";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static const char *sock_state_str(int state)
 | |
| {
 | |
| 	switch (state) {
 | |
| 	case TCP_CLOSE:
 | |
| 		return "UNCONNECTED";
 | |
| 	case TCP_SYN_SENT:
 | |
| 		return "CONNECTING";
 | |
| 	case TCP_ESTABLISHED:
 | |
| 		return "CONNECTED";
 | |
| 	case TCP_CLOSING:
 | |
| 		return "DISCONNECTING";
 | |
| 	case TCP_LISTEN:
 | |
| 		return "LISTEN";
 | |
| 	default:
 | |
| 		return "INVALID STATE";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static const char *sock_shutdown_str(int shutdown)
 | |
| {
 | |
| 	switch (shutdown) {
 | |
| 	case 1:
 | |
| 		return "RCV_SHUTDOWN";
 | |
| 	case 2:
 | |
| 		return "SEND_SHUTDOWN";
 | |
| 	case 3:
 | |
| 		return "RCV_SHUTDOWN | SEND_SHUTDOWN";
 | |
| 	default:
 | |
| 		return "0";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void print_vsock_addr(FILE *fp, unsigned int cid, unsigned int port)
 | |
| {
 | |
| 	if (cid == VMADDR_CID_ANY)
 | |
| 		fprintf(fp, "*:");
 | |
| 	else
 | |
| 		fprintf(fp, "%u:", cid);
 | |
| 
 | |
| 	if (port == VMADDR_PORT_ANY)
 | |
| 		fprintf(fp, "*");
 | |
| 	else
 | |
| 		fprintf(fp, "%u", port);
 | |
| }
 | |
| 
 | |
| static void print_vsock_stat(FILE *fp, struct vsock_stat *st)
 | |
| {
 | |
| 	print_vsock_addr(fp, st->msg.vdiag_src_cid, st->msg.vdiag_src_port);
 | |
| 	fprintf(fp, " ");
 | |
| 	print_vsock_addr(fp, st->msg.vdiag_dst_cid, st->msg.vdiag_dst_port);
 | |
| 	fprintf(fp, " %s %s %s %u\n",
 | |
| 		sock_type_str(st->msg.vdiag_type),
 | |
| 		sock_state_str(st->msg.vdiag_state),
 | |
| 		sock_shutdown_str(st->msg.vdiag_shutdown),
 | |
| 		st->msg.vdiag_ino);
 | |
| }
 | |
| 
 | |
| static void print_vsock_stats(FILE *fp, struct list_head *head)
 | |
| {
 | |
| 	struct vsock_stat *st;
 | |
| 
 | |
| 	list_for_each_entry(st, head, list)
 | |
| 		print_vsock_stat(fp, st);
 | |
| }
 | |
| 
 | |
| static struct vsock_stat *find_vsock_stat(struct list_head *head, int fd)
 | |
| {
 | |
| 	struct vsock_stat *st;
 | |
| 	struct stat stat;
 | |
| 
 | |
| 	if (fstat(fd, &stat) < 0) {
 | |
| 		perror("fstat");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	list_for_each_entry(st, head, list)
 | |
| 		if (st->msg.vdiag_ino == stat.st_ino)
 | |
| 			return st;
 | |
| 
 | |
| 	fprintf(stderr, "cannot find fd %d\n", fd);
 | |
| 	exit(EXIT_FAILURE);
 | |
| }
 | |
| 
 | |
| static void check_no_sockets(struct list_head *head)
 | |
| {
 | |
| 	if (!list_empty(head)) {
 | |
| 		fprintf(stderr, "expected no sockets\n");
 | |
| 		print_vsock_stats(stderr, head);
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void check_num_sockets(struct list_head *head, int expected)
 | |
| {
 | |
| 	struct list_head *node;
 | |
| 	int n = 0;
 | |
| 
 | |
| 	list_for_each(node, head)
 | |
| 		n++;
 | |
| 
 | |
| 	if (n != expected) {
 | |
| 		fprintf(stderr, "expected %d sockets, found %d\n",
 | |
| 			expected, n);
 | |
| 		print_vsock_stats(stderr, head);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void check_socket_state(struct vsock_stat *st, __u8 state)
 | |
| {
 | |
| 	if (st->msg.vdiag_state != state) {
 | |
| 		fprintf(stderr, "expected socket state %#x, got %#x\n",
 | |
| 			state, st->msg.vdiag_state);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void send_req(int fd)
 | |
| {
 | |
| 	struct sockaddr_nl nladdr = {
 | |
| 		.nl_family = AF_NETLINK,
 | |
| 	};
 | |
| 	struct {
 | |
| 		struct nlmsghdr nlh;
 | |
| 		struct vsock_diag_req vreq;
 | |
| 	} req = {
 | |
| 		.nlh = {
 | |
| 			.nlmsg_len = sizeof(req),
 | |
| 			.nlmsg_type = SOCK_DIAG_BY_FAMILY,
 | |
| 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
 | |
| 		},
 | |
| 		.vreq = {
 | |
| 			.sdiag_family = AF_VSOCK,
 | |
| 			.vdiag_states = ~(__u32)0,
 | |
| 		},
 | |
| 	};
 | |
| 	struct iovec iov = {
 | |
| 		.iov_base = &req,
 | |
| 		.iov_len = sizeof(req),
 | |
| 	};
 | |
| 	struct msghdr msg = {
 | |
| 		.msg_name = &nladdr,
 | |
| 		.msg_namelen = sizeof(nladdr),
 | |
| 		.msg_iov = &iov,
 | |
| 		.msg_iovlen = 1,
 | |
| 	};
 | |
| 
 | |
| 	for (;;) {
 | |
| 		if (sendmsg(fd, &msg, 0) < 0) {
 | |
| 			if (errno == EINTR)
 | |
| 				continue;
 | |
| 
 | |
| 			perror("sendmsg");
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static ssize_t recv_resp(int fd, void *buf, size_t len)
 | |
| {
 | |
| 	struct sockaddr_nl nladdr = {
 | |
| 		.nl_family = AF_NETLINK,
 | |
| 	};
 | |
| 	struct iovec iov = {
 | |
| 		.iov_base = buf,
 | |
| 		.iov_len = len,
 | |
| 	};
 | |
| 	struct msghdr msg = {
 | |
| 		.msg_name = &nladdr,
 | |
| 		.msg_namelen = sizeof(nladdr),
 | |
| 		.msg_iov = &iov,
 | |
| 		.msg_iovlen = 1,
 | |
| 	};
 | |
| 	ssize_t ret;
 | |
| 
 | |
| 	do {
 | |
| 		ret = recvmsg(fd, &msg, 0);
 | |
| 	} while (ret < 0 && errno == EINTR);
 | |
| 
 | |
| 	if (ret < 0) {
 | |
| 		perror("recvmsg");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void add_vsock_stat(struct list_head *sockets,
 | |
| 			   const struct vsock_diag_msg *resp)
 | |
| {
 | |
| 	struct vsock_stat *st;
 | |
| 
 | |
| 	st = malloc(sizeof(*st));
 | |
| 	if (!st) {
 | |
| 		perror("malloc");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	st->msg = *resp;
 | |
| 	list_add_tail(&st->list, sockets);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read vsock stats into a list.
 | |
|  */
 | |
| static void read_vsock_stat(struct list_head *sockets)
 | |
| {
 | |
| 	long buf[8192 / sizeof(long)];
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
 | |
| 	if (fd < 0) {
 | |
| 		perror("socket");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	send_req(fd);
 | |
| 
 | |
| 	for (;;) {
 | |
| 		const struct nlmsghdr *h;
 | |
| 		ssize_t ret;
 | |
| 
 | |
| 		ret = recv_resp(fd, buf, sizeof(buf));
 | |
| 		if (ret == 0)
 | |
| 			goto done;
 | |
| 		if (ret < sizeof(*h)) {
 | |
| 			fprintf(stderr, "short read of %zd bytes\n", ret);
 | |
| 			exit(EXIT_FAILURE);
 | |
| 		}
 | |
| 
 | |
| 		h = (struct nlmsghdr *)buf;
 | |
| 
 | |
| 		while (NLMSG_OK(h, ret)) {
 | |
| 			if (h->nlmsg_type == NLMSG_DONE)
 | |
| 				goto done;
 | |
| 
 | |
| 			if (h->nlmsg_type == NLMSG_ERROR) {
 | |
| 				const struct nlmsgerr *err = NLMSG_DATA(h);
 | |
| 
 | |
| 				if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
 | |
| 					fprintf(stderr, "NLMSG_ERROR\n");
 | |
| 				else {
 | |
| 					errno = -err->error;
 | |
| 					perror("NLMSG_ERROR");
 | |
| 				}
 | |
| 
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 
 | |
| 			if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
 | |
| 				fprintf(stderr, "unexpected nlmsg_type %#x\n",
 | |
| 					h->nlmsg_type);
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 			if (h->nlmsg_len <
 | |
| 			    NLMSG_LENGTH(sizeof(struct vsock_diag_msg))) {
 | |
| 				fprintf(stderr, "short vsock_diag_msg\n");
 | |
| 				exit(EXIT_FAILURE);
 | |
| 			}
 | |
| 
 | |
| 			add_vsock_stat(sockets, NLMSG_DATA(h));
 | |
| 
 | |
| 			h = NLMSG_NEXT(h, ret);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	close(fd);
 | |
| }
 | |
| 
 | |
| static void free_sock_stat(struct list_head *sockets)
 | |
| {
 | |
| 	struct vsock_stat *st;
 | |
| 	struct vsock_stat *next;
 | |
| 
 | |
| 	list_for_each_entry_safe(st, next, sockets, list)
 | |
| 		free(st);
 | |
| }
 | |
| 
 | |
| static void test_no_sockets(unsigned int peer_cid)
 | |
| {
 | |
| 	LIST_HEAD(sockets);
 | |
| 
 | |
| 	read_vsock_stat(&sockets);
 | |
| 
 | |
| 	check_no_sockets(&sockets);
 | |
| 
 | |
| 	free_sock_stat(&sockets);
 | |
| }
 | |
| 
 | |
| static void test_listen_socket_server(unsigned int peer_cid)
 | |
| {
 | |
| 	union {
 | |
| 		struct sockaddr sa;
 | |
| 		struct sockaddr_vm svm;
 | |
| 	} addr = {
 | |
| 		.svm = {
 | |
| 			.svm_family = AF_VSOCK,
 | |
| 			.svm_port = 1234,
 | |
| 			.svm_cid = VMADDR_CID_ANY,
 | |
| 		},
 | |
| 	};
 | |
| 	LIST_HEAD(sockets);
 | |
| 	struct vsock_stat *st;
 | |
| 	int fd;
 | |
| 
 | |
| 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
 | |
| 
 | |
| 	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
 | |
| 		perror("bind");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (listen(fd, 1) < 0) {
 | |
| 		perror("listen");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	read_vsock_stat(&sockets);
 | |
| 
 | |
| 	check_num_sockets(&sockets, 1);
 | |
| 	st = find_vsock_stat(&sockets, fd);
 | |
| 	check_socket_state(st, TCP_LISTEN);
 | |
| 
 | |
| 	close(fd);
 | |
| 	free_sock_stat(&sockets);
 | |
| }
 | |
| 
 | |
| static void test_connect_client(unsigned int peer_cid)
 | |
| {
 | |
| 	union {
 | |
| 		struct sockaddr sa;
 | |
| 		struct sockaddr_vm svm;
 | |
| 	} addr = {
 | |
| 		.svm = {
 | |
| 			.svm_family = AF_VSOCK,
 | |
| 			.svm_port = 1234,
 | |
| 			.svm_cid = peer_cid,
 | |
| 		},
 | |
| 	};
 | |
| 	int fd;
 | |
| 	int ret;
 | |
| 	LIST_HEAD(sockets);
 | |
| 	struct vsock_stat *st;
 | |
| 
 | |
| 	control_expectln("LISTENING");
 | |
| 
 | |
| 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
 | |
| 
 | |
| 	timeout_begin(TIMEOUT);
 | |
| 	do {
 | |
| 		ret = connect(fd, &addr.sa, sizeof(addr.svm));
 | |
| 		timeout_check("connect");
 | |
| 	} while (ret < 0 && errno == EINTR);
 | |
| 	timeout_end();
 | |
| 
 | |
| 	if (ret < 0) {
 | |
| 		perror("connect");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	read_vsock_stat(&sockets);
 | |
| 
 | |
| 	check_num_sockets(&sockets, 1);
 | |
| 	st = find_vsock_stat(&sockets, fd);
 | |
| 	check_socket_state(st, TCP_ESTABLISHED);
 | |
| 
 | |
| 	control_expectln("DONE");
 | |
| 	control_writeln("DONE");
 | |
| 
 | |
| 	close(fd);
 | |
| 	free_sock_stat(&sockets);
 | |
| }
 | |
| 
 | |
| static void test_connect_server(unsigned int peer_cid)
 | |
| {
 | |
| 	union {
 | |
| 		struct sockaddr sa;
 | |
| 		struct sockaddr_vm svm;
 | |
| 	} addr = {
 | |
| 		.svm = {
 | |
| 			.svm_family = AF_VSOCK,
 | |
| 			.svm_port = 1234,
 | |
| 			.svm_cid = VMADDR_CID_ANY,
 | |
| 		},
 | |
| 	};
 | |
| 	union {
 | |
| 		struct sockaddr sa;
 | |
| 		struct sockaddr_vm svm;
 | |
| 	} clientaddr;
 | |
| 	socklen_t clientaddr_len = sizeof(clientaddr.svm);
 | |
| 	LIST_HEAD(sockets);
 | |
| 	struct vsock_stat *st;
 | |
| 	int fd;
 | |
| 	int client_fd;
 | |
| 
 | |
| 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
 | |
| 
 | |
| 	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
 | |
| 		perror("bind");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	if (listen(fd, 1) < 0) {
 | |
| 		perror("listen");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	control_writeln("LISTENING");
 | |
| 
 | |
| 	timeout_begin(TIMEOUT);
 | |
| 	do {
 | |
| 		client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
 | |
| 		timeout_check("accept");
 | |
| 	} while (client_fd < 0 && errno == EINTR);
 | |
| 	timeout_end();
 | |
| 
 | |
| 	if (client_fd < 0) {
 | |
| 		perror("accept");
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	if (clientaddr.sa.sa_family != AF_VSOCK) {
 | |
| 		fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",
 | |
| 			clientaddr.sa.sa_family);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	if (clientaddr.svm.svm_cid != peer_cid) {
 | |
| 		fprintf(stderr, "expected peer CID %u from accept(2), got %u\n",
 | |
| 			peer_cid, clientaddr.svm.svm_cid);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 
 | |
| 	read_vsock_stat(&sockets);
 | |
| 
 | |
| 	check_num_sockets(&sockets, 2);
 | |
| 	find_vsock_stat(&sockets, fd);
 | |
| 	st = find_vsock_stat(&sockets, client_fd);
 | |
| 	check_socket_state(st, TCP_ESTABLISHED);
 | |
| 
 | |
| 	control_writeln("DONE");
 | |
| 	control_expectln("DONE");
 | |
| 
 | |
| 	close(client_fd);
 | |
| 	close(fd);
 | |
| 	free_sock_stat(&sockets);
 | |
| }
 | |
| 
 | |
| static struct {
 | |
| 	const char *name;
 | |
| 	void (*run_client)(unsigned int peer_cid);
 | |
| 	void (*run_server)(unsigned int peer_cid);
 | |
| } test_cases[] = {
 | |
| 	{
 | |
| 		.name = "No sockets",
 | |
| 		.run_server = test_no_sockets,
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "Listen socket",
 | |
| 		.run_server = test_listen_socket_server,
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "Connect",
 | |
| 		.run_client = test_connect_client,
 | |
| 		.run_server = test_connect_server,
 | |
| 	},
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static void init_signals(void)
 | |
| {
 | |
| 	struct sigaction act = {
 | |
| 		.sa_handler = sigalrm,
 | |
| 	};
 | |
| 
 | |
| 	sigaction(SIGALRM, &act, NULL);
 | |
| 	signal(SIGPIPE, SIG_IGN);
 | |
| }
 | |
| 
 | |
| static unsigned int parse_cid(const char *str)
 | |
| {
 | |
| 	char *endptr = NULL;
 | |
| 	unsigned long int n;
 | |
| 
 | |
| 	errno = 0;
 | |
| 	n = strtoul(str, &endptr, 10);
 | |
| 	if (errno || *endptr != '\0') {
 | |
| 		fprintf(stderr, "malformed CID \"%s\"\n", str);
 | |
| 		exit(EXIT_FAILURE);
 | |
| 	}
 | |
| 	return n;
 | |
| }
 | |
| 
 | |
| static const char optstring[] = "";
 | |
| static const struct option longopts[] = {
 | |
| 	{
 | |
| 		.name = "control-host",
 | |
| 		.has_arg = required_argument,
 | |
| 		.val = 'H',
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "control-port",
 | |
| 		.has_arg = required_argument,
 | |
| 		.val = 'P',
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "mode",
 | |
| 		.has_arg = required_argument,
 | |
| 		.val = 'm',
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "peer-cid",
 | |
| 		.has_arg = required_argument,
 | |
| 		.val = 'p',
 | |
| 	},
 | |
| 	{
 | |
| 		.name = "help",
 | |
| 		.has_arg = no_argument,
 | |
| 		.val = '?',
 | |
| 	},
 | |
| 	{},
 | |
| };
 | |
| 
 | |
| static void usage(void)
 | |
| {
 | |
| 	fprintf(stderr, "Usage: vsock_diag_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid>\n"
 | |
| 		"\n"
 | |
| 		"  Server: vsock_diag_test --control-port=1234 --mode=server --peer-cid=3\n"
 | |
| 		"  Client: vsock_diag_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
 | |
| 		"\n"
 | |
| 		"Run vsock_diag.ko tests.  Must be launched in both\n"
 | |
| 		"guest and host.  One side must use --mode=client and\n"
 | |
| 		"the other side must use --mode=server.\n"
 | |
| 		"\n"
 | |
| 		"A TCP control socket connection is used to coordinate tests\n"
 | |
| 		"between the client and the server.  The server requires a\n"
 | |
| 		"listen address and the client requires an address to\n"
 | |
| 		"connect to.\n"
 | |
| 		"\n"
 | |
| 		"The CID of the other side must be given with --peer-cid=<cid>.\n");
 | |
| 	exit(EXIT_FAILURE);
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| 	const char *control_host = NULL;
 | |
| 	const char *control_port = NULL;
 | |
| 	int mode = TEST_MODE_UNSET;
 | |
| 	unsigned int peer_cid = VMADDR_CID_ANY;
 | |
| 	int i;
 | |
| 
 | |
| 	init_signals();
 | |
| 
 | |
| 	for (;;) {
 | |
| 		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
 | |
| 
 | |
| 		if (opt == -1)
 | |
| 			break;
 | |
| 
 | |
| 		switch (opt) {
 | |
| 		case 'H':
 | |
| 			control_host = optarg;
 | |
| 			break;
 | |
| 		case 'm':
 | |
| 			if (strcmp(optarg, "client") == 0)
 | |
| 				mode = TEST_MODE_CLIENT;
 | |
| 			else if (strcmp(optarg, "server") == 0)
 | |
| 				mode = TEST_MODE_SERVER;
 | |
| 			else {
 | |
| 				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
 | |
| 				return EXIT_FAILURE;
 | |
| 			}
 | |
| 			break;
 | |
| 		case 'p':
 | |
| 			peer_cid = parse_cid(optarg);
 | |
| 			break;
 | |
| 		case 'P':
 | |
| 			control_port = optarg;
 | |
| 			break;
 | |
| 		case '?':
 | |
| 		default:
 | |
| 			usage();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!control_port)
 | |
| 		usage();
 | |
| 	if (mode == TEST_MODE_UNSET)
 | |
| 		usage();
 | |
| 	if (peer_cid == VMADDR_CID_ANY)
 | |
| 		usage();
 | |
| 
 | |
| 	if (!control_host) {
 | |
| 		if (mode != TEST_MODE_SERVER)
 | |
| 			usage();
 | |
| 		control_host = "0.0.0.0";
 | |
| 	}
 | |
| 
 | |
| 	control_init(control_host, control_port, mode == TEST_MODE_SERVER);
 | |
| 
 | |
| 	for (i = 0; test_cases[i].name; i++) {
 | |
| 		void (*run)(unsigned int peer_cid);
 | |
| 
 | |
| 		printf("%s...", test_cases[i].name);
 | |
| 		fflush(stdout);
 | |
| 
 | |
| 		if (mode == TEST_MODE_CLIENT)
 | |
| 			run = test_cases[i].run_client;
 | |
| 		else
 | |
| 			run = test_cases[i].run_server;
 | |
| 
 | |
| 		if (run)
 | |
| 			run(peer_cid);
 | |
| 
 | |
| 		printf("ok\n");
 | |
| 	}
 | |
| 
 | |
| 	control_cleanup();
 | |
| 	return EXIT_SUCCESS;
 | |
| }
 |