mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-02-02 12:30:24 +08:00
227 lines
7.3 KiB
C
227 lines
7.3 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_multi_send.c
|
|
|
|
Used in conjunction with scm_multi_recv.c to demonstrate passing of
|
|
ancillary data containing multiple 'msghdr' structures on a UNIX
|
|
domain socket.
|
|
|
|
This program sends ancillary data consisting of two blocks. One block
|
|
contains process credentials (SCM_CREDENTIALS) and the other contains
|
|
one or more file descriptors (SCM_RIGHTS).
|
|
|
|
Usage is as shown below in usageError().
|
|
|
|
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.
|
|
*/
|
|
#define _GNU_SOURCE
|
|
#include "scm_multi.h"
|
|
|
|
static void
|
|
usageError(char *pname)
|
|
{
|
|
fprintf(stderr, "Usage: %s [options] file...\n", pname);
|
|
fprintf(stderr, " Options:\n");
|
|
fprintf(stderr, "\t-d Use datagram (instead of stream) socket\n");
|
|
fprintf(stderr, "\t-n Don't send any real data with the "
|
|
"ancillary data\n");
|
|
fprintf(stderr, "\t-p <pid> Use this PID when sending credentials\n");
|
|
fprintf(stderr, "\t-u <uid> Use this UID when sending credentials\n");
|
|
fprintf(stderr, "\t-g <gid> Use this GID when sending credentials\n");
|
|
fprintf(stderr, " If any of any of -p/-u/-g is absent, the "
|
|
"corresponding real\n credential is used.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int data, sfd, opt, j;
|
|
pid_t pid;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
ssize_t numSent;
|
|
Boolean useDatagramSocket, sendData;
|
|
struct msghdr msgh;
|
|
struct iovec iov;
|
|
struct ucred *ucredp; /* Pointer to data area of a 'cmsghdr' that
|
|
contains credentials */
|
|
int *fdList; /* Pointer to data area of a 'cmsghdr' that
|
|
contains a list of file descriptors */
|
|
int fdCnt; /* Number of FDs in ancillary data */
|
|
char *controlMsg; /* Ancillary data (control message) */
|
|
size_t controlMsgSize; /* Size of ancillary data */
|
|
struct cmsghdr *cmsgp; /* Pointer used to iterate through
|
|
headers in ancillary data */
|
|
|
|
/* By default, this program sends an SCM_CREDENTIALS message containing
|
|
the process's real credentials. This can be altered via command-line
|
|
options. */
|
|
|
|
pid = getpid();
|
|
uid = getuid();
|
|
gid = getgid();
|
|
|
|
/* Parse command-line options */
|
|
|
|
useDatagramSocket = FALSE;
|
|
sendData = TRUE;
|
|
|
|
while ((opt = getopt(argc, argv, "dnp:u:g:")) != -1) {
|
|
switch (opt) {
|
|
case 'd':
|
|
useDatagramSocket = TRUE;
|
|
break;
|
|
|
|
case 'n':
|
|
sendData = FALSE;
|
|
break;
|
|
|
|
case 'p':
|
|
pid = atoi(optarg);
|
|
break;
|
|
|
|
case 'u':
|
|
uid = atoi(optarg);
|
|
break;
|
|
|
|
case 'g':
|
|
gid = atoi(optarg);
|
|
break;
|
|
|
|
default:
|
|
usageError(argv[0]);
|
|
}
|
|
}
|
|
|
|
fdCnt = argc - optind;
|
|
if (fdCnt <= 0)
|
|
usageError(argv[0]);
|
|
|
|
/* Allocate a buffer of suitable size to hold the ancillary data.
|
|
This buffer is in reality treated as a 'struct cmsghdr',
|
|
and so needs to be suitably aligned: malloc() provides a block
|
|
with suitable alignment. */
|
|
|
|
controlMsgSize = CMSG_SPACE(sizeof(int) * fdCnt) +
|
|
CMSG_SPACE(sizeof(struct ucred));
|
|
controlMsg = malloc(controlMsgSize);
|
|
if (controlMsg == NULL)
|
|
errExit("malloc");
|
|
|
|
/* The control message buffer must be zero-initialized in order for
|
|
the CMSG_NXTHDR() macro to work correctly */
|
|
|
|
memset(controlMsg, 0, controlMsgSize);
|
|
|
|
/* 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 use connect() below, which sets
|
|
a default outgoing address for datagrams. */
|
|
|
|
msgh.msg_name = NULL;
|
|
msgh.msg_namelen = 0;
|
|
|
|
/* On Linux, we must transmit at least 1 byte of real data in
|
|
order to send ancillary data, at least when using stream sockets.
|
|
The following allows for testing the results if no real data is
|
|
sent with the ancillary data. */
|
|
|
|
if (sendData) {
|
|
msgh.msg_iov = &iov;
|
|
msgh.msg_iovlen = 1;
|
|
iov.iov_base = &data;
|
|
iov.iov_len = sizeof(int);
|
|
data = 12345;
|
|
} else {
|
|
msgh.msg_iov = NULL;
|
|
msgh.msg_iovlen = 0;
|
|
}
|
|
|
|
/* Place a pointer to the ancillary data, and size of that data,
|
|
in the 'msghdr' structure that will be passed to sendmsg() */
|
|
|
|
msgh.msg_control = controlMsg;
|
|
msgh.msg_controllen = controlMsgSize;
|
|
|
|
/* Set message header to describe the ancillary data that
|
|
we want to send */
|
|
|
|
/* First, the file descriptor list */
|
|
|
|
cmsgp = CMSG_FIRSTHDR(&msgh);
|
|
cmsgp->cmsg_level = SOL_SOCKET;
|
|
cmsgp->cmsg_type = SCM_RIGHTS;
|
|
|
|
/* The ancillary message must include space for the required number
|
|
of file descriptors */
|
|
|
|
cmsgp->cmsg_len = CMSG_LEN(sizeof(int) * fdCnt);
|
|
printf("cmsg_len 1: %ld\n", (long) cmsgp->cmsg_len);
|
|
|
|
/* Set 'fdList' pointing to the data area of this ancillary message block.
|
|
The file descriptrs are placed into the data block by the loop below. */
|
|
|
|
fdList = (int *) CMSG_DATA(cmsgp);
|
|
|
|
/* Next, the credentials */
|
|
|
|
cmsgp = CMSG_NXTHDR(&msgh, cmsgp);
|
|
cmsgp->cmsg_level = SOL_SOCKET;
|
|
cmsgp->cmsg_type = SCM_CREDENTIALS;
|
|
|
|
/* The ancillary message must include space for a 'struct ucred' */
|
|
|
|
cmsgp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
|
printf("cmsg_len 2: %ld\n", (long) cmsgp->cmsg_len);
|
|
|
|
/* Set 'ucredp' pointing to the data area of this ancillary message block.
|
|
The credentials are placed into the data area by code below. */
|
|
|
|
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
|
|
|
|
/* Initialize the credentials inside the ancillary data */
|
|
|
|
ucredp->pid = pid;
|
|
ucredp->uid = uid;
|
|
ucredp->gid = gid;
|
|
|
|
/* Open the files named on the command line, placing the returned file
|
|
descriptors into the ancillary data */
|
|
|
|
for (j = 0; j < fdCnt; j++) {
|
|
fdList[j] = open(argv[optind + j], O_RDONLY);
|
|
if (fdList[j] == -1)
|
|
errExit("open");
|
|
}
|
|
|
|
/* Connect to the peer socket */
|
|
|
|
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
|
|
if (sfd == -1)
|
|
errExit("unixConnect");
|
|
|
|
/* Send the data plus ancillary data */
|
|
|
|
numSent = sendmsg(sfd, &msgh, 0);
|
|
if (numSent == -1)
|
|
errExit("sendmsg");
|
|
|
|
printf("sendmsg() returned %ld\n", (long) numSent);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|