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(); 2085a2b2425SStefano Garzarella 2095a2b2425SStefano Garzarella control_cmpln(line, str, true); 2105a2b2425SStefano Garzarella 2115a2b2425SStefano Garzarella free(line); 2125a2b2425SStefano Garzarella } 2135a2b2425SStefano Garzarella 2145a2b2425SStefano Garzarella bool control_cmpln(char *line, const char *str, bool fail) 2155a2b2425SStefano Garzarella { 2165a2b2425SStefano Garzarella if (strcmp(str, line) == 0) 2175a2b2425SStefano Garzarella return true; 2185a2b2425SStefano Garzarella 2195a2b2425SStefano Garzarella if (fail) { 2200b025033SStefan Hajnoczi fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n", 2210b025033SStefan Hajnoczi str, line); 2220b025033SStefan Hajnoczi exit(EXIT_FAILURE); 2230b025033SStefan Hajnoczi } 2240b025033SStefan Hajnoczi 2255a2b2425SStefano Garzarella return false; 2260b025033SStefan Hajnoczi } 227