linux/tools/testing/vsock/control.c
<<
>>
Prefs
   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}
 220