Blame gnulib-tests/test-select.h

Packit 33f14e
/* Test of select() substitute.
Packit 33f14e
   Copyright (C) 2008-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Written by Paolo Bonzini, 2008.  */
Packit 33f14e
Packit 33f14e
#include <stdio.h>
Packit 33f14e
#include <string.h>
Packit 33f14e
#include <netinet/in.h>
Packit 33f14e
#include <arpa/inet.h>
Packit 33f14e
#include <unistd.h>
Packit 33f14e
#include <fcntl.h>
Packit 33f14e
#include <stdlib.h>
Packit 33f14e
#include <stdbool.h>
Packit 33f14e
#include <sys/ioctl.h>
Packit 33f14e
#include <errno.h>
Packit 33f14e
Packit 33f14e
#include "macros.h"
Packit 33f14e
Packit 33f14e
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 33f14e
# define WINDOWS_NATIVE
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#ifdef HAVE_SYS_WAIT_H
Packit 33f14e
# include <sys/wait.h>
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#ifndef SO_REUSEPORT
Packit 33f14e
# define SO_REUSEPORT    SO_REUSEADDR
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#define TEST_PORT       12345
Packit 33f14e
Packit 33f14e
Packit 33f14e
typedef int (*select_fn) (int, fd_set *, fd_set *, fd_set *, struct timeval *);
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Minimal testing infrastructure.  */
Packit 33f14e
Packit 33f14e
static int failures;
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
failed (const char *reason)
Packit 33f14e
{
Packit 33f14e
  if (++failures > 1)
Packit 33f14e
    printf ("  ");
Packit 33f14e
  printf ("failed (%s)\n", reason);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
test (void (*fn) (select_fn), select_fn my_select, const char *msg)
Packit 33f14e
{
Packit 33f14e
  failures = 0;
Packit 33f14e
  printf ("%s... ", msg);
Packit 33f14e
  fflush (stdout);
Packit 33f14e
  fn (my_select);
Packit 33f14e
Packit 33f14e
  if (!failures)
Packit 33f14e
    printf ("passed\n");
Packit 33f14e
Packit 33f14e
  return failures;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Funny socket code.  */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
open_server_socket (void)
Packit 33f14e
{
Packit 33f14e
  int s, x;
Packit 33f14e
  struct sockaddr_in ia;
Packit 33f14e
Packit 33f14e
  s = socket (AF_INET, SOCK_STREAM, 0);
Packit 33f14e
Packit 33f14e
  x = 1;
Packit 33f14e
  setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x));
Packit 33f14e
Packit 33f14e
  memset (&ia, 0, sizeof (ia));
Packit 33f14e
  ia.sin_family = AF_INET;
Packit 33f14e
  inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr);
Packit 33f14e
  ia.sin_port = htons (TEST_PORT);
Packit 33f14e
  if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0)
Packit 33f14e
    {
Packit 33f14e
      perror ("bind");
Packit 33f14e
      exit (77);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (listen (s, 1) < 0)
Packit 33f14e
    {
Packit 33f14e
      perror ("listen");
Packit 33f14e
      exit (77);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  return s;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
connect_to_socket (bool blocking)
Packit 33f14e
{
Packit 33f14e
  int s;
Packit 33f14e
  struct sockaddr_in ia;
Packit 33f14e
Packit 33f14e
  s = socket (AF_INET, SOCK_STREAM, 0);
Packit 33f14e
Packit 33f14e
  memset (&ia, 0, sizeof (ia));
Packit 33f14e
  ia.sin_family = AF_INET;
Packit 33f14e
  inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr);
Packit 33f14e
  ia.sin_port = htons (TEST_PORT);
Packit 33f14e
Packit 33f14e
  if (!blocking)
Packit 33f14e
    {
Packit 33f14e
#ifdef WINDOWS_NATIVE
Packit 33f14e
      unsigned long iMode = 1;
Packit 33f14e
      ioctl (s, FIONBIO, (char *) &iMode);
Packit 33f14e
Packit 33f14e
#elif defined F_GETFL
Packit 33f14e
      int oldflags = fcntl (s, F_GETFL, NULL);
Packit 33f14e
Packit 33f14e
      if (!(oldflags & O_NONBLOCK))
Packit 33f14e
        fcntl (s, F_SETFL, oldflags | O_NONBLOCK);
Packit 33f14e
#endif
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0
Packit 33f14e
      && (blocking || errno != EINPROGRESS))
Packit 33f14e
    {
Packit 33f14e
      perror ("connect");
Packit 33f14e
      exit (77);
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  return s;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* A slightly more convenient interface to select(2).
Packit 33f14e
   Waits until a specific event occurs on a file descriptor FD.
Packit 33f14e
   EV is a bit mask of events to look for:
Packit 33f14e
     SEL_IN - input can be polled without blocking,
Packit 33f14e
     SEL_OUT - output can be provided without blocking,
Packit 33f14e
     SEL_EXC - an exception occurred,
Packit 33f14e
   A maximum wait time is specified by TIMEOUT.
Packit 33f14e
   *TIMEOUT = { 0, 0 } means to return immediately,
Packit 33f14e
   TIMEOUT = NULL means to wait indefinitely.  */
Packit 33f14e
Packit 33f14e
enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 };
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select (int fd, int ev, struct timeval *timeout, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  fd_set rfds, wfds, xfds;
Packit 33f14e
  int r, rev;
Packit 33f14e
Packit 33f14e
  FD_ZERO (&rfds);
Packit 33f14e
  FD_ZERO (&wfds);
Packit 33f14e
  FD_ZERO (&xfds);
Packit 33f14e
  if (ev & SEL_IN)
Packit 33f14e
    FD_SET (fd, &rfds);
Packit 33f14e
  if (ev & SEL_OUT)
Packit 33f14e
    FD_SET (fd, &wfds);
Packit 33f14e
  if (ev & SEL_EXC)
Packit 33f14e
    FD_SET (fd, &xfds);
Packit 33f14e
  r = my_select (fd + 1, &rfds, &wfds, &xfds, timeout);
Packit 33f14e
  if (r < 0)
Packit 33f14e
    return r;
Packit 33f14e
Packit 33f14e
  rev = 0;
Packit 33f14e
  if (FD_ISSET (fd, &rfds))
Packit 33f14e
    rev |= SEL_IN;
Packit 33f14e
  if (FD_ISSET (fd, &wfds))
Packit 33f14e
    rev |= SEL_OUT;
Packit 33f14e
  if (FD_ISSET (fd, &xfds))
Packit 33f14e
    rev |= SEL_EXC;
Packit 33f14e
  if (rev && r == 0)
Packit 33f14e
    failed ("select returned 0");
Packit 33f14e
  if (rev & ~ev)
Packit 33f14e
    failed ("select returned unrequested events");
Packit 33f14e
Packit 33f14e
  return rev;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select_nowait (int fd, int ev, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  struct timeval tv0;
Packit 33f14e
  tv0.tv_sec = 0;
Packit 33f14e
  tv0.tv_usec = 0;
Packit 33f14e
  return do_select (fd, ev, &tv0, my_select);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select_wait (int fd, int ev, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  return do_select (fd, ev, NULL, my_select);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Test select(2) for TTYs.  */
Packit 33f14e
Packit 33f14e
#ifdef INTERACTIVE
Packit 33f14e
static void
Packit 33f14e
test_tty (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  if (do_select_nowait (0, SEL_IN, my_select) != 0)
Packit 33f14e
    failed ("can read");
Packit 33f14e
  if (do_select_nowait (0, SEL_OUT, my_select) == 0)
Packit 33f14e
    failed ("cannot write");
Packit 33f14e
Packit 33f14e
  if (do_select_wait (0, SEL_IN, my_select) == 0)
Packit 33f14e
    failed ("return with infinite timeout");
Packit 33f14e
Packit 33f14e
  getchar ();
Packit 33f14e
  if (do_select_nowait (0, SEL_IN, my_select) != 0)
Packit 33f14e
    failed ("can read after getc");
Packit 33f14e
}
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select_bad_nfd_nowait (int nfd, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  struct timeval tv0;
Packit 33f14e
  tv0.tv_sec = 0;
Packit 33f14e
  tv0.tv_usec = 0;
Packit 33f14e
  errno = 0;
Packit 33f14e
  return my_select (nfd, NULL, NULL, NULL, &tv0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_bad_nfd (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  if (do_select_bad_nfd_nowait (-1, my_select) != -1 || errno != EINVAL)
Packit 33f14e
    failed ("invalid errno after negative nfds");
Packit 33f14e
  /* Can't test FD_SETSIZE + 1 for EINVAL, since some systems allow
Packit 33f14e
     dynamically larger set size by redefining FD_SETSIZE anywhere up
Packit 33f14e
     to the actual maximum fd.  */
Packit 33f14e
  /* if (do_select_bad_nfd_nowait (FD_SETSIZE + 1, my_select) != -1 */
Packit 33f14e
  /*     || errno != EINVAL) */
Packit 33f14e
  /*   failed ("invalid errno after bogus nfds"); */
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Test select(2) on invalid file descriptors.  */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select_bad_fd (int fd, int ev, struct timeval *timeout, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  fd_set rfds, wfds, xfds;
Packit 33f14e
Packit 33f14e
  FD_ZERO (&rfds);
Packit 33f14e
  FD_ZERO (&wfds);
Packit 33f14e
  FD_ZERO (&xfds);
Packit 33f14e
  if (ev & SEL_IN)
Packit 33f14e
    FD_SET (fd, &rfds);
Packit 33f14e
  if (ev & SEL_OUT)
Packit 33f14e
    FD_SET (fd, &wfds);
Packit 33f14e
  if (ev & SEL_EXC)
Packit 33f14e
    FD_SET (fd, &xfds);
Packit 33f14e
  errno = 0;
Packit 33f14e
  return my_select (fd + 1, &rfds, &wfds, &xfds, timeout);
Packit 33f14e
  /* In this case, when fd is invalid, on some platforms, the bit for fd
Packit 33f14e
     is left alone in the fd_set, whereas on other platforms it is cleared.
Packit 33f14e
     So, don't check the bit for fd here.  */
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
do_select_bad_fd_nowait (int fd, int ev, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  struct timeval tv0;
Packit 33f14e
  tv0.tv_sec = 0;
Packit 33f14e
  tv0.tv_usec = 0;
Packit 33f14e
  return do_select_bad_fd (fd, ev, &tv0, my_select);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_bad_fd (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  /* This tests fails on OSF/1 and native Windows, even with fd = 16.  */
Packit 33f14e
#if !(defined __osf__ || defined WINDOWS_NATIVE)
Packit 33f14e
  int fd;
Packit 33f14e
Packit 33f14e
  /* On Linux, Mac OS X, *BSD, values of fd like 99 or 399 are discarded
Packit 33f14e
     by the kernel early and therefore do *not* lead to EBADF, as required
Packit 33f14e
     by POSIX.  */
Packit 33f14e
# if defined __linux__ || (defined __APPLE__ && defined __MACH__) || (defined __FreeBSD__ || defined __DragonFly__) || defined __OpenBSD__ || defined __NetBSD__
Packit 33f14e
  fd = 14;
Packit 33f14e
# else
Packit 33f14e
  fd = 99;
Packit 33f14e
# endif
Packit 33f14e
  close (fd);
Packit 33f14e
Packit 33f14e
  if (do_select_bad_fd_nowait (fd, SEL_IN, my_select) == 0 || errno != EBADF)
Packit 33f14e
    failed ("invalid fd among rfds");
Packit 33f14e
  if (do_select_bad_fd_nowait (fd, SEL_OUT, my_select) == 0 || errno != EBADF)
Packit 33f14e
    failed ("invalid fd among wfds");
Packit 33f14e
  if (do_select_bad_fd_nowait (fd, SEL_EXC, my_select) == 0 || errno != EBADF)
Packit 33f14e
    failed ("invalid fd among xfds");
Packit 33f14e
#endif
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Test select(2) for unconnected nonblocking sockets.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_connect_first (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  int s = open_server_socket ();
Packit 33f14e
  struct sockaddr_in ia;
Packit 33f14e
  socklen_t addrlen;
Packit 33f14e
Packit 33f14e
  int c1, c2;
Packit 33f14e
Packit 33f14e
  if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != 0)
Packit 33f14e
    failed ("can read, socket not connected");
Packit 33f14e
Packit 33f14e
  c1 = connect_to_socket (false);
Packit 33f14e
Packit 33f14e
  if (do_select_wait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN)
Packit 33f14e
    failed ("expecting readability on passive socket");
Packit 33f14e
  if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN)
Packit 33f14e
    failed ("expecting readability on passive socket");
Packit 33f14e
Packit 33f14e
  addrlen = sizeof (ia);
Packit 33f14e
  c2 = accept (s, (struct sockaddr *) &ia, &addrlen);
Packit 33f14e
  ASSERT (close (s) == 0);
Packit 33f14e
  ASSERT (close (c1) == 0);
Packit 33f14e
  ASSERT (close (c2) == 0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Test select(2) for unconnected blocking sockets.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_accept_first (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
#ifndef WINDOWS_NATIVE
Packit 33f14e
  int s = open_server_socket ();
Packit 33f14e
  struct sockaddr_in ia;
Packit 33f14e
  socklen_t addrlen;
Packit 33f14e
  char buf[3];
Packit 33f14e
  int c, pid;
Packit 33f14e
Packit 33f14e
  pid = fork ();
Packit 33f14e
  if (pid < 0)
Packit 33f14e
    return;
Packit 33f14e
Packit 33f14e
  if (pid == 0)
Packit 33f14e
    {
Packit 33f14e
      addrlen = sizeof (ia);
Packit 33f14e
      c = accept (s, (struct sockaddr *) &ia, &addrlen);
Packit 33f14e
      ASSERT (close (s) == 0);
Packit 33f14e
      ASSERT (write (c, "foo", 3) == 3);
Packit 33f14e
      ASSERT (read (c, buf, 3) == 3);
Packit 33f14e
      shutdown (c, SHUT_RD);
Packit 33f14e
      ASSERT (close (c) == 0);
Packit 33f14e
      exit (0);
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      ASSERT (close (s) == 0);
Packit 33f14e
      c = connect_to_socket (true);
Packit 33f14e
      if (do_select_nowait (c, SEL_OUT, my_select) != SEL_OUT)
Packit 33f14e
        failed ("cannot write after blocking connect");
Packit 33f14e
      ASSERT (write (c, "foo", 3) == 3);
Packit 33f14e
      wait (&pid;;
Packit 33f14e
      if (do_select_wait (c, SEL_IN, my_select) != SEL_IN)
Packit 33f14e
        failed ("cannot read data left in the socket by closed process");
Packit 33f14e
      ASSERT (read (c, buf, 3) == 3);
Packit 33f14e
      ASSERT (write (c, "foo", 3) == 3);
Packit 33f14e
      (void) close (c); /* may fail with errno = ECONNRESET */
Packit 33f14e
    }
Packit 33f14e
#endif
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Common code for pipes and connected sockets.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_pair (int rd, int wd, select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  char buf[3];
Packit 33f14e
  if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT)
Packit 33f14e
    failed ("expecting writability before writing");
Packit 33f14e
  if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT)
Packit 33f14e
    failed ("expecting writability before writing");
Packit 33f14e
Packit 33f14e
  ASSERT (write (wd, "foo", 3) == 3);
Packit 33f14e
  if (do_select_wait (rd, SEL_IN, my_select) != SEL_IN)
Packit 33f14e
    failed ("expecting readability after writing");
Packit 33f14e
  if (do_select_nowait (rd, SEL_IN, my_select) != SEL_IN)
Packit 33f14e
    failed ("expecting readability after writing");
Packit 33f14e
Packit 33f14e
  ASSERT (read (rd, buf, 3) == 3);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Test select(2) on connected sockets.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_socket_pair (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  struct sockaddr_in ia;
Packit 33f14e
Packit 33f14e
  socklen_t addrlen = sizeof (ia);
Packit 33f14e
  int s = open_server_socket ();
Packit 33f14e
  int c1 = connect_to_socket (false);
Packit 33f14e
  int c2 = accept (s, (struct sockaddr *) &ia, &addrlen);
Packit 33f14e
Packit 33f14e
  ASSERT (close (s) == 0);
Packit 33f14e
Packit 33f14e
  test_pair (c1, c2, my_select);
Packit 33f14e
  ASSERT (close (c1) == 0);
Packit 33f14e
  ASSERT (write (c2, "foo", 3) == 3);
Packit 33f14e
  (void) close (c2); /* may fail with errno = ECONNRESET */
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Test select(2) on pipes.  */
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_pipe (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  int fd[2];
Packit 33f14e
Packit 33f14e
  ASSERT (pipe (fd) == 0);
Packit 33f14e
  test_pair (fd[0], fd[1], my_select);
Packit 33f14e
  ASSERT (close (fd[0]) == 0);
Packit 33f14e
  ASSERT (close (fd[1]) == 0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
Packit 33f14e
/* Do them all.  */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
test_function (select_fn my_select)
Packit 33f14e
{
Packit 33f14e
  int result = 0;
Packit 33f14e
Packit 33f14e
#ifdef INTERACTIVE
Packit 33f14e
  printf ("Please press Enter\n");
Packit 33f14e
  test (test_tty, "TTY", my_select);
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
  result += test (test_bad_nfd, my_select, "Invalid nfd test");
Packit 33f14e
  result += test (test_bad_fd, my_select, "Invalid fd test");
Packit 33f14e
  result += test (test_connect_first, my_select, "Unconnected socket test");
Packit 33f14e
  result += test (test_socket_pair, my_select, "Connected sockets test");
Packit 33f14e
  result += test (test_accept_first, my_select, "General socket test with fork");
Packit 33f14e
  result += test (test_pipe, my_select, "Pipe test");
Packit 33f14e
Packit 33f14e
  return result;
Packit 33f14e
}