--- /dev/null
+../../licenses/CC0
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * fdpass - routines to pass a file descriptor over a socket.
+ *
+ * This code handles all the hairy details of fd passing.
+ *
+ * License: CC0 (Public domain)
+ * Maintainer: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ * // Outputs hello!
+ * #include <ccan/fdpass/fdpass.h>
+ * #include <sys/socket.h>
+ * #include <sys/un.h>
+ * #include <stdio.h>
+ * #include <stdlib.h>
+ * #include <unistd.h>
+ *
+ * static void child(int sockfd)
+ * {
+ * char buffer[6];
+ * int newfd = fdpass_recv(sockfd);
+ * read(newfd, buffer, sizeof(buffer));
+ * printf("%.*s\n", (int)sizeof(buffer), buffer);
+ * exit(0);
+ * }
+ *
+ * static void parent(int sockfd)
+ * {
+ * int pfds[2];
+ *
+ * pipe(pfds);
+ * fdpass_send(sockfd, pfds[0]);
+ * close(pfds[0]);
+ * write(pfds[1], "hello!", 6);
+ * exit(0);
+ * }
+ *
+ * int main(void)
+ * {
+ * int sv[2];
+ *
+ * socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ * if (fork() == 0)
+ * child(sv[0]);
+ * else
+ * parent(sv[1]);
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0)
+ return 0;
+
+ return 1;
+}
+
+
--- /dev/null
+/* CC0 license (public domain) - see LICENSE file for details */
+#include <ccan/fdpass/fdpass.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+
+bool fdpass_send(int sockout, int fd)
+{
+ /* From the cmsg(3) manpage: */
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ char c = 0;
+ union { /* Ancillary data buffer, wrapped in a union
+ in order to ensure it is suitably aligned */
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr align;
+ } u;
+
+ msg.msg_control = u.buf;
+ msg.msg_controllen = sizeof(u.buf);
+ memset(&u, 0, sizeof(u));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ /* Keith Packard reports that 0-length sends don't work, so we
+ * always send 1 byte. */
+ iov.iov_base = &c;
+ iov.iov_len = 1;
+
+ return sendmsg(sockout, &msg, 0) == 1;
+}
+
+int fdpass_recv(int sockin)
+{
+ /* From the cmsg(3) manpage: */
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ int fd;
+ char c;
+ union { /* Ancillary data buffer, wrapped in a union
+ in order to ensure it is suitably aligned */
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr align;
+ } u;
+
+ msg.msg_control = u.buf;
+ msg.msg_controllen = sizeof(u.buf);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ iov.iov_base = &c;
+ iov.iov_len = 1;
+
+ if (recvmsg(sockin, &msg, 0) < 0)
+ return -1;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg
+ || cmsg->cmsg_len != CMSG_LEN(sizeof(fd))
+ || cmsg->cmsg_level != SOL_SOCKET
+ || cmsg->cmsg_type != SCM_RIGHTS) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+ return fd;
+}
--- /dev/null
+/* CC0 license (public domain) - see LICENSE file for details */
+#ifndef CCAN_FDPASS_H
+#define CCAN_FDPASS_H
+
+#include <stdbool.h>
+
+/**
+ * fdpass_send - send a file descriptor across a socket
+ * @sockout: socket to write to
+ * @fd: file descriptor to pass
+ *
+ * On failure, sets errno and returns false.
+ */
+bool fdpass_send(int sockout, int fd);
+
+/**
+ * fdpass_recv - receive a file descriptor from a socket
+ * @sockin: socket to read from
+ *
+ * On failure, returns -1 and sets errno. Otherwise returns fd.
+ */
+int fdpass_recv(int sockin);
+#endif /* CCAN_FDPASS_H */
--- /dev/null
+#include <ccan/fdpass/fdpass.h>
+/* Include the C files directly. */
+#include <ccan/fdpass/fdpass.c>
+#include <ccan/tap/tap.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+
+static void child(int sockfd)
+{
+ char c;
+ int newfd = fdpass_recv(sockfd);
+ assert(newfd >= 0);
+ assert(read(newfd, &c, 1) == 1);
+ assert(c == 0x77);
+ exit(0);
+}
+
+static void child_nofd(int sockfd)
+{
+ assert(fdpass_recv(sockfd) == -1);
+ exit(0);
+}
+
+static void parent(int sockfd)
+{
+ int pfds[2];
+
+ ok1(pipe(pfds) == 0);
+ ok1(fdpass_send(sockfd, pfds[0]));
+ ok1(close(pfds[0]) == 0);
+ ok1(write(pfds[1], "\x77", 1) == 1);
+ ok1(close(pfds[1]) == 0);
+}
+
+int main(void)
+{
+ int sv[2];
+ int pid, wstatus;
+
+ plan_tests(17);
+ ok1(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+
+ pid = fork();
+ if (pid == 0) {
+ close(sv[1]);
+ child(sv[0]);
+ }
+
+ parent(sv[1]);
+ ok1(waitpid(pid, &wstatus, 0) == pid);
+ ok1(WIFEXITED(wstatus));
+ ok1(WEXITSTATUS(wstatus) == 0);
+
+ pid = fork();
+ if (pid == 0) {
+ close(sv[1]);
+ child_nofd(sv[0]);
+ }
+ /* Don't write an fd. */
+ ok1(write(sv[1], "1", 1) == 1);
+ ok1(waitpid(pid, &wstatus, 0) == pid);
+ ok1(WIFEXITED(wstatus));
+ ok1(WEXITSTATUS(wstatus) == 0);
+
+ pid = fork();
+ if (pid == 0) {
+ close(sv[1]);
+ child_nofd(sv[0]);
+ }
+ /* Don't write anything. */
+ close(sv[1]);
+ ok1(waitpid(pid, &wstatus, 0) == pid);
+ ok1(WIFEXITED(wstatus));
+ ok1(WEXITSTATUS(wstatus) == 0);
+
+ close(sv[0]);
+ /* Test fdpass_recv from invalid fd. */
+ ok1(fdpass_recv(sv[0]) == -1 && errno == EBADF);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}