/* * Copyright (C) 2004 Red Hat Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authors: * Mark McLoughlin */ #include #include #include #include #include #include #include #include #include #include #include #include #define SOCKET_PATH "/tmp/pass-your-file-descriptor-here" #define RANDOM_FILE_TO_OPEN "/proc/version" static void read_descriptor (int fd) { union { struct cmsghdr cmsg; char control [CMSG_SPACE (sizeof (int))]; } msg_control; struct msghdr msg; struct iovec iov [1]; struct cmsghdr *cmsg; char buf [192]; int n; iov [0].iov_base = buf; iov [0].iov_len = sizeof (buf); /* not relevant for connected sockets */ msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = &msg_control; msg.msg_controllen = sizeof (msg_control); msg.msg_flags = 0; n = recvmsg (fd, &msg, 0); assert (n == 1); assert (buf [0] == 'x'); assert (msg.msg_control != NULL); close (fd); for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) { int descriptor; if (cmsg->cmsg_len != CMSG_LEN (sizeof (int)) || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) continue; descriptor = *((int *) CMSG_DATA (cmsg)); go_again: while ((n = read (descriptor, buf, sizeof (buf) - 1)) > 0) { buf [n] = '\0'; fputs (buf, stdout); fflush (stdout); } if (n == -1 && errno == EINTR) { goto go_again; } close (descriptor); } } static void write_descriptor (int fd, int descriptor) { struct msghdr msg; struct iovec iov [1]; int n; /* need to send some data otherwise client can't distinguish between * EOF and "just sending a file descriptor" */ iov [0].iov_base = "x"; iov [0].iov_len = 1; /* not relevant for connected sockets */ msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = NULL; msg.msg_controllen = 0; /* put the descriptor in the ancillary data */ { /* using a union to ensure its correctly aligned */ union { struct cmsghdr cmsg; char control [CMSG_SPACE (sizeof (int))]; } msg_control; struct cmsghdr *cmsg; msg.msg_control = &msg_control; msg.msg_controllen = sizeof (msg_control); cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_len = CMSG_LEN (sizeof (int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *((int *) CMSG_DATA (cmsg)) = descriptor; } n = sendmsg (fd, &msg, 0); assert (n == 1); } static void server (void) { struct sockaddr_un servaddr_un; struct sockaddr *servaddr; struct pollfd fds [1]; int listenfd; int n; int servaddr_len; memset (&servaddr_un, 0, sizeof (struct sockaddr_un)); servaddr_un.sun_family = AF_UNIX; strcpy (servaddr_un.sun_path, SOCKET_PATH); servaddr = (struct sockaddr *) &servaddr_un; servaddr_len = sizeof (struct sockaddr_un) - sizeof (servaddr_un.sun_path) + strlen (SOCKET_PATH); listenfd = socket (AF_UNIX, SOCK_STREAM, 0); assert (listenfd > 0); bind (listenfd, servaddr, servaddr_len); n = listen (listenfd, 5); assert (n == 0); fds [0].fd = accept (listenfd, (struct sockaddr *) NULL, NULL); fds [0].events = POLLIN|POLLPRI; assert (fds [0].fd > 0); n = poll (fds, 1, -1); assert (n == 1); assert ((fds [0].revents & POLLIN) != 0); sleep(1); read_descriptor (fds [0].fd); } static void client (int descriptor) { struct sockaddr_un servaddr_un; struct sockaddr *servaddr; int fd; int n; int servaddr_len; memset (&servaddr_un, 0, sizeof (struct sockaddr_un)); servaddr_un.sun_family = AF_UNIX; strcpy (servaddr_un.sun_path, SOCKET_PATH); servaddr = (struct sockaddr *)&servaddr_un; servaddr_len = sizeof (struct sockaddr_un) - sizeof (servaddr_un.sun_path) + strlen (SOCKET_PATH); fd = socket (AF_UNIX, SOCK_STREAM, 0); assert (fd > 0); while ((n = connect (fd, servaddr, servaddr_len) != 0)); write_descriptor (fd, descriptor); } int main (int argc, char **argv) { pid_t pid; int descriptor; unlink (SOCKET_PATH); descriptor = open (RANDOM_FILE_TO_OPEN, O_RDONLY); assert (descriptor > 0); if ((pid = fork ())) { close (descriptor); server (); } else { client (descriptor); return 0; } waitpid (pid, NULL, 0); return 0; }