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