Commit 8967bc9e authored by Rusty Russell's avatar Rusty Russell

fdpass: new module.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 4bb69fe6
../../licenses/CC0
\ No newline at end of file
#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;
}
/* 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;
}
/* 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 */
#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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment