xref: /openbmc/linux/tools/testing/vsock/util.c (revision 9bb8a29d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * vsock test utilities
4  *
5  * Copyright (C) 2017 Red Hat, Inc.
6  *
7  * Author: Stefan Hajnoczi <stefanha@redhat.com>
8  */
9 
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <signal.h>
14 #include <unistd.h>
15 
16 #include "timeout.h"
17 #include "control.h"
18 #include "util.h"
19 
20 /* Install signal handlers */
21 void init_signals(void)
22 {
23 	struct sigaction act = {
24 		.sa_handler = sigalrm,
25 	};
26 
27 	sigaction(SIGALRM, &act, NULL);
28 	signal(SIGPIPE, SIG_IGN);
29 }
30 
31 /* Parse a CID in string representation */
32 unsigned int parse_cid(const char *str)
33 {
34 	char *endptr = NULL;
35 	unsigned long n;
36 
37 	errno = 0;
38 	n = strtoul(str, &endptr, 10);
39 	if (errno || *endptr != '\0') {
40 		fprintf(stderr, "malformed CID \"%s\"\n", str);
41 		exit(EXIT_FAILURE);
42 	}
43 	return n;
44 }
45 
46 /* Connect to <cid, port> and return the file descriptor. */
47 int vsock_stream_connect(unsigned int cid, unsigned int port)
48 {
49 	union {
50 		struct sockaddr sa;
51 		struct sockaddr_vm svm;
52 	} addr = {
53 		.svm = {
54 			.svm_family = AF_VSOCK,
55 			.svm_port = port,
56 			.svm_cid = cid,
57 		},
58 	};
59 	int ret;
60 	int fd;
61 
62 	control_expectln("LISTENING");
63 
64 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
65 
66 	timeout_begin(TIMEOUT);
67 	do {
68 		ret = connect(fd, &addr.sa, sizeof(addr.svm));
69 		timeout_check("connect");
70 	} while (ret < 0 && errno == EINTR);
71 	timeout_end();
72 
73 	if (ret < 0) {
74 		int old_errno = errno;
75 
76 		close(fd);
77 		fd = -1;
78 		errno = old_errno;
79 	}
80 	return fd;
81 }
82 
83 /* Listen on <cid, port> and return the first incoming connection.  The remote
84  * address is stored to clientaddrp.  clientaddrp may be NULL.
85  */
86 int vsock_stream_accept(unsigned int cid, unsigned int port,
87 			struct sockaddr_vm *clientaddrp)
88 {
89 	union {
90 		struct sockaddr sa;
91 		struct sockaddr_vm svm;
92 	} addr = {
93 		.svm = {
94 			.svm_family = AF_VSOCK,
95 			.svm_port = port,
96 			.svm_cid = cid,
97 		},
98 	};
99 	union {
100 		struct sockaddr sa;
101 		struct sockaddr_vm svm;
102 	} clientaddr;
103 	socklen_t clientaddr_len = sizeof(clientaddr.svm);
104 	int fd;
105 	int client_fd;
106 	int old_errno;
107 
108 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
109 
110 	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
111 		perror("bind");
112 		exit(EXIT_FAILURE);
113 	}
114 
115 	if (listen(fd, 1) < 0) {
116 		perror("listen");
117 		exit(EXIT_FAILURE);
118 	}
119 
120 	control_writeln("LISTENING");
121 
122 	timeout_begin(TIMEOUT);
123 	do {
124 		client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
125 		timeout_check("accept");
126 	} while (client_fd < 0 && errno == EINTR);
127 	timeout_end();
128 
129 	old_errno = errno;
130 	close(fd);
131 	errno = old_errno;
132 
133 	if (client_fd < 0)
134 		return client_fd;
135 
136 	if (clientaddr_len != sizeof(clientaddr.svm)) {
137 		fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",
138 			(size_t)clientaddr_len);
139 		exit(EXIT_FAILURE);
140 	}
141 	if (clientaddr.sa.sa_family != AF_VSOCK) {
142 		fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",
143 			clientaddr.sa.sa_family);
144 		exit(EXIT_FAILURE);
145 	}
146 
147 	if (clientaddrp)
148 		*clientaddrp = clientaddr.svm;
149 	return client_fd;
150 }
151 
152 /* Run test cases.  The program terminates if a failure occurs. */
153 void run_tests(const struct test_case *test_cases,
154 	       const struct test_opts *opts)
155 {
156 	int i;
157 
158 	for (i = 0; test_cases[i].name; i++) {
159 		void (*run)(const struct test_opts *opts);
160 
161 		printf("%s...", test_cases[i].name);
162 		fflush(stdout);
163 
164 		if (opts->mode == TEST_MODE_CLIENT)
165 			run = test_cases[i].run_client;
166 		else
167 			run = test_cases[i].run_server;
168 
169 		if (run)
170 			run(opts);
171 
172 		printf("ok\n");
173 	}
174 }
175