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 <[email protected]>
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"
30*86814d8fSKonstantin Shkolnyy #include "util.h"
310b025033SStefan Hajnoczi
320b025033SStefan Hajnoczi static int control_fd = -1;
330b025033SStefan Hajnoczi
340b025033SStefan Hajnoczi /* Open the control socket, either in server or client mode */
control_init(const char * control_host,const char * control_port,bool server)350b025033SStefan Hajnoczi void control_init(const char *control_host,
360b025033SStefan Hajnoczi const char *control_port,
370b025033SStefan Hajnoczi bool server)
380b025033SStefan Hajnoczi {
390b025033SStefan Hajnoczi struct addrinfo hints = {
400b025033SStefan Hajnoczi .ai_socktype = SOCK_STREAM,
410b025033SStefan Hajnoczi };
420b025033SStefan Hajnoczi struct addrinfo *result = NULL;
430b025033SStefan Hajnoczi struct addrinfo *ai;
440b025033SStefan Hajnoczi int ret;
450b025033SStefan Hajnoczi
460b025033SStefan Hajnoczi ret = getaddrinfo(control_host, control_port, &hints, &result);
470b025033SStefan Hajnoczi if (ret != 0) {
480b025033SStefan Hajnoczi fprintf(stderr, "%s\n", gai_strerror(ret));
490b025033SStefan Hajnoczi exit(EXIT_FAILURE);
500b025033SStefan Hajnoczi }
510b025033SStefan Hajnoczi
520b025033SStefan Hajnoczi for (ai = result; ai; ai = ai->ai_next) {
530b025033SStefan Hajnoczi int fd;
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
68*86814d8fSKonstantin Shkolnyy setsockopt_int_check(fd, SOL_SOCKET, SO_REUSEADDR, 1,
69*86814d8fSKonstantin Shkolnyy "setsockopt SO_REUSEADDR");
700b025033SStefan Hajnoczi
710b025033SStefan Hajnoczi if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
720b025033SStefan Hajnoczi goto next;
730b025033SStefan Hajnoczi if (listen(fd, 1) < 0)
740b025033SStefan Hajnoczi goto next;
750b025033SStefan Hajnoczi
760b025033SStefan Hajnoczi printf("Control socket listening on %s:%s\n",
770b025033SStefan Hajnoczi control_host, control_port);
780b025033SStefan Hajnoczi fflush(stdout);
790b025033SStefan Hajnoczi
800b025033SStefan Hajnoczi control_fd = accept(fd, NULL, 0);
810b025033SStefan Hajnoczi close(fd);
820b025033SStefan Hajnoczi
830b025033SStefan Hajnoczi if (control_fd < 0) {
840b025033SStefan Hajnoczi perror("accept");
850b025033SStefan Hajnoczi exit(EXIT_FAILURE);
860b025033SStefan Hajnoczi }
870b025033SStefan Hajnoczi printf("Control socket connection accepted...\n");
880b025033SStefan Hajnoczi break;
890b025033SStefan Hajnoczi
900b025033SStefan Hajnoczi next:
910b025033SStefan Hajnoczi close(fd);
920b025033SStefan Hajnoczi }
930b025033SStefan Hajnoczi
940b025033SStefan Hajnoczi if (control_fd < 0) {
950b025033SStefan Hajnoczi fprintf(stderr, "Control socket initialization failed. Invalid address %s:%s?\n",
960b025033SStefan Hajnoczi control_host, control_port);
970b025033SStefan Hajnoczi exit(EXIT_FAILURE);
980b025033SStefan Hajnoczi }
990b025033SStefan Hajnoczi
1000b025033SStefan Hajnoczi freeaddrinfo(result);
1010b025033SStefan Hajnoczi }
1020b025033SStefan Hajnoczi
1030b025033SStefan Hajnoczi /* Free resources */
control_cleanup(void)1040b025033SStefan Hajnoczi void control_cleanup(void)
1050b025033SStefan Hajnoczi {
1060b025033SStefan Hajnoczi close(control_fd);
1070b025033SStefan Hajnoczi control_fd = -1;
1080b025033SStefan Hajnoczi }
1090b025033SStefan Hajnoczi
1100b025033SStefan Hajnoczi /* Write a line to the control socket */
control_writeln(const char * str)1110b025033SStefan Hajnoczi void control_writeln(const char *str)
1120b025033SStefan Hajnoczi {
1130b025033SStefan Hajnoczi ssize_t len = strlen(str);
1140b025033SStefan Hajnoczi ssize_t ret;
1150b025033SStefan Hajnoczi
1160b025033SStefan Hajnoczi timeout_begin(TIMEOUT);
1170b025033SStefan Hajnoczi
1180b025033SStefan Hajnoczi do {
1190b025033SStefan Hajnoczi ret = send(control_fd, str, len, MSG_MORE);
1200b025033SStefan Hajnoczi timeout_check("send");
1210b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR);
1220b025033SStefan Hajnoczi
1230b025033SStefan Hajnoczi if (ret != len) {
1240b025033SStefan Hajnoczi perror("send");
1250b025033SStefan Hajnoczi exit(EXIT_FAILURE);
1260b025033SStefan Hajnoczi }
1270b025033SStefan Hajnoczi
1280b025033SStefan Hajnoczi do {
1290b025033SStefan Hajnoczi ret = send(control_fd, "\n", 1, 0);
1300b025033SStefan Hajnoczi timeout_check("send");
1310b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR);
1320b025033SStefan Hajnoczi
1330b025033SStefan Hajnoczi if (ret != 1) {
1340b025033SStefan Hajnoczi perror("send");
1350b025033SStefan Hajnoczi exit(EXIT_FAILURE);
1360b025033SStefan Hajnoczi }
1370b025033SStefan Hajnoczi
1380b025033SStefan Hajnoczi timeout_end();
1390b025033SStefan Hajnoczi }
1400b025033SStefan Hajnoczi
control_writeulong(unsigned long value)1415c338112SArseniy Krasnov void control_writeulong(unsigned long value)
1425c338112SArseniy Krasnov {
1435c338112SArseniy Krasnov char str[32];
1445c338112SArseniy Krasnov
1455c338112SArseniy Krasnov if (snprintf(str, sizeof(str), "%lu", value) >= sizeof(str)) {
1465c338112SArseniy Krasnov perror("snprintf");
1475c338112SArseniy Krasnov exit(EXIT_FAILURE);
1485c338112SArseniy Krasnov }
1495c338112SArseniy Krasnov
1505c338112SArseniy Krasnov control_writeln(str);
1515c338112SArseniy Krasnov }
1525c338112SArseniy Krasnov
control_readulong(void)1535c338112SArseniy Krasnov unsigned long control_readulong(void)
1545c338112SArseniy Krasnov {
1555c338112SArseniy Krasnov unsigned long value;
1565c338112SArseniy Krasnov char *str;
1575c338112SArseniy Krasnov
1585c338112SArseniy Krasnov str = control_readln();
1595c338112SArseniy Krasnov
1605c338112SArseniy Krasnov if (!str)
1615c338112SArseniy Krasnov exit(EXIT_FAILURE);
1625c338112SArseniy Krasnov
1635c338112SArseniy Krasnov value = strtoul(str, NULL, 10);
1645c338112SArseniy Krasnov free(str);
1655c338112SArseniy Krasnov
1665c338112SArseniy Krasnov return value;
1675c338112SArseniy Krasnov }
1685c338112SArseniy Krasnov
1690b025033SStefan Hajnoczi /* Return the next line from the control socket (without the trailing newline).
1700b025033SStefan Hajnoczi *
1710b025033SStefan Hajnoczi * The program terminates if a timeout occurs.
1720b025033SStefan Hajnoczi *
1730b025033SStefan Hajnoczi * The caller must free() the returned string.
1740b025033SStefan Hajnoczi */
control_readln(void)1750b025033SStefan Hajnoczi char *control_readln(void)
1760b025033SStefan Hajnoczi {
1770b025033SStefan Hajnoczi char *buf = NULL;
1780b025033SStefan Hajnoczi size_t idx = 0;
1790b025033SStefan Hajnoczi size_t buflen = 0;
1800b025033SStefan Hajnoczi
1810b025033SStefan Hajnoczi timeout_begin(TIMEOUT);
1820b025033SStefan Hajnoczi
1830b025033SStefan Hajnoczi for (;;) {
1840b025033SStefan Hajnoczi ssize_t ret;
1850b025033SStefan Hajnoczi
1860b025033SStefan Hajnoczi if (idx >= buflen) {
1870b025033SStefan Hajnoczi char *new_buf;
1880b025033SStefan Hajnoczi
1890b025033SStefan Hajnoczi new_buf = realloc(buf, buflen + 80);
1900b025033SStefan Hajnoczi if (!new_buf) {
1910b025033SStefan Hajnoczi perror("realloc");
1920b025033SStefan Hajnoczi exit(EXIT_FAILURE);
1930b025033SStefan Hajnoczi }
1940b025033SStefan Hajnoczi
1950b025033SStefan Hajnoczi buf = new_buf;
1960b025033SStefan Hajnoczi buflen += 80;
1970b025033SStefan Hajnoczi }
1980b025033SStefan Hajnoczi
1990b025033SStefan Hajnoczi do {
2000b025033SStefan Hajnoczi ret = recv(control_fd, &buf[idx], 1, 0);
2010b025033SStefan Hajnoczi timeout_check("recv");
2020b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR);
2030b025033SStefan Hajnoczi
2040b025033SStefan Hajnoczi if (ret == 0) {
2050b025033SStefan Hajnoczi fprintf(stderr, "unexpected EOF on control socket\n");
2060b025033SStefan Hajnoczi exit(EXIT_FAILURE);
2070b025033SStefan Hajnoczi }
2080b025033SStefan Hajnoczi
2090b025033SStefan Hajnoczi if (ret != 1) {
2100b025033SStefan Hajnoczi perror("recv");
2110b025033SStefan Hajnoczi exit(EXIT_FAILURE);
2120b025033SStefan Hajnoczi }
2130b025033SStefan Hajnoczi
2140b025033SStefan Hajnoczi if (buf[idx] == '\n') {
2150b025033SStefan Hajnoczi buf[idx] = '\0';
2160b025033SStefan Hajnoczi break;
2170b025033SStefan Hajnoczi }
2180b025033SStefan Hajnoczi
2190b025033SStefan Hajnoczi idx++;
2200b025033SStefan Hajnoczi }
2210b025033SStefan Hajnoczi
2220b025033SStefan Hajnoczi timeout_end();
2230b025033SStefan Hajnoczi
2240b025033SStefan Hajnoczi return buf;
2250b025033SStefan Hajnoczi }
2260b025033SStefan Hajnoczi
2270b025033SStefan Hajnoczi /* Wait until a given line is received or a timeout occurs */
control_expectln(const char * str)2280b025033SStefan Hajnoczi void control_expectln(const char *str)
2290b025033SStefan Hajnoczi {
2300b025033SStefan Hajnoczi char *line;
2310b025033SStefan Hajnoczi
2320b025033SStefan Hajnoczi line = control_readln();
2335a2b2425SStefano Garzarella
2345a2b2425SStefano Garzarella control_cmpln(line, str, true);
2355a2b2425SStefano Garzarella
2365a2b2425SStefano Garzarella free(line);
2375a2b2425SStefano Garzarella }
2385a2b2425SStefano Garzarella
control_cmpln(char * line,const char * str,bool fail)2395a2b2425SStefano Garzarella bool control_cmpln(char *line, const char *str, bool fail)
2405a2b2425SStefano Garzarella {
2415a2b2425SStefano Garzarella if (strcmp(str, line) == 0)
2425a2b2425SStefano Garzarella return true;
2435a2b2425SStefano Garzarella
2445a2b2425SStefano Garzarella if (fail) {
2450b025033SStefan Hajnoczi fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n",
2460b025033SStefan Hajnoczi str, line);
2470b025033SStefan Hajnoczi exit(EXIT_FAILURE);
2480b025033SStefan Hajnoczi }
2490b025033SStefan Hajnoczi
2505a2b2425SStefano Garzarella return false;
2510b025033SStefan Hajnoczi }
252