/* FDs over sockets, clagged from http://www.mail-archive.com/kragen-hacks@canonical.org/msg00002.html */

/* hello, world program that works with portlisten.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

struct sockaddr_un client_socket_name = {0}, server_socket_name = {0};

static int
create_socket_name(char *path, struct sockaddr_un *socket_name)
{
	socket_name->sun_family = AF_UNIX;

	if (strlen(path) >= sizeof(socket_name->sun_path)) return -1;
	strcpy(socket_name->sun_path, path);

	return 0;
}

/* open a PF_UNIX SOCK_DGRAM socket bound to 'path' */
static int sock_dgram(char *path, struct sockaddr_un *socket_name)
{
	int fd;

	if (unlink(path) < 0) {
		if (errno != ENOENT) {
			return -1;
		}
	}

	if(create_socket_name(path, socket_name) != 0) {
		return -1;
	}

	fd = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (fd == -1) {
		return -1;
	}
	if (bind(fd, (void *)socket_name, sizeof(struct sockaddr_un)) == -1) {
		close(fd);
		return -1;
	}
	return fd;
}

/* receive a file descriptor over file descriptor fd */
static int receive_fd(int fd)
{
	struct msghdr msg;
	struct iovec iov;
	char buf[1];
	int rv;
	int connfd = -1;
	char ccmsg[CMSG_SPACE(sizeof(connfd))];
	struct cmsghdr *cmsg;

	iov.iov_base = buf;
	iov.iov_len = 1;

	msg.msg_name = 0;
	msg.msg_namelen = 0;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	msg.msg_control = ccmsg;
	msg.msg_controllen = sizeof(ccmsg); 

	rv = recvmsg(fd, &msg, 0);
	if (rv == -1) {
		perror("recvmsg");
		return -1;
	}

	cmsg = CMSG_FIRSTHDR(&msg);
	if (!cmsg->cmsg_type == SCM_RIGHTS) {
		fprintf(stderr, "got control message of unknown type %d\n", 
				cmsg->cmsg_type);
		return -1;
	}
	return *(int*)CMSG_DATA(cmsg);
}

static void
send_request(int sender_socket, struct sockaddr_un *dest, unsigned char request)
{
	struct iovec iov = {
		.iov_base = &request,
		.iov_len = 1
	};

	struct msghdr msg = {
		.msg_name = dest,
		.msg_namelen = sizeof(struct sockaddr_un),
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = 0,
		.msg_controllen = 0,
		.msg_flags = 0
	};

	sendmsg(sender_socket, &msg, 0);
}

void run(char *serversock, char *clientsock) {
	char buf[5];

	if (create_socket_name(serversock, &server_socket_name) != 0) {
		perror("create_socket_name");
		return;
	}

	int client_fd = sock_dgram(clientsock, &client_socket_name);
	if (client_fd == -1) {
		perror("sock_dgram");
		return;
	}


	send_request(client_fd, &server_socket_name, '1');

	int connfd = receive_fd(client_fd);
	if (connfd == -1) {
		printf("fail\n");
		return;
	}
	read(connfd, buf, 4);
	buf[4] = '\0';
	printf("%s\n", buf);
	close(connfd);
}

int main(int argc, char **argv) {
	if (argc != 3) {
		fprintf(stderr, "Usage: %s serversock clientsock\n", argv[0]);
		return -1;
	}
	run(argv[1], argv[2]);
	return -1;
}

