--- /dev/null
+../../../licenses/LGPL-2.1
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * io/fdpass - IO helper for passing file descriptors across local sockets
+ *
+ * This code adds the ability to pass file descriptors to ccan/io.
+ *
+ * License: LGPL (v2.1 or any later version)
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * Example:
+ * // Given "hello" outputs hello
+ * #include <ccan/io/fdpass/fdpass.h>
+ * #include <sys/types.h>
+ * #include <sys/socket.h>
+ * #include <sys/un.h>
+ * #include <stdio.h>
+ * #include <stdlib.h>
+ * #include <unistd.h>
+ *
+ * // Child reads stdin into the buffer, prints it out.
+ * struct buf {
+ * size_t used;
+ * char c[100];
+ * };
+ * static struct io_plan *read_more(struct io_conn *conn, struct buf *buf)
+ * {
+ * printf("%.*s", (int)buf->used, buf->c);
+ * return io_read_partial(conn, buf->c, sizeof(buf->c), &buf->used,
+ * read_more, buf);
+ * }
+ *
+ * // Child has received fd, start reading loop.
+ * static struct io_plan *got_infd(struct io_conn *conn, int *infd)
+ * {
+ * struct buf *buf = calloc(1, sizeof(*buf));
+ *
+ * io_new_conn(NULL, *infd, read_more, buf);
+ * return io_close(conn);
+ * }
+ * // Child is receiving the fd to read into.
+ * static struct io_plan *recv_infd(struct io_conn *conn, int *infd)
+ * {
+ * return io_recv_fd(conn, infd, got_infd, infd);
+ * }
+ *
+ * // Gets passed fd (stdin), which it reads from.
+ * static void child(int sockfd)
+ * {
+ * int infd;
+ *
+ * io_new_conn(NULL, sockfd, recv_infd, &infd);
+ * io_loop(NULL, NULL);
+ * exit(0);
+ * }
+ *
+ * static struct io_plan *send_stdin(struct io_conn *conn, void *unused)
+ * {
+ * return io_send_fd(conn, STDIN_FILENO, io_close_cb, NULL);
+ * }
+ *
+ * static void parent(int sockfd)
+ * {
+ * io_new_conn(NULL, sockfd, send_stdin, NULL);
+ * io_loop(NULL, NULL);
+ * 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) {
+ printf("ccan/fdpass\n");
+ printf("ccan/io\n");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/* GNU LGPL version 2 (or later) - see LICENSE file for details */
+#include <ccan/io/fdpass/fdpass.h>
+#include <ccan/fdpass/fdpass.h>
+#include <ccan/io/io_plan.h>
+#include <errno.h>
+
+static int do_fd_send(int fd, struct io_plan_arg *arg)
+{
+ if (!fdpass_send(fd, arg->u1.s)) {
+ /* In case ccan/io ever gets smart with non-blocking. */
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return 0;
+ return -1;
+ }
+ return 1;
+}
+
+struct io_plan *io_send_fd_(struct io_conn *conn,
+ int fd,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *next_arg)
+{
+ struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT);
+
+ arg->u1.s = fd;
+
+ return io_set_plan(conn, IO_OUT, do_fd_send, next, next_arg);
+}
+
+static int do_fd_recv(int fd, struct io_plan_arg *arg)
+{
+ int fdin = fdpass_recv(fd);
+
+ if (fdin < 0) {
+ /* In case ccan/io ever gets smart with non-blocking. */
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return 0;
+ return -1;
+ }
+ *(int *)arg->u1.vp = fdin;
+ return 1;
+}
+
+struct io_plan *io_recv_fd_(struct io_conn *conn,
+ int *fd,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *next_arg)
+{
+ struct io_plan_arg *arg = io_plan_arg(conn, IO_IN);
+
+ arg->u1.vp = fd;
+
+ return io_set_plan(conn, IO_IN, do_fd_recv, next, next_arg);
+}
--- /dev/null
+/* GNU LGPL version 2 (or later) - see LICENSE file for details */
+#ifndef CCAN_IO_FDPASS_H
+#define CCAN_IO_FDPASS_H
+#include <ccan/io/io.h>
+
+/**
+ * io_send_fd - output plan to send a file descriptor
+ * @conn: the connection that plan is for.
+ * @fd: the file descriptor to pass.
+ * @next: function to call output is done.
+ * @arg: @next argument
+ *
+ * This updates the output plan, to write out a file descriptor. This
+ * usually only works over an AF_LOCAL (ie. Unix domain) socket. Once
+ * that's sent, the @next function will be called: on an error, the
+ * finish function is called instead.
+ *
+ * Note that the I/O may actually be done immediately, and the other end
+ * of the socket must use io_recv_fd: if it does a normal read, the file
+ * descriptor will be lost.
+ *
+ * Example:
+ * static struct io_plan *fd_to_conn(struct io_conn *conn, int fd)
+ * {
+ * // Write fd, then close.
+ * return io_send_fd(conn, fd, io_close_cb, NULL);
+ * }
+ */
+#define io_send_fd(conn, fd, next, arg) \
+ io_send_fd_((conn), (fd), \
+ typesafe_cb_preargs(struct io_plan *, void *, \
+ (next), (arg), struct io_conn *), \
+ (arg))
+struct io_plan *io_send_fd_(struct io_conn *conn,
+ int fd,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *arg);
+
+/**
+ * io_recv_fd - input plan to receive a file descriptor
+ * @conn: the connection that plan is for.
+ * @fd: a pointer to where to place to file descriptor
+ * @next: function to call once input is done.
+ * @arg: @next argument
+ *
+ * This creates a plan to receive a file descriptor, as sent by
+ * io_send_fd. Once it's all read, the @next function will be called:
+ * on an error, the finish function is called instead.
+ *
+ * Note that the I/O may actually be done immediately.
+ *
+ * Example:
+ * static struct io_plan *read_from_conn(struct io_conn *conn, int *fdp)
+ * {
+ * // Read message, then close.
+ * return io_recv_fd(conn, fdp, io_close_cb, NULL);
+ * }
+ */
+#define io_recv_fd(conn, fd, next, arg) \
+ io_recv_fd_((conn), (fd), \
+ typesafe_cb_preargs(struct io_plan *, void *, \
+ (next), (arg), struct io_conn *), \
+ (arg))
+struct io_plan *io_recv_fd_(struct io_conn *conn,
+ int *fd,
+ struct io_plan *(*next)(struct io_conn *, void *),
+ void *arg);
+#endif /* CCAN_IO_FDPASS_H */
--- /dev/null
+#include <ccan/io/fdpass/fdpass.h>
+/* Include the C files directly. */
+#include <ccan/io/fdpass/fdpass.c>
+#include <ccan/tap/tap.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+static struct io_plan *try_reading(struct io_conn *conn, int *fd)
+{
+ char buf[6];
+ ok1(read(*fd, buf, sizeof(buf)) == sizeof(buf));
+ ok1(memcmp(buf, "hello!", sizeof(buf)) == 0);
+ return io_close(conn);
+}
+
+static struct io_plan *get_fd(struct io_conn *conn, void *unused)
+{
+ int *fd = tal(conn, int);
+ return io_recv_fd(conn, fd, try_reading, fd);
+}
+
+static struct io_plan *try_writing(struct io_conn *conn, int *pfd)
+{
+ close(pfd[0]);
+ ok1(write(pfd[1], "hello!", 6) == 6);
+ return io_close(conn);
+}
+
+static struct io_plan *send_fd(struct io_conn *conn, int *pfd)
+{
+ return io_send_fd(conn, pfd[0], try_writing, pfd);
+}
+
+int main(void)
+{
+ int sv[2];
+ int pfd[2];
+
+ plan_tests(5);
+ ok1(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ ok1(pipe(pfd) == 0);
+
+ /* Pass read end of pipe to ourselves, test. */
+ io_new_conn(NULL, sv[0], get_fd, NULL);
+ io_new_conn(NULL, sv[1], send_fd, pfd);
+
+ io_loop(NULL, NULL);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}