xref: /openbmc/linux/tools/testing/vsock/control.c (revision 2c64e9cb)
1 /* Control socket for client/server test execution
2  *
3  * Copyright (C) 2017 Red Hat, Inc.
4  *
5  * Author: Stefan Hajnoczi <stefanha@redhat.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; version 2
10  * of the License.
11  */
12 
13 /* The client and server may need to coordinate to avoid race conditions like
14  * the client attempting to connect to a socket that the server is not
15  * listening on yet.  The control socket offers a communications channel for
16  * such coordination tasks.
17  *
18  * If the client calls control_expectln("LISTENING"), then it will block until
19  * the server calls control_writeln("LISTENING").  This provides a simple
20  * mechanism for coordinating between the client and the server.
21  */
22 
23 #include <errno.h>
24 #include <netdb.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 
32 #include "timeout.h"
33 #include "control.h"
34 
35 static int control_fd = -1;
36 
37 /* Open the control socket, either in server or client mode */
38 void control_init(const char *control_host,
39 		  const char *control_port,
40 		  bool server)
41 {
42 	struct addrinfo hints = {
43 		.ai_socktype = SOCK_STREAM,
44 	};
45 	struct addrinfo *result = NULL;
46 	struct addrinfo *ai;
47 	int ret;
48 
49 	ret = getaddrinfo(control_host, control_port, &hints, &result);
50 	if (ret != 0) {
51 		fprintf(stderr, "%s\n", gai_strerror(ret));
52 		exit(EXIT_FAILURE);
53 	}
54 
55 	for (ai = result; ai; ai = ai->ai_next) {
56 		int fd;
57 		int val = 1;
58 
59 		fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
60 		if (fd < 0)
61 			continue;
62 
63 		if (!server) {
64 			if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0)
65 				goto next;
66 			control_fd = fd;
67 			printf("Control socket connected to %s:%s.\n",
68 			       control_host, control_port);
69 			break;
70 		}
71 
72 		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
73 			       &val, sizeof(val)) < 0) {
74 			perror("setsockopt");
75 			exit(EXIT_FAILURE);
76 		}
77 
78 		if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
79 			goto next;
80 		if (listen(fd, 1) < 0)
81 			goto next;
82 
83 		printf("Control socket listening on %s:%s\n",
84 		       control_host, control_port);
85 		fflush(stdout);
86 
87 		control_fd = accept(fd, NULL, 0);
88 		close(fd);
89 
90 		if (control_fd < 0) {
91 			perror("accept");
92 			exit(EXIT_FAILURE);
93 		}
94 		printf("Control socket connection accepted...\n");
95 		break;
96 
97 next:
98 		close(fd);
99 	}
100 
101 	if (control_fd < 0) {
102 		fprintf(stderr, "Control socket initialization failed.  Invalid address %s:%s?\n",
103 			control_host, control_port);
104 		exit(EXIT_FAILURE);
105 	}
106 
107 	freeaddrinfo(result);
108 }
109 
110 /* Free resources */
111 void control_cleanup(void)
112 {
113 	close(control_fd);
114 	control_fd = -1;
115 }
116 
117 /* Write a line to the control socket */
118 void control_writeln(const char *str)
119 {
120 	ssize_t len = strlen(str);
121 	ssize_t ret;
122 
123 	timeout_begin(TIMEOUT);
124 
125 	do {
126 		ret = send(control_fd, str, len, MSG_MORE);
127 		timeout_check("send");
128 	} while (ret < 0 && errno == EINTR);
129 
130 	if (ret != len) {
131 		perror("send");
132 		exit(EXIT_FAILURE);
133 	}
134 
135 	do {
136 		ret = send(control_fd, "\n", 1, 0);
137 		timeout_check("send");
138 	} while (ret < 0 && errno == EINTR);
139 
140 	if (ret != 1) {
141 		perror("send");
142 		exit(EXIT_FAILURE);
143 	}
144 
145 	timeout_end();
146 }
147 
148 /* Return the next line from the control socket (without the trailing newline).
149  *
150  * The program terminates if a timeout occurs.
151  *
152  * The caller must free() the returned string.
153  */
154 char *control_readln(void)
155 {
156 	char *buf = NULL;
157 	size_t idx = 0;
158 	size_t buflen = 0;
159 
160 	timeout_begin(TIMEOUT);
161 
162 	for (;;) {
163 		ssize_t ret;
164 
165 		if (idx >= buflen) {
166 			char *new_buf;
167 
168 			new_buf = realloc(buf, buflen + 80);
169 			if (!new_buf) {
170 				perror("realloc");
171 				exit(EXIT_FAILURE);
172 			}
173 
174 			buf = new_buf;
175 			buflen += 80;
176 		}
177 
178 		do {
179 			ret = recv(control_fd, &buf[idx], 1, 0);
180 			timeout_check("recv");
181 		} while (ret < 0 && errno == EINTR);
182 
183 		if (ret == 0) {
184 			fprintf(stderr, "unexpected EOF on control socket\n");
185 			exit(EXIT_FAILURE);
186 		}
187 
188 		if (ret != 1) {
189 			perror("recv");
190 			exit(EXIT_FAILURE);
191 		}
192 
193 		if (buf[idx] == '\n') {
194 			buf[idx] = '\0';
195 			break;
196 		}
197 
198 		idx++;
199 	}
200 
201 	timeout_end();
202 
203 	return buf;
204 }
205 
206 /* Wait until a given line is received or a timeout occurs */
207 void control_expectln(const char *str)
208 {
209 	char *line;
210 
211 	line = control_readln();
212 	if (strcmp(str, line) != 0) {
213 		fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n",
214 			str, line);
215 		exit(EXIT_FAILURE);
216 	}
217 
218 	free(line);
219 }
220