/* FDs over sockets, clagged from http://www.mail-archive.com/kragen-hacks@canonical.org/msg00002.html */
/* Substantially modified with code from http://docs.hp.com/en/B2355-90136/ch07s06.html */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <poll.h>
#include <string.h>

#define bzero(ptr, len)  memset((void *)(ptr), '\0', (len))

static int server_socket_fd = -1;
static struct sockaddr_un server_socket_name = {0}, client_socket_name = {0};
socklen_t client_socket_len;

static int send_connection(int fd)
{
	struct msghdr msg = {0};
	char ccmsg[CMSG_SPACE(sizeof(fd))];
	struct cmsghdr *cmsg;
	struct iovec vec;
	char str[2];
	int rv;

	str[0] = 'x';
	str[1] = 'y';

	msg.msg_name = &client_socket_name;
	msg.msg_namelen = client_socket_len;

	vec.iov_base = str;
	vec.iov_len = 2;
	msg.msg_iov = &vec;
	msg.msg_iovlen = 1;

	/* old BSD implementations should use msg_accrights instead of 
	 * msg_control; the interface is different. */
	msg.msg_control = ccmsg;
	msg.msg_controllen = sizeof(ccmsg);
	cmsg = CMSG_FIRSTHDR(&msg);
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
	*((int*)CMSG_DATA(cmsg)) = fd;
	msg.msg_controllen = sizeof(ccmsg); //cmsg->cmsg_len;

	rv = sendmsg(server_socket_fd, &msg, 0);
	return rv;
}

static unsigned char
wait_for_request(void)
{
	/*struct pollfd sockpollfd = {
		.fd = unix_socket_fd,
		.events = POLLIN
	};
	*/
	
	unsigned char buf[1];
	client_socket_len = sizeof(struct sockaddr_un);
	/* poll(&sockpollfd, 1, -1);*/
	
	if(recvfrom(server_socket_fd, buf, 1, 0, (void *)&client_socket_name, &client_socket_len) != 1) {
		perror("wait_for_request");
		return '\0';
	}

	return buf[0];
}

static int open_unix_fd(char *path)
{
	server_socket_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (server_socket_fd == -1) return 0;

	bzero(&server_socket_name, sizeof(server_socket_name));
	server_socket_name.sun_family = AF_UNIX;
	strcpy(server_socket_name.sun_path, path);

	if(bind(server_socket_fd, (void *)&server_socket_name, sizeof(server_socket_name)) == -1) {
		close(server_socket_fd);
		return 0;
	}

	return 1;
}

static void maybeclose(int fd)
{
	if (fd != -1) close(fd);
}

static void close_unix_fd()
{
	maybeclose(server_socket_fd);
	server_socket_fd = -1;
}

static int
open_fd(char *filename)
{
	return open(filename, O_RDONLY);
}

int run(char *filename, char *socketpath)
{
	int source_fd = -1;

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

	source_fd = open_fd(filename);
	if (source_fd < 0) {
		perror("opening file");
		goto error;
	}

	if (!open_unix_fd(socketpath)) {
		perror("opening Unix fd");
		goto error;
	}

	while (1) {
		wait_for_request();

		/* Send the FD */
		if (send_connection(source_fd) == -1) {
			perror("sending");
			//sleep(1);
		}
	}

error:
	maybeclose(source_fd);
	close_unix_fd();
	return 0;
}

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

