4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-09 02:29:07 +08:00

170 lines
5.2 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 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 file COPYING.gpl-v3 for details. *
\*************************************************************************/
/* Supplementary program for Chapter 61 */
/* scm_rights_recv.c
Used in conjunction with scm_rights_send.c to demonstrate passing of
file descriptors via a UNIX domain socket.
This program receives a file descriptor sent to a UNIX domain socket.
Usage is as shown in the usageErr() call below.
File descriptors can be exchanged over stream or datagram sockets. This
program uses stream sockets by default; the "-d" command-line option
specifies that datagram sockets should be used instead.
This program is Linux-specific.
See also scm_multi_recv.c.
*/
#include "scm_rights.h"
#define BUF_SIZE 100
int
main(int argc, char *argv[])
{
int data, lfd, sfd, fd, opt;
ssize_t nr;
Boolean useDatagramSocket;
struct msghdr msgh;
struct iovec iov;
/* 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 */
union {
char buf[CMSG_SPACE(sizeof(int))];
/* Space large enough to hold an 'int' */
struct cmsghdr align;
} controlMsg;
struct cmsghdr *cmsgp; /* Pointer used to iterate through
headers in ancillary data */
/* Parse command-line options */
useDatagramSocket = FALSE;
while ((opt = getopt(argc, argv, "d")) != -1) {
switch (opt) {
case 'd':
useDatagramSocket = TRUE;
break;
default:
usageErr("%s [-d]\n"
" -d use datagram socket\n", argv[0]);
}
}
/* Create socket bound to a well-known address. In the case where
we are using stream sockets, also make the socket a listening
socket and accept a connection on the socket. */
if (remove(SOCK_PATH) == -1 && errno != ENOENT)
errExit("remove-%s", SOCK_PATH);
if (useDatagramSocket) {
sfd = unixBind(SOCK_PATH, SOCK_DGRAM);
if (sfd == -1)
errExit("unixBind");
} else {
lfd = unixBind(SOCK_PATH, SOCK_STREAM);
if (lfd == -1)
errExit("unixBind");
if (listen(lfd, 5) == -1)
errExit("listen");
sfd = accept(lfd, NULL, NULL);
if (sfd == -1)
errExit("accept");
}
/* The 'msg_name' field can be set to point to a buffer where the
kernel will place the address of the peer socket. However, we don't
need the address of the peer, so we set this field to NULL. */
msgh.msg_name = NULL;
msgh.msg_namelen = 0;
/* Set fields of 'msgh' to point to buffer used to receive the (real)
data read by recvmsg() */
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
iov.iov_base = &data;
iov.iov_len = sizeof(int);
/* Set 'msgh' fields to describe the ancillary data buffer */
msgh.msg_control = controlMsg.buf;
msgh.msg_controllen = sizeof(controlMsg.buf);
/* Receive real plus ancillary data */
nr = recvmsg(sfd, &msgh, 0);
if (nr == -1)
errExit("recvmsg");
fprintf(stderr, "recvmsg() returned %ld\n", (long) nr);
if (nr > 0)
fprintf(stderr, "Received data = %d\n", data);
/* Get the address of the first 'cmsghdr' in the received
ancillary data */
cmsgp = CMSG_FIRSTHDR(&msgh);
/* Check the validity of the 'cmsghdr' */
if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(int)))
fatal("bad cmsg header / message length");
if (cmsgp->cmsg_level != SOL_SOCKET)
fatal("cmsg_level != SOL_SOCKET");
if (cmsgp->cmsg_type != SCM_RIGHTS)
fatal("cmsg_type != SCM_RIGHTS");
/* The data area of the 'cmsghdr' is an 'int', so assign
the address of the data area to a suitable pointer. The data
is the received file descriptor (which is typically a different
file descriptor number than was used in the sending process). */
fd = *((int *) CMSG_DATA(cmsgp));
fprintf(stderr, "Received FD %d\n", fd);
/* Having obtained the file descriptor, read the file's contents and
print them on standard output */
for (;;) {
char buf[BUF_SIZE];
ssize_t numRead;
numRead = read(fd, buf, BUF_SIZE);
if (numRead == -1)
errExit("read");
if (numRead == 0)
break;
write(STDOUT_FILENO, buf, numRead);
}
exit(EXIT_SUCCESS);
}