xref: /openbmc/linux/tools/testing/vsock/control.c (revision 5c338112)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b025033SStefan Hajnoczi /* Control socket for client/server test execution
30b025033SStefan Hajnoczi  *
40b025033SStefan Hajnoczi  * Copyright (C) 2017 Red Hat, Inc.
50b025033SStefan Hajnoczi  *
60b025033SStefan Hajnoczi  * Author: Stefan Hajnoczi <stefanha@redhat.com>
70b025033SStefan Hajnoczi  */
80b025033SStefan Hajnoczi 
90b025033SStefan Hajnoczi /* The client and server may need to coordinate to avoid race conditions like
100b025033SStefan Hajnoczi  * the client attempting to connect to a socket that the server is not
110b025033SStefan Hajnoczi  * listening on yet.  The control socket offers a communications channel for
120b025033SStefan Hajnoczi  * such coordination tasks.
130b025033SStefan Hajnoczi  *
140b025033SStefan Hajnoczi  * If the client calls control_expectln("LISTENING"), then it will block until
150b025033SStefan Hajnoczi  * the server calls control_writeln("LISTENING").  This provides a simple
160b025033SStefan Hajnoczi  * mechanism for coordinating between the client and the server.
170b025033SStefan Hajnoczi  */
180b025033SStefan Hajnoczi 
190b025033SStefan Hajnoczi #include <errno.h>
200b025033SStefan Hajnoczi #include <netdb.h>
210b025033SStefan Hajnoczi #include <stdio.h>
220b025033SStefan Hajnoczi #include <stdlib.h>
230b025033SStefan Hajnoczi #include <string.h>
240b025033SStefan Hajnoczi #include <unistd.h>
250b025033SStefan Hajnoczi #include <sys/types.h>
260b025033SStefan Hajnoczi #include <sys/socket.h>
270b025033SStefan Hajnoczi 
280b025033SStefan Hajnoczi #include "timeout.h"
290b025033SStefan Hajnoczi #include "control.h"
300b025033SStefan Hajnoczi 
310b025033SStefan Hajnoczi static int control_fd = -1;
320b025033SStefan Hajnoczi 
330b025033SStefan Hajnoczi /* Open the control socket, either in server or client mode */
control_init(const char * control_host,const char * control_port,bool server)340b025033SStefan Hajnoczi void control_init(const char *control_host,
350b025033SStefan Hajnoczi 		  const char *control_port,
360b025033SStefan Hajnoczi 		  bool server)
370b025033SStefan Hajnoczi {
380b025033SStefan Hajnoczi 	struct addrinfo hints = {
390b025033SStefan Hajnoczi 		.ai_socktype = SOCK_STREAM,
400b025033SStefan Hajnoczi 	};
410b025033SStefan Hajnoczi 	struct addrinfo *result = NULL;
420b025033SStefan Hajnoczi 	struct addrinfo *ai;
430b025033SStefan Hajnoczi 	int ret;
440b025033SStefan Hajnoczi 
450b025033SStefan Hajnoczi 	ret = getaddrinfo(control_host, control_port, &hints, &result);
460b025033SStefan Hajnoczi 	if (ret != 0) {
470b025033SStefan Hajnoczi 		fprintf(stderr, "%s\n", gai_strerror(ret));
480b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
490b025033SStefan Hajnoczi 	}
500b025033SStefan Hajnoczi 
510b025033SStefan Hajnoczi 	for (ai = result; ai; ai = ai->ai_next) {
520b025033SStefan Hajnoczi 		int fd;
530b025033SStefan Hajnoczi 		int val = 1;
540b025033SStefan Hajnoczi 
550b025033SStefan Hajnoczi 		fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
560b025033SStefan Hajnoczi 		if (fd < 0)
570b025033SStefan Hajnoczi 			continue;
580b025033SStefan Hajnoczi 
590b025033SStefan Hajnoczi 		if (!server) {
600b025033SStefan Hajnoczi 			if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0)
610b025033SStefan Hajnoczi 				goto next;
620b025033SStefan Hajnoczi 			control_fd = fd;
630b025033SStefan Hajnoczi 			printf("Control socket connected to %s:%s.\n",
640b025033SStefan Hajnoczi 			       control_host, control_port);
650b025033SStefan Hajnoczi 			break;
660b025033SStefan Hajnoczi 		}
670b025033SStefan Hajnoczi 
680b025033SStefan Hajnoczi 		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
690b025033SStefan Hajnoczi 			       &val, sizeof(val)) < 0) {
700b025033SStefan Hajnoczi 			perror("setsockopt");
710b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
720b025033SStefan Hajnoczi 		}
730b025033SStefan Hajnoczi 
740b025033SStefan Hajnoczi 		if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
750b025033SStefan Hajnoczi 			goto next;
760b025033SStefan Hajnoczi 		if (listen(fd, 1) < 0)
770b025033SStefan Hajnoczi 			goto next;
780b025033SStefan Hajnoczi 
790b025033SStefan Hajnoczi 		printf("Control socket listening on %s:%s\n",
800b025033SStefan Hajnoczi 		       control_host, control_port);
810b025033SStefan Hajnoczi 		fflush(stdout);
820b025033SStefan Hajnoczi 
830b025033SStefan Hajnoczi 		control_fd = accept(fd, NULL, 0);
840b025033SStefan Hajnoczi 		close(fd);
850b025033SStefan Hajnoczi 
860b025033SStefan Hajnoczi 		if (control_fd < 0) {
870b025033SStefan Hajnoczi 			perror("accept");
880b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
890b025033SStefan Hajnoczi 		}
900b025033SStefan Hajnoczi 		printf("Control socket connection accepted...\n");
910b025033SStefan Hajnoczi 		break;
920b025033SStefan Hajnoczi 
930b025033SStefan Hajnoczi next:
940b025033SStefan Hajnoczi 		close(fd);
950b025033SStefan Hajnoczi 	}
960b025033SStefan Hajnoczi 
970b025033SStefan Hajnoczi 	if (control_fd < 0) {
980b025033SStefan Hajnoczi 		fprintf(stderr, "Control socket initialization failed.  Invalid address %s:%s?\n",
990b025033SStefan Hajnoczi 			control_host, control_port);
1000b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
1010b025033SStefan Hajnoczi 	}
1020b025033SStefan Hajnoczi 
1030b025033SStefan Hajnoczi 	freeaddrinfo(result);
1040b025033SStefan Hajnoczi }
1050b025033SStefan Hajnoczi 
1060b025033SStefan Hajnoczi /* Free resources */
control_cleanup(void)1070b025033SStefan Hajnoczi void control_cleanup(void)
1080b025033SStefan Hajnoczi {
1090b025033SStefan Hajnoczi 	close(control_fd);
1100b025033SStefan Hajnoczi 	control_fd = -1;
1110b025033SStefan Hajnoczi }
1120b025033SStefan Hajnoczi 
1130b025033SStefan Hajnoczi /* Write a line to the control socket */
control_writeln(const char * str)1140b025033SStefan Hajnoczi void control_writeln(const char *str)
1150b025033SStefan Hajnoczi {
1160b025033SStefan Hajnoczi 	ssize_t len = strlen(str);
1170b025033SStefan Hajnoczi 	ssize_t ret;
1180b025033SStefan Hajnoczi 
1190b025033SStefan Hajnoczi 	timeout_begin(TIMEOUT);
1200b025033SStefan Hajnoczi 
1210b025033SStefan Hajnoczi 	do {
1220b025033SStefan Hajnoczi 		ret = send(control_fd, str, len, MSG_MORE);
1230b025033SStefan Hajnoczi 		timeout_check("send");
1240b025033SStefan Hajnoczi 	} while (ret < 0 && errno == EINTR);
1250b025033SStefan Hajnoczi 
1260b025033SStefan Hajnoczi 	if (ret != len) {
1270b025033SStefan Hajnoczi 		perror("send");
1280b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
1290b025033SStefan Hajnoczi 	}
1300b025033SStefan Hajnoczi 
1310b025033SStefan Hajnoczi 	do {
1320b025033SStefan Hajnoczi 		ret = send(control_fd, "\n", 1, 0);
1330b025033SStefan Hajnoczi 		timeout_check("send");
1340b025033SStefan Hajnoczi 	} while (ret < 0 && errno == EINTR);
1350b025033SStefan Hajnoczi 
1360b025033SStefan Hajnoczi 	if (ret != 1) {
1370b025033SStefan Hajnoczi 		perror("send");
1380b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
1390b025033SStefan Hajnoczi 	}
1400b025033SStefan Hajnoczi 
1410b025033SStefan Hajnoczi 	timeout_end();
1420b025033SStefan Hajnoczi }
1430b025033SStefan Hajnoczi 
control_writeulong(unsigned long value)144*5c338112SArseniy Krasnov void control_writeulong(unsigned long value)
145*5c338112SArseniy Krasnov {
146*5c338112SArseniy Krasnov 	char str[32];
147*5c338112SArseniy Krasnov 
148*5c338112SArseniy Krasnov 	if (snprintf(str, sizeof(str), "%lu", value) >= sizeof(str)) {
149*5c338112SArseniy Krasnov 		perror("snprintf");
150*5c338112SArseniy Krasnov 		exit(EXIT_FAILURE);
151*5c338112SArseniy Krasnov 	}
152*5c338112SArseniy Krasnov 
153*5c338112SArseniy Krasnov 	control_writeln(str);
154*5c338112SArseniy Krasnov }
155*5c338112SArseniy Krasnov 
control_readulong(void)156*5c338112SArseniy Krasnov unsigned long control_readulong(void)
157*5c338112SArseniy Krasnov {
158*5c338112SArseniy Krasnov 	unsigned long value;
159*5c338112SArseniy Krasnov 	char *str;
160*5c338112SArseniy Krasnov 
161*5c338112SArseniy Krasnov 	str = control_readln();
162*5c338112SArseniy Krasnov 
163*5c338112SArseniy Krasnov 	if (!str)
164*5c338112SArseniy Krasnov 		exit(EXIT_FAILURE);
165*5c338112SArseniy Krasnov 
166*5c338112SArseniy Krasnov 	value = strtoul(str, NULL, 10);
167*5c338112SArseniy Krasnov 	free(str);
168*5c338112SArseniy Krasnov 
169*5c338112SArseniy Krasnov 	return value;
170*5c338112SArseniy Krasnov }
171*5c338112SArseniy Krasnov 
1720b025033SStefan Hajnoczi /* Return the next line from the control socket (without the trailing newline).
1730b025033SStefan Hajnoczi  *
1740b025033SStefan Hajnoczi  * The program terminates if a timeout occurs.
1750b025033SStefan Hajnoczi  *
1760b025033SStefan Hajnoczi  * The caller must free() the returned string.
1770b025033SStefan Hajnoczi  */
control_readln(void)1780b025033SStefan Hajnoczi char *control_readln(void)
1790b025033SStefan Hajnoczi {
1800b025033SStefan Hajnoczi 	char *buf = NULL;
1810b025033SStefan Hajnoczi 	size_t idx = 0;
1820b025033SStefan Hajnoczi 	size_t buflen = 0;
1830b025033SStefan Hajnoczi 
1840b025033SStefan Hajnoczi 	timeout_begin(TIMEOUT);
1850b025033SStefan Hajnoczi 
1860b025033SStefan Hajnoczi 	for (;;) {
1870b025033SStefan Hajnoczi 		ssize_t ret;
1880b025033SStefan Hajnoczi 
1890b025033SStefan Hajnoczi 		if (idx >= buflen) {
1900b025033SStefan Hajnoczi 			char *new_buf;
1910b025033SStefan Hajnoczi 
1920b025033SStefan Hajnoczi 			new_buf = realloc(buf, buflen + 80);
1930b025033SStefan Hajnoczi 			if (!new_buf) {
1940b025033SStefan Hajnoczi 				perror("realloc");
1950b025033SStefan Hajnoczi 				exit(EXIT_FAILURE);
1960b025033SStefan Hajnoczi 			}
1970b025033SStefan Hajnoczi 
1980b025033SStefan Hajnoczi 			buf = new_buf;
1990b025033SStefan Hajnoczi 			buflen += 80;
2000b025033SStefan Hajnoczi 		}
2010b025033SStefan Hajnoczi 
2020b025033SStefan Hajnoczi 		do {
2030b025033SStefan Hajnoczi 			ret = recv(control_fd, &buf[idx], 1, 0);
2040b025033SStefan Hajnoczi 			timeout_check("recv");
2050b025033SStefan Hajnoczi 		} while (ret < 0 && errno == EINTR);
2060b025033SStefan Hajnoczi 
2070b025033SStefan Hajnoczi 		if (ret == 0) {
2080b025033SStefan Hajnoczi 			fprintf(stderr, "unexpected EOF on control socket\n");
2090b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
2100b025033SStefan Hajnoczi 		}
2110b025033SStefan Hajnoczi 
2120b025033SStefan Hajnoczi 		if (ret != 1) {
2130b025033SStefan Hajnoczi 			perror("recv");
2140b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
2150b025033SStefan Hajnoczi 		}
2160b025033SStefan Hajnoczi 
2170b025033SStefan Hajnoczi 		if (buf[idx] == '\n') {
2180b025033SStefan Hajnoczi 			buf[idx] = '\0';
2190b025033SStefan Hajnoczi 			break;
2200b025033SStefan Hajnoczi 		}
2210b025033SStefan Hajnoczi 
2220b025033SStefan Hajnoczi 		idx++;
2230b025033SStefan Hajnoczi 	}
2240b025033SStefan Hajnoczi 
2250b025033SStefan Hajnoczi 	timeout_end();
2260b025033SStefan Hajnoczi 
2270b025033SStefan Hajnoczi 	return buf;
2280b025033SStefan Hajnoczi }
2290b025033SStefan Hajnoczi 
2300b025033SStefan Hajnoczi /* Wait until a given line is received or a timeout occurs */
control_expectln(const char * str)2310b025033SStefan Hajnoczi void control_expectln(const char *str)
2320b025033SStefan Hajnoczi {
2330b025033SStefan Hajnoczi 	char *line;
2340b025033SStefan Hajnoczi 
2350b025033SStefan Hajnoczi 	line = control_readln();
2365a2b2425SStefano Garzarella 
2375a2b2425SStefano Garzarella 	control_cmpln(line, str, true);
2385a2b2425SStefano Garzarella 
2395a2b2425SStefano Garzarella 	free(line);
2405a2b2425SStefano Garzarella }
2415a2b2425SStefano Garzarella 
control_cmpln(char * line,const char * str,bool fail)2425a2b2425SStefano Garzarella bool control_cmpln(char *line, const char *str, bool fail)
2435a2b2425SStefano Garzarella {
2445a2b2425SStefano Garzarella 	if (strcmp(str, line) == 0)
2455a2b2425SStefano Garzarella 		return true;
2465a2b2425SStefano Garzarella 
2475a2b2425SStefano Garzarella 	if (fail) {
2480b025033SStefan Hajnoczi 		fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n",
2490b025033SStefan Hajnoczi 			str, line);
2500b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
2510b025033SStefan Hajnoczi 	}
2520b025033SStefan Hajnoczi 
2535a2b2425SStefano Garzarella 	return false;
2540b025033SStefan Hajnoczi }
255