#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <assert.h>
#include <signal.h>
#include <sys/wait.h>

static void __attribute__ ((noreturn)) usage(const char *progname)
{
  fprintf(stderr, "%s: [-v] [-c 16] [-b 256] [-i 100] [-f]\n", progname);
  exit(1);
}


int main(int argc, char *argv[])
{
  int verbose = 0;
  int flags = 0;
  unsigned long vec_size = 16;
  unsigned long block_size = 256;
  unsigned long iterations = 100;
  int callval_i;
  pid_t child_pid;
  int spfd[2];
  int ii;
  
  struct msghdr message = {};
  
  while ((callval_i = getopt(argc, argv, "vc:b:i:f")) >= 0)
    {
      switch (callval_i)
        {
        case 'v':
          verbose++;
          break;
          
        case 'c':
          vec_size = strtoul(optarg, NULL, 0);
          break;
          
        case 'b':
          block_size = strtoul(optarg, NULL, 0);
          break;
          
        case 'i':
          iterations = strtoul(optarg, NULL, 0);
          break;
          
        case 'f':
          flags |= 0x80000000U;
          break;
          
        case ':':
        case '?':
        default:
          usage(argv[0]);
        }
    }
  
  if (optind != argc)
    {
      usage(argv[0]);
    }
  
  callval_i = socketpair(AF_UNIX, SOCK_DGRAM, 0, spfd);
  if (callval_i < 0)
    {
      perror("socketpair");
      exit(1);
    }
  
  printf("iterations=%lu vec_size=%lu block_size=%lu flags=%.8x\n",
         iterations, vec_size, block_size, flags);
  
  message.msg_iovlen = vec_size;
  message.msg_iov = calloc(vec_size, sizeof(struct iovec));
  assert(message.msg_iov);
  
  for (ii=0; ii<vec_size; ii++)
    {
      message.msg_iov[ii].iov_len  = block_size;
      message.msg_iov[ii].iov_base = malloc(block_size);
      assert(message.msg_iov[ii].iov_base);
    }
  
  child_pid = fork();
  
  if (child_pid < 0)
    {
      perror("fork");
      exit(1);
    }
  else if (child_pid > 0)
    {
      close(spfd[1]);
      while (iterations--)
        {
          callval_i = sendmsg(spfd[0], &message, flags);
          if (callval_i <= 0)
            {
              perror("sendmsg");
              kill(child_pid, SIGTERM);
              break;
            }
        }
      close(spfd[0]);
      waitpid(child_pid, NULL, 0);
    }
  else
    {
      unsigned long messages = iterations * vec_size;
      
      close(spfd[0]);
      while (messages > 0)
        {
          callval_i = recvmsg(spfd[1], &message, flags);
          if (callval_i <= 0)
            {
              perror("recvmsg");
              break;
            }
          messages -= callval_i/block_size;
        }
      close(spfd[1]);
    }
  
  exit(0);
}
