xref: /openbmc/linux/tools/testing/vsock/control.c (revision b886d83c)
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 */
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 */
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 */
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 
1440b025033SStefan Hajnoczi /* Return the next line from the control socket (without the trailing newline).
1450b025033SStefan Hajnoczi  *
1460b025033SStefan Hajnoczi  * The program terminates if a timeout occurs.
1470b025033SStefan Hajnoczi  *
1480b025033SStefan Hajnoczi  * The caller must free() the returned string.
1490b025033SStefan Hajnoczi  */
1500b025033SStefan Hajnoczi char *control_readln(void)
1510b025033SStefan Hajnoczi {
1520b025033SStefan Hajnoczi 	char *buf = NULL;
1530b025033SStefan Hajnoczi 	size_t idx = 0;
1540b025033SStefan Hajnoczi 	size_t buflen = 0;
1550b025033SStefan Hajnoczi 
1560b025033SStefan Hajnoczi 	timeout_begin(TIMEOUT);
1570b025033SStefan Hajnoczi 
1580b025033SStefan Hajnoczi 	for (;;) {
1590b025033SStefan Hajnoczi 		ssize_t ret;
1600b025033SStefan Hajnoczi 
1610b025033SStefan Hajnoczi 		if (idx >= buflen) {
1620b025033SStefan Hajnoczi 			char *new_buf;
1630b025033SStefan Hajnoczi 
1640b025033SStefan Hajnoczi 			new_buf = realloc(buf, buflen + 80);
1650b025033SStefan Hajnoczi 			if (!new_buf) {
1660b025033SStefan Hajnoczi 				perror("realloc");
1670b025033SStefan Hajnoczi 				exit(EXIT_FAILURE);
1680b025033SStefan Hajnoczi 			}
1690b025033SStefan Hajnoczi 
1700b025033SStefan Hajnoczi 			buf = new_buf;
1710b025033SStefan Hajnoczi 			buflen += 80;
1720b025033SStefan Hajnoczi 		}
1730b025033SStefan Hajnoczi 
1740b025033SStefan Hajnoczi 		do {
1750b025033SStefan Hajnoczi 			ret = recv(control_fd, &buf[idx], 1, 0);
1760b025033SStefan Hajnoczi 			timeout_check("recv");
1770b025033SStefan Hajnoczi 		} while (ret < 0 && errno == EINTR);
1780b025033SStefan Hajnoczi 
1790b025033SStefan Hajnoczi 		if (ret == 0) {
1800b025033SStefan Hajnoczi 			fprintf(stderr, "unexpected EOF on control socket\n");
1810b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
1820b025033SStefan Hajnoczi 		}
1830b025033SStefan Hajnoczi 
1840b025033SStefan Hajnoczi 		if (ret != 1) {
1850b025033SStefan Hajnoczi 			perror("recv");
1860b025033SStefan Hajnoczi 			exit(EXIT_FAILURE);
1870b025033SStefan Hajnoczi 		}
1880b025033SStefan Hajnoczi 
1890b025033SStefan Hajnoczi 		if (buf[idx] == '\n') {
1900b025033SStefan Hajnoczi 			buf[idx] = '\0';
1910b025033SStefan Hajnoczi 			break;
1920b025033SStefan Hajnoczi 		}
1930b025033SStefan Hajnoczi 
1940b025033SStefan Hajnoczi 		idx++;
1950b025033SStefan Hajnoczi 	}
1960b025033SStefan Hajnoczi 
1970b025033SStefan Hajnoczi 	timeout_end();
1980b025033SStefan Hajnoczi 
1990b025033SStefan Hajnoczi 	return buf;
2000b025033SStefan Hajnoczi }
2010b025033SStefan Hajnoczi 
2020b025033SStefan Hajnoczi /* Wait until a given line is received or a timeout occurs */
2030b025033SStefan Hajnoczi void control_expectln(const char *str)
2040b025033SStefan Hajnoczi {
2050b025033SStefan Hajnoczi 	char *line;
2060b025033SStefan Hajnoczi 
2070b025033SStefan Hajnoczi 	line = control_readln();
2080b025033SStefan Hajnoczi 	if (strcmp(str, line) != 0) {
2090b025033SStefan Hajnoczi 		fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n",
2100b025033SStefan Hajnoczi 			str, line);
2110b025033SStefan Hajnoczi 		exit(EXIT_FAILURE);
2120b025033SStefan Hajnoczi 	}
2130b025033SStefan Hajnoczi 
2140b025033SStefan Hajnoczi 	free(line);
2150b025033SStefan Hajnoczi }
216