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