#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/un.h>

#include <sys/time.h>
#include <time.h>

#include <fcntl.h>

#include <errno.h>

#include <signal.h>

static void handle_connections(int listener);
static void connect_to_server();

#define die(x) do { perror(x); exit(EXIT_FAILURE); } while(0)

#define SOCKET "/tmp/nblock.sock"

int main(int ac, char **av)
{
  struct sockaddr_un addr;
  int listener;
  pid_t pid;

  // create, bind, listen socket
  listener= socket(PF_UNIX, SOCK_STREAM, 0);
  if (listener < 0)
    die("failed to create socket for listener");

  addr.sun_family= AF_UNIX;
  strcpy(addr.sun_path, SOCKET);

  (void)unlink(SOCKET);

  if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) != 0)
    die("unable to bind to socket");

  if (listen(listener, 5) != 0)
    die("failed to listen on socket");

  // fork
  pid= fork();
  if (pid < 0)
    die("failed to fork");
  else if (pid > 0)
    handle_connections(listener);
  else
    connect_to_server();

  exit(EXIT_SUCCESS);
}

/* Only handles one connection at a time, for now. */
void handle_connections(int listener)
{
  while (1)
  {
    int sock= accept(listener, NULL, 0);
    if (sock > 0)
    {
      int blocking= 0;
      /* Set O_NONBLOCK on socket. */
      if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0)
        die("failed to set O_NONBLOCK");

      while (1)
      {
        struct timeval before, after;
        signed long diff;
        char buff[512];

        if (gettimeofday(&before, NULL) < 0)
          die("gettimeofday before read failed");

        int bytes= (int)read(sock, buff, sizeof(buff));

        if (bytes < 0 && errno != EAGAIN)
          die("unexpected error from read");

        if (gettimeofday(&after, NULL) < 0)
          die("gettimeofday after read failed");

        diff= 1000000 * (after.tv_sec - before.tv_sec);
        diff+= after.tv_usec - before.tv_usec;

        if (diff > 20 || bytes > 0)
          printf("read of %s (%d) took %ldmsec\n",
                 bytes > 0 ? buff : "(null)",
                 bytes, diff);

        usleep(100);
      }
    }
  }
  exit(EXIT_SUCCESS);
}

#undef die
#define die(x) do { ec= EXIT_FAILURE; perror(x); goto err; } while(0)

void connect_to_server()
{
  int i, ec= EXIT_SUCCESS;
  struct sockaddr_un addr;
  int sock= socket(PF_UNIX, SOCK_STREAM, 0);

  addr.sun_family= AF_UNIX;
  strcpy(addr.sun_path, SOCKET);

  if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
    die("failed to connect to socket");

  sleep(5);

  for (i= 0; i < 10; i++)
  {
    char buff[512];
    sprintf(buff, "this is try #%d", i);
    if (write(sock, buff, strlen(buff)) < 0)
      die("failed to write data");
    usleep(200);
  }

  /* Give parent time to read data. */
  sleep(5);

err:
  close(sock);

  /* Kill our parent. */
  (void)kill(getppid(), SIGTERM);
  exit(ec);
}
