10b025033SStefan Hajnoczi /* Control socket for client/server test execution 20b025033SStefan Hajnoczi * 30b025033SStefan Hajnoczi * Copyright (C) 2017 Red Hat, Inc. 40b025033SStefan Hajnoczi * 50b025033SStefan Hajnoczi * Author: Stefan Hajnoczi <stefanha@redhat.com> 60b025033SStefan Hajnoczi * 70b025033SStefan Hajnoczi * This program is free software; you can redistribute it and/or 80b025033SStefan Hajnoczi * modify it under the terms of the GNU General Public License 90b025033SStefan Hajnoczi * as published by the Free Software Foundation; version 2 100b025033SStefan Hajnoczi * of the License. 110b025033SStefan Hajnoczi */ 120b025033SStefan Hajnoczi 130b025033SStefan Hajnoczi /* The client and server may need to coordinate to avoid race conditions like 140b025033SStefan Hajnoczi * the client attempting to connect to a socket that the server is not 150b025033SStefan Hajnoczi * listening on yet. The control socket offers a communications channel for 160b025033SStefan Hajnoczi * such coordination tasks. 170b025033SStefan Hajnoczi * 180b025033SStefan Hajnoczi * If the client calls control_expectln("LISTENING"), then it will block until 190b025033SStefan Hajnoczi * the server calls control_writeln("LISTENING"). This provides a simple 200b025033SStefan Hajnoczi * mechanism for coordinating between the client and the server. 210b025033SStefan Hajnoczi */ 220b025033SStefan Hajnoczi 230b025033SStefan Hajnoczi #include <errno.h> 240b025033SStefan Hajnoczi #include <netdb.h> 250b025033SStefan Hajnoczi #include <stdio.h> 260b025033SStefan Hajnoczi #include <stdlib.h> 270b025033SStefan Hajnoczi #include <string.h> 280b025033SStefan Hajnoczi #include <unistd.h> 290b025033SStefan Hajnoczi #include <sys/types.h> 300b025033SStefan Hajnoczi #include <sys/socket.h> 310b025033SStefan Hajnoczi 320b025033SStefan Hajnoczi #include "timeout.h" 330b025033SStefan Hajnoczi #include "control.h" 340b025033SStefan Hajnoczi 350b025033SStefan Hajnoczi static int control_fd = -1; 360b025033SStefan Hajnoczi 370b025033SStefan Hajnoczi /* Open the control socket, either in server or client mode */ 380b025033SStefan Hajnoczi void control_init(const char *control_host, 390b025033SStefan Hajnoczi const char *control_port, 400b025033SStefan Hajnoczi bool server) 410b025033SStefan Hajnoczi { 420b025033SStefan Hajnoczi struct addrinfo hints = { 430b025033SStefan Hajnoczi .ai_socktype = SOCK_STREAM, 440b025033SStefan Hajnoczi }; 450b025033SStefan Hajnoczi struct addrinfo *result = NULL; 460b025033SStefan Hajnoczi struct addrinfo *ai; 470b025033SStefan Hajnoczi int ret; 480b025033SStefan Hajnoczi 490b025033SStefan Hajnoczi ret = getaddrinfo(control_host, control_port, &hints, &result); 500b025033SStefan Hajnoczi if (ret != 0) { 510b025033SStefan Hajnoczi fprintf(stderr, "%s\n", gai_strerror(ret)); 520b025033SStefan Hajnoczi exit(EXIT_FAILURE); 530b025033SStefan Hajnoczi } 540b025033SStefan Hajnoczi 550b025033SStefan Hajnoczi for (ai = result; ai; ai = ai->ai_next) { 560b025033SStefan Hajnoczi int fd; 570b025033SStefan Hajnoczi int val = 1; 580b025033SStefan Hajnoczi 590b025033SStefan Hajnoczi fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 600b025033SStefan Hajnoczi if (fd < 0) 610b025033SStefan Hajnoczi continue; 620b025033SStefan Hajnoczi 630b025033SStefan Hajnoczi if (!server) { 640b025033SStefan Hajnoczi if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0) 650b025033SStefan Hajnoczi goto next; 660b025033SStefan Hajnoczi control_fd = fd; 670b025033SStefan Hajnoczi printf("Control socket connected to %s:%s.\n", 680b025033SStefan Hajnoczi control_host, control_port); 690b025033SStefan Hajnoczi break; 700b025033SStefan Hajnoczi } 710b025033SStefan Hajnoczi 720b025033SStefan Hajnoczi if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 730b025033SStefan Hajnoczi &val, sizeof(val)) < 0) { 740b025033SStefan Hajnoczi perror("setsockopt"); 750b025033SStefan Hajnoczi exit(EXIT_FAILURE); 760b025033SStefan Hajnoczi } 770b025033SStefan Hajnoczi 780b025033SStefan Hajnoczi if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) 790b025033SStefan Hajnoczi goto next; 800b025033SStefan Hajnoczi if (listen(fd, 1) < 0) 810b025033SStefan Hajnoczi goto next; 820b025033SStefan Hajnoczi 830b025033SStefan Hajnoczi printf("Control socket listening on %s:%s\n", 840b025033SStefan Hajnoczi control_host, control_port); 850b025033SStefan Hajnoczi fflush(stdout); 860b025033SStefan Hajnoczi 870b025033SStefan Hajnoczi control_fd = accept(fd, NULL, 0); 880b025033SStefan Hajnoczi close(fd); 890b025033SStefan Hajnoczi 900b025033SStefan Hajnoczi if (control_fd < 0) { 910b025033SStefan Hajnoczi perror("accept"); 920b025033SStefan Hajnoczi exit(EXIT_FAILURE); 930b025033SStefan Hajnoczi } 940b025033SStefan Hajnoczi printf("Control socket connection accepted...\n"); 950b025033SStefan Hajnoczi break; 960b025033SStefan Hajnoczi 970b025033SStefan Hajnoczi next: 980b025033SStefan Hajnoczi close(fd); 990b025033SStefan Hajnoczi } 1000b025033SStefan Hajnoczi 1010b025033SStefan Hajnoczi if (control_fd < 0) { 1020b025033SStefan Hajnoczi fprintf(stderr, "Control socket initialization failed. Invalid address %s:%s?\n", 1030b025033SStefan Hajnoczi control_host, control_port); 1040b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1050b025033SStefan Hajnoczi } 1060b025033SStefan Hajnoczi 1070b025033SStefan Hajnoczi freeaddrinfo(result); 1080b025033SStefan Hajnoczi } 1090b025033SStefan Hajnoczi 1100b025033SStefan Hajnoczi /* Free resources */ 1110b025033SStefan Hajnoczi void control_cleanup(void) 1120b025033SStefan Hajnoczi { 1130b025033SStefan Hajnoczi close(control_fd); 1140b025033SStefan Hajnoczi control_fd = -1; 1150b025033SStefan Hajnoczi } 1160b025033SStefan Hajnoczi 1170b025033SStefan Hajnoczi /* Write a line to the control socket */ 1180b025033SStefan Hajnoczi void control_writeln(const char *str) 1190b025033SStefan Hajnoczi { 1200b025033SStefan Hajnoczi ssize_t len = strlen(str); 1210b025033SStefan Hajnoczi ssize_t ret; 1220b025033SStefan Hajnoczi 1230b025033SStefan Hajnoczi timeout_begin(TIMEOUT); 1240b025033SStefan Hajnoczi 1250b025033SStefan Hajnoczi do { 1260b025033SStefan Hajnoczi ret = send(control_fd, str, len, MSG_MORE); 1270b025033SStefan Hajnoczi timeout_check("send"); 1280b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR); 1290b025033SStefan Hajnoczi 1300b025033SStefan Hajnoczi if (ret != len) { 1310b025033SStefan Hajnoczi perror("send"); 1320b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1330b025033SStefan Hajnoczi } 1340b025033SStefan Hajnoczi 1350b025033SStefan Hajnoczi do { 1360b025033SStefan Hajnoczi ret = send(control_fd, "\n", 1, 0); 1370b025033SStefan Hajnoczi timeout_check("send"); 1380b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR); 1390b025033SStefan Hajnoczi 1400b025033SStefan Hajnoczi if (ret != 1) { 1410b025033SStefan Hajnoczi perror("send"); 1420b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1430b025033SStefan Hajnoczi } 1440b025033SStefan Hajnoczi 1450b025033SStefan Hajnoczi timeout_end(); 1460b025033SStefan Hajnoczi } 1470b025033SStefan Hajnoczi 1480b025033SStefan Hajnoczi /* Return the next line from the control socket (without the trailing newline). 1490b025033SStefan Hajnoczi * 1500b025033SStefan Hajnoczi * The program terminates if a timeout occurs. 1510b025033SStefan Hajnoczi * 1520b025033SStefan Hajnoczi * The caller must free() the returned string. 1530b025033SStefan Hajnoczi */ 1540b025033SStefan Hajnoczi char *control_readln(void) 1550b025033SStefan Hajnoczi { 1560b025033SStefan Hajnoczi char *buf = NULL; 1570b025033SStefan Hajnoczi size_t idx = 0; 1580b025033SStefan Hajnoczi size_t buflen = 0; 1590b025033SStefan Hajnoczi 1600b025033SStefan Hajnoczi timeout_begin(TIMEOUT); 1610b025033SStefan Hajnoczi 1620b025033SStefan Hajnoczi for (;;) { 1630b025033SStefan Hajnoczi ssize_t ret; 1640b025033SStefan Hajnoczi 1650b025033SStefan Hajnoczi if (idx >= buflen) { 1660b025033SStefan Hajnoczi char *new_buf; 1670b025033SStefan Hajnoczi 1680b025033SStefan Hajnoczi new_buf = realloc(buf, buflen + 80); 1690b025033SStefan Hajnoczi if (!new_buf) { 1700b025033SStefan Hajnoczi perror("realloc"); 1710b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1720b025033SStefan Hajnoczi } 1730b025033SStefan Hajnoczi 1740b025033SStefan Hajnoczi buf = new_buf; 1750b025033SStefan Hajnoczi buflen += 80; 1760b025033SStefan Hajnoczi } 1770b025033SStefan Hajnoczi 1780b025033SStefan Hajnoczi do { 1790b025033SStefan Hajnoczi ret = recv(control_fd, &buf[idx], 1, 0); 1800b025033SStefan Hajnoczi timeout_check("recv"); 1810b025033SStefan Hajnoczi } while (ret < 0 && errno == EINTR); 1820b025033SStefan Hajnoczi 1830b025033SStefan Hajnoczi if (ret == 0) { 1840b025033SStefan Hajnoczi fprintf(stderr, "unexpected EOF on control socket\n"); 1850b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1860b025033SStefan Hajnoczi } 1870b025033SStefan Hajnoczi 1880b025033SStefan Hajnoczi if (ret != 1) { 1890b025033SStefan Hajnoczi perror("recv"); 1900b025033SStefan Hajnoczi exit(EXIT_FAILURE); 1910b025033SStefan Hajnoczi } 1920b025033SStefan Hajnoczi 1930b025033SStefan Hajnoczi if (buf[idx] == '\n') { 1940b025033SStefan Hajnoczi buf[idx] = '\0'; 1950b025033SStefan Hajnoczi break; 1960b025033SStefan Hajnoczi } 1970b025033SStefan Hajnoczi 1980b025033SStefan Hajnoczi idx++; 1990b025033SStefan Hajnoczi } 2000b025033SStefan Hajnoczi 2010b025033SStefan Hajnoczi timeout_end(); 2020b025033SStefan Hajnoczi 2030b025033SStefan Hajnoczi return buf; 2040b025033SStefan Hajnoczi } 2050b025033SStefan Hajnoczi 2060b025033SStefan Hajnoczi /* Wait until a given line is received or a timeout occurs */ 2070b025033SStefan Hajnoczi void control_expectln(const char *str) 2080b025033SStefan Hajnoczi { 2090b025033SStefan Hajnoczi char *line; 2100b025033SStefan Hajnoczi 2110b025033SStefan Hajnoczi line = control_readln(); 2120b025033SStefan Hajnoczi if (strcmp(str, line) != 0) { 2130b025033SStefan Hajnoczi fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n", 2140b025033SStefan Hajnoczi str, line); 2150b025033SStefan Hajnoczi exit(EXIT_FAILURE); 2160b025033SStefan Hajnoczi } 2170b025033SStefan Hajnoczi 2180b025033SStefan Hajnoczi free(line); 2190b025033SStefan Hajnoczi } 220