
/* On AIX 5.2, _LINUX_SOURCE_COMPAT needs to be defined when reading
   sys/socket.h in order to make CMSG_SPACE and CMSG_LEN visible. */

#if defined(_AIX)
#define _LINUX_SOURCE_COMPAT 1
#endif

#include <sys/socket.h>

#if defined(_AIX)
#undef _LINUX_SOURCE_COMPAT
#endif


/* Needed for 'memset' on AIX 5.2 */
#if defined(_AIX)
# include <memory.h>
#endif

#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include "fdleak.h"

char filea[24];
char fileb[24];
char sock[24];

void server (void)
{
   int s, fd1, fd2;
   struct sockaddr_un addr;
   
   fd1 = DO( open(filea, O_RDWR | O_CREAT | O_TRUNC, 0750) );
   fd2 = DO( open(fileb, O_RDWR | O_CREAT | O_TRUNC, 0750) );
   s   = DO( socket(PF_UNIX, SOCK_STREAM, 0) );

   memset(&addr, 0, sizeof(addr));
   addr.sun_family = AF_UNIX;
   sprintf(addr.sun_path, "%s", sock);

   unlink(sock);
   DO( bind(s, (struct sockaddr *)&addr, sizeof(addr)) );
   DO( listen(s, 5) );

   {
      int x;
      int baddrsize = 0;
      struct sockaddr_un baddr;
      struct msghdr msg = {NULL, 0, NULL, 0, 0, 0, 0};
      struct cmsghdr *cmsg;
      char buf[CMSG_SPACE(sizeof(int) * 2)];
      struct iovec iov[1];

      memset(&baddr, 0, sizeof(baddr));
      x = DO( accept(s, (struct sockaddr *)&baddr, &baddrsize) );

      msg.msg_control = buf;
      msg.msg_controllen = sizeof(buf);
      cmsg = CMSG_FIRSTHDR(&msg);
      cmsg->cmsg_level = SOL_SOCKET;
      cmsg->cmsg_type = SCM_RIGHTS;
      cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 2);
      ((int *)CMSG_DATA(cmsg))[0] = fd1;
      ((int *)CMSG_DATA(cmsg))[1] = fd2;

      iov[0].iov_base = "hello";
      iov[0].iov_len = 6;

      msg.msg_iov = iov;
      msg.msg_iovlen = 1;

      DO( sendmsg(x, &msg, 0) );
   }
}

void client (void)
{
   int s, fd1 = -1, fd2 = -1, size, count = 0, ret;
   struct sockaddr_un addr;
   struct iovec iov[1];
   union {
      struct cmsghdr cm;
      char control[CMSG_SPACE(sizeof(int) * 2)];
   } control_un;
   struct msghdr msg = { NULL, 0, iov, 1, control_un.control,
                         sizeof(control_un), 0 };
   struct cmsghdr *cmsg = &control_un.cm;
   char buf[1024];

   iov[0].iov_base = buf;
   iov[0].iov_len = sizeof(buf);

   s = socket(PF_UNIX, SOCK_STREAM, 0);
   if (s == -1) {
      perror("socket");
      exit(1);
   }

   addr.sun_family = AF_UNIX;
   sprintf(addr.sun_path, "%s", sock);

   do {
     count++;
     ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
     if (ret == -1) sleep(1);
   } while (count < 10 && ret == -1);

   if (ret == -1) {
      perror("connect");
      exit(1);
   }

  again:
   if ((size = recvmsg(s, &msg, 0)) == -1) {
      if (errno == EINTR)
	 goto again;		/* SIGCHLD from server exiting could interrupt */
      perror("recvmsg");
      exit(1);
   }


   cmsg = CMSG_FIRSTHDR(&msg);
   while (cmsg) {
      if (cmsg->cmsg_level == SOL_SOCKET &&
         cmsg->cmsg_type == SCM_RIGHTS &&
         cmsg->cmsg_len == CMSG_LEN(sizeof(int) * 2)) {
         fd1 = ((int *)CMSG_DATA(cmsg))[0];
         fd2 = ((int *)CMSG_DATA(cmsg))[1];
      }

      cmsg = CMSG_NXTHDR(&msg, cmsg);
   }

   if (fd1 != -1) write(fd1, "Yeah 1\n", 8);
   if (fd2 != -1) write(fd2, "Yeah 2\n", 8);
}


int main (int argc, char **argv)
{
   int pid, status;

   CLOSE_INHERITED_FDS;

   pid = getpid();
   sprintf(filea, "/tmp/data1.%d", pid);
   sprintf(fileb, "/tmp/data2.%d", pid);
   sprintf(sock, "/tmp/sock.%d", pid);

   if ((pid = fork()) == 0) {
      server();
      return 0;
   }

   client();

   wait(&status);

   DO( unlink(filea) );
   DO( unlink(fileb) );
   DO( unlink(sock) );
   return 0;
}
