Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/* Control socket for client/server test execution
  2 *
  3 * Copyright (C) 2017 Red Hat, Inc.
  4 *
  5 * Author: Stefan Hajnoczi <stefanha@redhat.com>
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU General Public License
  9 * as published by the Free Software Foundation; version 2
 10 * of the License.
 11 */
 12
 13/* The client and server may need to coordinate to avoid race conditions like
 14 * the client attempting to connect to a socket that the server is not
 15 * listening on yet.  The control socket offers a communications channel for
 16 * such coordination tasks.
 17 *
 18 * If the client calls control_expectln("LISTENING"), then it will block until
 19 * the server calls control_writeln("LISTENING").  This provides a simple
 20 * mechanism for coordinating between the client and the server.
 21 */
 22
 23#include <errno.h>
 24#include <netdb.h>
 25#include <stdio.h>
 26#include <stdlib.h>
 27#include <string.h>
 28#include <unistd.h>
 29#include <sys/types.h>
 30#include <sys/socket.h>
 31
 32#include "timeout.h"
 33#include "control.h"
 34
 35static int control_fd = -1;
 36
 37/* Open the control socket, either in server or client mode */
 38void control_init(const char *control_host,
 39		  const char *control_port,
 40		  bool server)
 41{
 42	struct addrinfo hints = {
 43		.ai_socktype = SOCK_STREAM,
 44	};
 45	struct addrinfo *result = NULL;
 46	struct addrinfo *ai;
 47	int ret;
 48
 49	ret = getaddrinfo(control_host, control_port, &hints, &result);
 50	if (ret != 0) {
 51		fprintf(stderr, "%s\n", gai_strerror(ret));
 52		exit(EXIT_FAILURE);
 53	}
 54
 55	for (ai = result; ai; ai = ai->ai_next) {
 56		int fd;
 57		int val = 1;
 58
 59		fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 60		if (fd < 0)
 61			continue;
 62
 63		if (!server) {
 64			if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0)
 65				goto next;
 66			control_fd = fd;
 67			printf("Control socket connected to %s:%s.\n",
 68			       control_host, control_port);
 69			break;
 70		}
 71
 72		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
 73			       &val, sizeof(val)) < 0) {
 74			perror("setsockopt");
 75			exit(EXIT_FAILURE);
 76		}
 77
 78		if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
 79			goto next;
 80		if (listen(fd, 1) < 0)
 81			goto next;
 82
 83		printf("Control socket listening on %s:%s\n",
 84		       control_host, control_port);
 85		fflush(stdout);
 86
 87		control_fd = accept(fd, NULL, 0);
 88		close(fd);
 89
 90		if (control_fd < 0) {
 91			perror("accept");
 92			exit(EXIT_FAILURE);
 93		}
 94		printf("Control socket connection accepted...\n");
 95		break;
 96
 97next:
 98		close(fd);
 99	}
100
101	if (control_fd < 0) {
102		fprintf(stderr, "Control socket initialization failed.  Invalid address %s:%s?\n",
103			control_host, control_port);
104		exit(EXIT_FAILURE);
105	}
106
107	freeaddrinfo(result);
108}
109
110/* Free resources */
111void control_cleanup(void)
112{
113	close(control_fd);
114	control_fd = -1;
115}
116
117/* Write a line to the control socket */
118void control_writeln(const char *str)
119{
120	ssize_t len = strlen(str);
121	ssize_t ret;
122
123	timeout_begin(TIMEOUT);
124
125	do {
126		ret = send(control_fd, str, len, MSG_MORE);
127		timeout_check("send");
128	} while (ret < 0 && errno == EINTR);
129
130	if (ret != len) {
131		perror("send");
132		exit(EXIT_FAILURE);
133	}
134
135	do {
136		ret = send(control_fd, "\n", 1, 0);
137		timeout_check("send");
138	} while (ret < 0 && errno == EINTR);
139
140	if (ret != 1) {
141		perror("send");
142		exit(EXIT_FAILURE);
143	}
144
145	timeout_end();
146}
147
148/* Return the next line from the control socket (without the trailing newline).
149 *
150 * The program terminates if a timeout occurs.
151 *
152 * The caller must free() the returned string.
153 */
154char *control_readln(void)
155{
156	char *buf = NULL;
157	size_t idx = 0;
158	size_t buflen = 0;
159
160	timeout_begin(TIMEOUT);
161
162	for (;;) {
163		ssize_t ret;
164
165		if (idx >= buflen) {
166			char *new_buf;
167
168			new_buf = realloc(buf, buflen + 80);
169			if (!new_buf) {
170				perror("realloc");
171				exit(EXIT_FAILURE);
172			}
173
174			buf = new_buf;
175			buflen += 80;
176		}
177
178		do {
179			ret = recv(control_fd, &buf[idx], 1, 0);
180			timeout_check("recv");
181		} while (ret < 0 && errno == EINTR);
182
183		if (ret == 0) {
184			fprintf(stderr, "unexpected EOF on control socket\n");
185			exit(EXIT_FAILURE);
186		}
187
188		if (ret != 1) {
189			perror("recv");
190			exit(EXIT_FAILURE);
191		}
192
193		if (buf[idx] == '\n') {
194			buf[idx] = '\0';
195			break;
196		}
197
198		idx++;
199	}
200
201	timeout_end();
202
203	return buf;
204}
205
206/* Wait until a given line is received or a timeout occurs */
207void control_expectln(const char *str)
208{
209	char *line;
210
211	line = control_readln();
212	if (strcmp(str, line) != 0) {
213		fprintf(stderr, "expected \"%s\" on control socket, got \"%s\"\n",
214			str, line);
215		exit(EXIT_FAILURE);
216	}
217
218	free(line);
219}