mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-08 18:19:08 +08:00
147 lines
4.7 KiB
C
147 lines
4.7 KiB
C
/*************************************************************************\
|
|
* Copyright (C) Michael Kerrisk, 2018. *
|
|
* *
|
|
* This program is free software. You may use, modify, and redistribute it *
|
|
* under the terms of the GNU Lesser General Public License as published *
|
|
* by the Free Software Foundation, either version 3 or (at your option) *
|
|
* any later version. This program is distributed without any warranty. *
|
|
* See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. *
|
|
\*************************************************************************/
|
|
|
|
/* Supplementary program for Chapter 61 */
|
|
|
|
/* scm_functions.c
|
|
|
|
Functions to exchange ancillary data over UNIX domain sockets.
|
|
These functions are simplistic, in that they ignore the "real" data
|
|
content on the assumption that the sockets are being used only for
|
|
the purposes of exchanging ancillary data. In many real-world
|
|
applications, the application makes use of both the "real" data
|
|
channel and the ancillary data, with some kind of protocol that
|
|
determines how the "real" and ancillary data are used together.
|
|
*/
|
|
#include "scm_functions.h"
|
|
|
|
/* Send the file descriptor 'fd' over the connected UNIX domain
|
|
socket 'sockfd' */
|
|
|
|
int
|
|
sendfd(int sockfd, int fd)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec iov;
|
|
int data;
|
|
struct cmsghdr *cmsgp;
|
|
|
|
/* Allocate a char array of suitable size to hold the ancillary data.
|
|
However, since this buffer is in reality a 'struct cmsghdr', use a
|
|
union to ensure that it is aligned as required for that structure.
|
|
Alternatively, we could allocate the buffer using malloc(), which
|
|
returns a buffer that satisfies the strictest alignment requirements
|
|
of any type. However, if we employ that approach, we must ensure
|
|
that we free() the buffer on all return paths from this function. */
|
|
union {
|
|
char buf[CMSG_SPACE(sizeof(int))];
|
|
/* Space large enough to hold an 'int' */
|
|
struct cmsghdr align;
|
|
} controlMsg;
|
|
|
|
/* The 'msg_name' field can be used to specify the address of the
|
|
destination socket when sending a datagram. However, we do not
|
|
need to use this field because we presume that 'sockfd' is a
|
|
connected socket. */
|
|
|
|
msgh.msg_name = NULL;
|
|
msgh.msg_namelen = 0;
|
|
|
|
/* On Linux, we must transmit at least one byte of real data in
|
|
order to send ancillary data. We transmit an arbitrary integer
|
|
whose value is ignored by recvfd(). */
|
|
|
|
msgh.msg_iov = &iov;
|
|
msgh.msg_iovlen = 1;
|
|
iov.iov_base = &data;
|
|
iov.iov_len = sizeof(int);
|
|
data = 12345;
|
|
|
|
/* Set 'msghdr' fields that describe ancillary data */
|
|
|
|
msgh.msg_control = controlMsg.buf;
|
|
msgh.msg_controllen = sizeof(controlMsg.buf);
|
|
|
|
/* Set up ancillary data describing file descriptor to send */
|
|
|
|
cmsgp = CMSG_FIRSTHDR(&msgh);
|
|
cmsgp->cmsg_level = SOL_SOCKET;
|
|
cmsgp->cmsg_type = SCM_RIGHTS;
|
|
cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
|
|
*((int *) CMSG_DATA(cmsgp)) = fd;
|
|
|
|
/* Send real plus ancillary data */
|
|
|
|
if (sendmsg(sockfd, &msgh, 0) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Receive a file descriptor on a connected UNIX domain socket.
|
|
The received file descriptor is returned as the function result. */
|
|
|
|
int
|
|
recvfd(int sockfd)
|
|
{
|
|
struct msghdr msgh;
|
|
struct iovec iov;
|
|
int data;
|
|
ssize_t nr;
|
|
|
|
/* Allocate a char buffer for the ancillary data. See the comments
|
|
in sendfd() */
|
|
union {
|
|
char buf[CMSG_SPACE(sizeof(int))];
|
|
struct cmsghdr align;
|
|
} controlMsg;
|
|
struct cmsghdr *cmsgp;
|
|
|
|
/* The 'msg_name' field can be used to obtain the address of the
|
|
sending socket. However, we do not need this information. */
|
|
|
|
msgh.msg_name = NULL;
|
|
msgh.msg_namelen = 0;
|
|
|
|
/* Specify buffer for receiving real data */
|
|
|
|
msgh.msg_iov = &iov;
|
|
msgh.msg_iovlen = 1;
|
|
iov.iov_base = &data; /* Real data is an 'int' */
|
|
iov.iov_len = sizeof(int);
|
|
|
|
/* Set 'msghdr' fields that describe ancillary data */
|
|
|
|
msgh.msg_control = controlMsg.buf;
|
|
msgh.msg_controllen = sizeof(controlMsg.buf);
|
|
|
|
/* Receive real plus ancillary data; content of real data is ignored */
|
|
|
|
nr = recvmsg(sockfd, &msgh, 0);
|
|
if (nr == -1)
|
|
return -1;
|
|
|
|
cmsgp = CMSG_FIRSTHDR(&msgh);
|
|
|
|
/* Check the validity of the 'cmsghdr' */
|
|
|
|
if (cmsgp == NULL ||
|
|
cmsgp->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
|
cmsgp->cmsg_level != SOL_SOCKET ||
|
|
cmsgp->cmsg_type != SCM_RIGHTS) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* Return the received file descriptor to our caller */
|
|
|
|
return *((int *) CMSG_DATA(cmsgp));
|
|
}
|