Cygwin: AF_UNIX: add socket tests
This commit is contained in:
parent
dfe5988f96
commit
315920326a
|
@ -7,6 +7,7 @@
|
|||
.#*
|
||||
*#
|
||||
|
||||
*.exe
|
||||
*.flt
|
||||
*.gmo
|
||||
*.info
|
||||
|
@ -47,3 +48,6 @@ core
|
|||
!core/
|
||||
|
||||
lost+found
|
||||
|
||||
ename.c.inc
|
||||
libafunix.a
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
include Makefile.inc
|
||||
|
||||
CFLAGS += -Ilib
|
||||
|
||||
AF_UNIX_LIB = libafunix.a
|
||||
|
||||
AF_UNIX_HDR = lib/af_unix_hdr.h
|
||||
|
||||
EXE = ud_ucase_sv ud_ucase_cl \
|
||||
us_xfr_cl us_xfr_sv \
|
||||
us_xfr_v2_cl us_xfr_v2_sv \
|
||||
scm_cred_recv scm_cred_send \
|
||||
scm_rights_recv scm_rights_send \
|
||||
scm_multi_recv scm_multi_send \
|
||||
us_abstract_bind \
|
||||
waitall_sv waitall_cl \
|
||||
readv_socket writev_socket \
|
||||
msg_peek_sv msg_peek_cl \
|
||||
fork_socketpair \
|
||||
select_sv select_cl \
|
||||
is_seqnum_v2_sv is_seqnum_v3_sv is_seqnum_v2_cl \
|
||||
recv_pty_slave send_pty_slave \
|
||||
recv_pty_master send_pty_master
|
||||
|
||||
all: ${EXE}
|
||||
|
||||
${EXE}: ${AF_UNIX_LIB} # True as a rough approximation
|
||||
|
||||
${AF_UNIX_LIB}:
|
||||
cd lib; ${MAKE}
|
||||
|
||||
*.o: ${AF_UNIX_HDR}
|
||||
|
||||
scm_cred_recv.o scm_cred_send.o: scm_cred.h
|
||||
|
||||
scm_rights_recv.o scm_rights_send.o: scm_rights.h
|
||||
|
||||
scm_multi_recv.o scm_multi_send.o: scm_multi.h
|
||||
|
||||
us_xfr_sv.o us_xfr_cl.o: us_xfr.h
|
||||
|
||||
us_xfr_v2_sv.o us_xfr_v2_cl.o: us_xfr_v2.h
|
||||
|
||||
ud_ucase_sv.o ud_ucase_cl.o: ud_ucase.h
|
||||
|
||||
waitall_sv.o waitall_cl.o: waitall.h
|
||||
|
||||
readv_socket.o writev_socket.o: scatter_gather.h
|
||||
|
||||
msg_peek_sv.o msg_peek_cl.o: msg_peek.h
|
||||
|
||||
select_sv.o select_cl.o: select_test.h
|
||||
|
||||
is_seqnum_v2_sv.o is_seqnum_v3_sv.o is_seqnum_v2_cl.o: is_seqnum_v2.h
|
||||
|
||||
recv_pty_slave.o send_pty_slave.o: pty_slave.h
|
||||
|
||||
recv_pty_master.o send_pty_master.o: pty_master.h
|
||||
|
||||
clean:
|
||||
cd lib; ${MAKE} clean
|
||||
${RM} *.exe *.o ${AF_UNIX_LIB}
|
|
@ -0,0 +1,11 @@
|
|||
CC = gcc
|
||||
|
||||
CFLAGS = -D_XOPEN_SOURCE=600 \
|
||||
-D_DEFAULT_SOURCE \
|
||||
-g -O0 -I. \
|
||||
-pedantic \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wmissing-prototypes \
|
||||
-Wno-sign-compare \
|
||||
-Wno-unused-parameter
|
|
@ -0,0 +1,279 @@
|
|||
0. make
|
||||
|
||||
1. Server-client using read/write.
|
||||
|
||||
$ cat *.c > a
|
||||
|
||||
$ ./us_xfr_sv.exe > b&
|
||||
|
||||
$ ./us_xfr_cl.exe < a
|
||||
|
||||
$ kill %1
|
||||
|
||||
$
|
||||
[1]+ Terminated ./us_xfr_sv.exe > b
|
||||
|
||||
$ diff a b
|
||||
|
||||
$ rm a b
|
||||
|
||||
Should be able to do same test with v2 versions.
|
||||
|
||||
2. Datagram server-client using sendto/recvfrom.
|
||||
|
||||
$ ./ud_ucase_sv.exe &
|
||||
|
||||
$ ./ud_ucase_cl.exe long message
|
||||
Server received 4 bytes from /tmp/ud_ucase_cl.925
|
||||
Response 1: LONG
|
||||
Server received 7 bytes from /tmp/ud_ucase_cl.925
|
||||
Response 2: MESSAGE
|
||||
|
||||
$ ./ud_ucase_cl.exe 'long message'
|
||||
Server received 10 bytes from /tmp/ud_ucase_cl.926
|
||||
Response 1: LONG MESSA
|
||||
|
||||
$ kill %1
|
||||
|
||||
3. MSG_WAITALL test. In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./waitall_sv.exe
|
||||
|
||||
# Terminal 2:
|
||||
$ ./waitall_cl.exe
|
||||
abcd
|
||||
abcd
|
||||
|
||||
[Should see this echoed in Terminal 1 after both lines have been
|
||||
typed. Kill both programs with Ctrl-C.]
|
||||
|
||||
4. scatter-gather test. In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./readv_socket.exe
|
||||
|
||||
# Terminal 2:
|
||||
$ ./writev_socket.exe
|
||||
wrote 148 bytes
|
||||
|
||||
# Terminal 1 should now show:
|
||||
$ ./readv_socket.exe
|
||||
read 148 bytes
|
||||
0: The term buccaneer comes from the word boucan.
|
||||
1: A boucan is a wooden frame used for cooking meat.
|
||||
2: Buccaneer is the West Indies name for a pirate.
|
||||
|
||||
5. MSG_PEEK test. In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./msg_peek_sv.exe
|
||||
peeking...
|
||||
|
||||
# Terminal 2:
|
||||
$ ./msg_peek_cl.exe
|
||||
hello
|
||||
|
||||
# Terminal 1 should now show:
|
||||
$ ./msg_peek_sv.exe
|
||||
peeking...
|
||||
reading would yield 6 bytes: hello
|
||||
|
||||
[After 1 second delay]
|
||||
|
||||
reading...
|
||||
read 6 bytes: hello
|
||||
|
||||
[Need to kill msg_peek_cl.]
|
||||
|
||||
6. fork/socketpair test.
|
||||
|
||||
$ ./fork_socketpair.exe
|
||||
count = 0
|
||||
count = 1
|
||||
count = 2
|
||||
count = 3
|
||||
count = 4
|
||||
count = 5
|
||||
count = 6
|
||||
count = 7
|
||||
count = 8
|
||||
count = 9
|
||||
|
||||
7. select test. In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./select_sv
|
||||
waiting for connection request...
|
||||
|
||||
# Terminal 2:
|
||||
$ ./select_cl
|
||||
waiting for socket to be ready for write...
|
||||
ready for write, writing until buffer full
|
||||
buffer full
|
||||
wrote 262108 bytes
|
||||
waiting for write ready again...
|
||||
ready for write, writing once more
|
||||
wrote 65527 more bytes for a total of 327635
|
||||
|
||||
# Terminal 1 should now show:
|
||||
$ ./select_sv
|
||||
waiting for connection request...
|
||||
connection request received; accepting
|
||||
slowly reading from socket...
|
||||
read 327635 bytes
|
||||
|
||||
8. Ancillary data test (SCM_CREDENTIALS). In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./scm_cred_recv.exe
|
||||
|
||||
# Terminal 2:
|
||||
$ ./scm_cred_send.exe
|
||||
Sending data = 12345
|
||||
Send credentials pid=234, uid=197609, gid=197121
|
||||
sendmsg() returned 4
|
||||
|
||||
# Terminal 1 should now show:
|
||||
$ ./scm_cred_recv.exe
|
||||
recvmsg() returned 4
|
||||
Received data = 12345
|
||||
Received credentials pid=234, uid=197609, gid=197121
|
||||
Credentials from SO_PEERCRED: pid=234, euid=197609, egid=197121
|
||||
|
||||
If use -d option in both programs to use datagrams, the last line
|
||||
instead reads:
|
||||
|
||||
ERROR [EINVAL Invalid argument] getsockopt
|
||||
|
||||
I think this is correct. According to
|
||||
https://man7.org/linux/man-pages/man7/unix.7.html, SO_PEERCRED is
|
||||
not supported for datagram sockets unless they are created using
|
||||
socketpair.
|
||||
|
||||
If we use -n in the send program, credentials will be sent even
|
||||
though the caller didn't specify control message data.
|
||||
|
||||
scm_cred_send can also specify credentials:
|
||||
|
||||
$ ./scm_cred_send.exe data 1 3 5
|
||||
|
||||
This should fail with EPERM if the specified credentials are not
|
||||
the actual credentials of the sender, unless the sender is an
|
||||
administrator. In that case the specified pid must be the pid of
|
||||
an existing process, but the uid and gid can be arbitrary.
|
||||
|
||||
9. Ancillary data test (SCM_RIGHTS, disk file descriptor).
|
||||
In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./scm_rights_recv.exe
|
||||
|
||||
# Terminal 2:
|
||||
$ ./scm_rights_send.exe <some disk file>
|
||||
Sending data = 12345
|
||||
Sending FD 3
|
||||
sendmsg() returned 4
|
||||
|
||||
# Terminal 1 should now show:
|
||||
recvmsg() returned 4
|
||||
Received data = 12345
|
||||
Received FD 5
|
||||
<contents of some disk file>
|
||||
|
||||
10. Ancillary data test (SCM_RIGHTS, socket descriptor).
|
||||
|
||||
$ ./is_seqnum_v3_sv.exe &
|
||||
[1] 8880
|
||||
|
||||
$ ./is_seqnum_v2_cl.exe localhost
|
||||
Connection from (<host>, <port>)
|
||||
Sending fd 4 to child
|
||||
Sequence number: 0
|
||||
|
||||
11. Ancillary data test (SCM_RIGHTS, pty slave descriptor).
|
||||
send_pty_slave creates pty pair and a shell subprocess connected
|
||||
to the slave. It sends the slave descriptor over an AF_UNIX
|
||||
socket to recv_pty_slave. It then monitors its stdin and the pty
|
||||
master for input. Anything it reads from stdin is written to the
|
||||
pty master (and so read by the shell). Anything it reads from the
|
||||
pty master is written to stdout. This is normally just the shell
|
||||
output. But recv_pty_slave writes "hello" to the slave and so is
|
||||
read by send_pty_slave and written to stdout as though it were
|
||||
written by the shell.
|
||||
|
||||
In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./recv_pty_slave.exe
|
||||
Waiting for sender to connect and send descriptor...
|
||||
|
||||
# Terminal 2:
|
||||
$ ./send_pty_slave.exe
|
||||
hello
|
||||
|
||||
#Terminal 1 now shows:
|
||||
$ ./recv_pty_slave.exe
|
||||
Waiting for sender to connect and send descriptor...
|
||||
Received descriptor 5.
|
||||
Writing "hello" to that descriptor.
|
||||
This should appear in the other terminal as though it were output by the shell.
|
||||
|
||||
Can now exit the shell in terminal 2.
|
||||
|
||||
To test all this when the pty is connected to a pseudo terminal,
|
||||
set SHELL=cmd before running send_pty_slave. Terminal 2 then
|
||||
looks like this:
|
||||
|
||||
$ SHELL=cmd ./send_pty_slave.exe
|
||||
hello
|
||||
Microsoft Windows [Version 10.0.18363.1256]
|
||||
(c) 2019 Microsoft Corporation. All rights reserved.
|
||||
|
||||
C:\Users\kbrown\src\cygdll\af_unix\winsup\cygwin\socket_tests>exit
|
||||
|
||||
12. Ancillary data test (SCM_RIGHTS, pty master descriptor).
|
||||
send_pty_master creates pty pair and a shell subprocess connected
|
||||
to the slave. It then does the same as in 11, except that it
|
||||
sends the master descriptor instead of the slave descriptor.
|
||||
recv_pty_master writes "ps\n" to the received master fd. The
|
||||
shell created by send_pty_master reads and executes this.
|
||||
|
||||
In two terminals:
|
||||
|
||||
# Terminal 1:
|
||||
$ ./recv_pty_master.exe
|
||||
Waiting for sender to connect and send descriptor...
|
||||
|
||||
# Terminal 2:
|
||||
$ ./send_pty_master.exe
|
||||
ps
|
||||
|
||||
$ ps
|
||||
PID PPID PGID WINPID TTY UID STIME COMMAND
|
||||
934 933 934 138392 pty2 197609 13:47:22 /usr/bin/bash
|
||||
937 934 937 109052 pty2 197609 13:47:22 /usr/bin/ps
|
||||
887 886 887 51496 pty1 197609 13:11:25 /usr/bin/bash
|
||||
875 874 875 30396 pty0 197609 13:11:21 /usr/bin/bash
|
||||
874 1 874 23516 ? 197609 13:11:21 /usr/bin/mintty
|
||||
886 1 886 118428 ? 197609 13:11:25 /usr/bin/mintty
|
||||
933 887 933 59856 pty1 197609 13:47:22 /home/kbrown/src/cygdll/af_unix/winsup/cygwin/socket_tests/send_pty_master
|
||||
932 875 932 115304 pty0 197609 13:46:30 /home/kbrown/src/cygdll/af_unix/winsup/cygwin/socket_tests/recv_pty_master
|
||||
|
||||
[Why is ps echoed twice?]
|
||||
|
||||
|
||||
#Terminal 1 now shows:
|
||||
$ ./recv_pty_master.exe
|
||||
Waiting for sender to connect and send descriptor...
|
||||
Received descriptor 5.
|
||||
Writing "ps" to that descriptor.
|
||||
This should appear in the other terminal
|
||||
and be executed by the shell running there.
|
||||
Waiting for sender to finish...
|
||||
|
||||
Can now exit the shell in terminal 2 and both programs exit.
|
||||
|
||||
This doesn't work if we use SHELL=cmd in terminal 2. "ps" gets
|
||||
echoed but not executed. I'm not sure if we should expect it to
|
||||
work.
|
|
@ -0,0 +1,72 @@
|
|||
/* Adapted from the code for debug/backlog.c in Stevens, Unix Network
|
||||
Programming. */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
int pipefd[2];
|
||||
#define pfd pipefd[1] /* parent's end */
|
||||
#define cfd pipefd[0] /* child's end */
|
||||
|
||||
/* function prototypes */
|
||||
void do_parent(void);
|
||||
void do_child(void);
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (socketpair (AF_UNIX, SOCK_STREAM, 0, pipefd) < 0)
|
||||
errExit ("socketpair");
|
||||
if ((pid = fork ()) < 0)
|
||||
errExit ("fork");
|
||||
else if (pid == 0)
|
||||
do_child();
|
||||
else
|
||||
do_parent();
|
||||
}
|
||||
|
||||
void
|
||||
do_parent (void)
|
||||
{
|
||||
int count, junk;
|
||||
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
|
||||
for (count = 0; count < 10; count++)
|
||||
{
|
||||
printf ("count = %d\n", count);
|
||||
/* tell child value */
|
||||
if (write (pfd, &count, sizeof (int)) != sizeof (int))
|
||||
errExit ("write");
|
||||
/* wait for child */
|
||||
if (read (pfd, &junk, sizeof (int)) < 0)
|
||||
errExit ("read");
|
||||
sleep (1);
|
||||
}
|
||||
count = -1; /* tell child we're all done */
|
||||
if (write (pfd, &count, sizeof (int)) != sizeof (int))
|
||||
errExit ("write");
|
||||
}
|
||||
|
||||
void
|
||||
do_child(void)
|
||||
{
|
||||
int count, junk;
|
||||
|
||||
if (close (pfd) < 0)
|
||||
errExit ("close");
|
||||
/* wait for parent */
|
||||
if (read (cfd, &count, sizeof (int)) < 0)
|
||||
errExit ("read");
|
||||
while (count >= 0)
|
||||
{
|
||||
/* tell parent */
|
||||
if (write (cfd, &junk, sizeof (int)) != sizeof (int))
|
||||
errExit ("write");
|
||||
/* wait for parent */
|
||||
if (read (cfd, &count, sizeof (int)) < 0)
|
||||
errExit ("read");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-2:a */
|
||||
|
||||
/* is_seqnum_v2.h
|
||||
|
||||
Header file for is_seqnum_v2_sv.c and is_seqnum_v2_cl.c.
|
||||
*/
|
||||
#include "af_unix_hdr.h"
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include "inet_sockets.h" /* Declares our socket functions */
|
||||
#include "read_line.h" /* Declaration of readLine() */
|
||||
|
||||
#define PORT_NUM_STR "50000" /* Port number for server */
|
||||
|
||||
#define INT_LEN 30 /* Size of string able to hold largest
|
||||
integer (including terminating '\n') */
|
|
@ -0,0 +1,56 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-2:c */
|
||||
|
||||
/* is_seqnum_v2_cl.c
|
||||
|
||||
A simple Internet stream socket client. This server obtains a sequence
|
||||
number from the server.
|
||||
|
||||
The program is the same as is_seqnum_cl.c, except that it uses the
|
||||
functions in our inet_sockets.c library to simplify the creation of a
|
||||
socket that connects to the server's socket.
|
||||
|
||||
See also is_seqnum_v2_sv.c.
|
||||
*/
|
||||
#include "is_seqnum_v2.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char *reqLenStr; /* Requested length of sequence */
|
||||
char seqNumStr[INT_LEN]; /* Start of granted sequence */
|
||||
int cfd;
|
||||
ssize_t numRead;
|
||||
|
||||
if (argc < 2 || strcmp(argv[1], "--help") == 0)
|
||||
usageErr("%s server-host [sequence-len]\n", argv[0]);
|
||||
|
||||
cfd = inetConnect(argv[1], PORT_NUM_STR, SOCK_STREAM);
|
||||
if (cfd == -1)
|
||||
fatal("inetConnect() failed");
|
||||
|
||||
reqLenStr = (argc > 2) ? argv[2] : "1";
|
||||
if (write(cfd, reqLenStr, strlen(reqLenStr)) != strlen(reqLenStr))
|
||||
fatal("Partial/failed write (reqLenStr)");
|
||||
if (write(cfd, "\n", 1) != 1)
|
||||
fatal("Partial/failed write (newline)");
|
||||
|
||||
numRead = readLine(cfd, seqNumStr, INT_LEN);
|
||||
if (numRead == -1)
|
||||
errExit("readLine");
|
||||
if (numRead == 0)
|
||||
fatal("Unexpected EOF from server");
|
||||
|
||||
printf("Sequence number: %s", seqNumStr); /* Includes '\n' */
|
||||
|
||||
exit(EXIT_SUCCESS); /* Closes 'cfd' */
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-2:b */
|
||||
|
||||
/* is_seqnum_v2_sv.c
|
||||
|
||||
A simple Internet stream socket server. Our service is to provide unique
|
||||
sequence numbers to the client.
|
||||
|
||||
This program is the same as is_seqnum_cl.c, except that it uses the functions
|
||||
in our inet_sockets.c library to simplify set up of the server's socket.
|
||||
|
||||
Usage: is_seqnum_sv [init-seq-num] (default = 0)
|
||||
|
||||
See also is_seqnum_v2_cl.c.
|
||||
*/
|
||||
#include "is_seqnum_v2.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t seqNum;
|
||||
char reqLenStr[INT_LEN]; /* Length of requested sequence */
|
||||
char seqNumStr[INT_LEN]; /* Start of granted sequence */
|
||||
struct sockaddr *claddr;
|
||||
int lfd, cfd, reqLen;
|
||||
socklen_t addrlen, alen;
|
||||
char addrStr[IS_ADDR_STR_LEN];
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--help") == 0)
|
||||
usageErr("%s [init-seq-num]\n", argv[0]);
|
||||
|
||||
seqNum = (argc > 1) ? getInt(argv[1], 0, "init-seq-num") : 0;
|
||||
|
||||
/* Ignore the SIGPIPE signal, so that we find out about broken connection
|
||||
errors via a failure from write(). */
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) errExit("signal");
|
||||
|
||||
lfd = inetListen(PORT_NUM_STR, 5, &addrlen);
|
||||
if (lfd == -1)
|
||||
fatal("inetListen() failed");
|
||||
|
||||
/* Allocate a buffer large enough to hold the client's socket address */
|
||||
|
||||
claddr = malloc(addrlen);
|
||||
if (claddr == NULL)
|
||||
errExit("malloc");
|
||||
|
||||
for (;;) { /* Handle clients iteratively */
|
||||
|
||||
/* Accept a client connection, obtaining client's address */
|
||||
|
||||
alen = addrlen;
|
||||
cfd = accept(lfd, (struct sockaddr *) claddr, &alen);
|
||||
if (cfd == -1) {
|
||||
errMsg("accept");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Connection from %s\n", inetAddressStr(claddr, alen,
|
||||
addrStr, IS_ADDR_STR_LEN));
|
||||
|
||||
/* Read client request, send sequence number back */
|
||||
|
||||
if (readLine(cfd, reqLenStr, INT_LEN) <= 0) {
|
||||
close(cfd);
|
||||
continue; /* Failed read; skip request */
|
||||
}
|
||||
|
||||
reqLen = atoi(reqLenStr);
|
||||
if (reqLen <= 0) { /* Watch for misbehaving clients */
|
||||
close(cfd);
|
||||
continue; /* Bad request; skip it */
|
||||
}
|
||||
|
||||
snprintf(seqNumStr, INT_LEN, "%d\n", seqNum);
|
||||
if (write(cfd, seqNumStr, strlen(seqNumStr)) != strlen(seqNumStr))
|
||||
fprintf(stderr, "Error on write");
|
||||
|
||||
seqNum += reqLen; /* Update sequence number */
|
||||
|
||||
if (close(cfd) == -1) /* Close connection */
|
||||
errMsg("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* is_seqnum_v3_sv.c (KB)
|
||||
|
||||
A simple Internet stream socket server. Our service is to provide unique
|
||||
sequence numbers to the client.
|
||||
|
||||
This program is the same as is_seqnum_v2.c, except that it forks a
|
||||
subprocess to do the work, and it sends the connection fd to the
|
||||
child over an AF_UNIX socket. Invoke it with --debug to allow time
|
||||
to attach gdb to the child. (KB)
|
||||
|
||||
Usage: is_seqnum_sv [init-seq-num] (default = 0)
|
||||
|
||||
See also is_seqnum_v2_cl.c.
|
||||
*/
|
||||
#include "af_unix_hdr.h"
|
||||
#include "is_seqnum_v2.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint32_t seqNum;
|
||||
char reqLenStr[INT_LEN]; /* Length of requested sequence */
|
||||
char seqNumStr[INT_LEN]; /* Start of granted sequence */
|
||||
struct sockaddr *claddr;
|
||||
int lfd, reqLen;
|
||||
socklen_t addrlen, alen;
|
||||
char addrStr[IS_ADDR_STR_LEN];
|
||||
Boolean debug = FALSE;
|
||||
pid_t pid;
|
||||
int pipefd[2];
|
||||
int pfd; /* parent's end */
|
||||
int cfd; /* child's end */
|
||||
|
||||
if (argc > 1 && strcmp (argv[1], "--help") == 0)
|
||||
usageErr ("%s [--debug] [init-seq-num]\n", argv[0]);
|
||||
if (argc > 1 && strcmp (argv[1], "--debug") == 0)
|
||||
debug = TRUE;
|
||||
|
||||
if (!debug)
|
||||
seqNum = (argc > 1) ? getInt (argv[1], 0, "init-seq-num") : 0;
|
||||
else
|
||||
seqNum = (argc > 2) ? getInt (argv[2], 0, "init-seq-num") : 0;
|
||||
|
||||
/* Ignore the SIGPIPE signal, so that we find out about broken connection
|
||||
errors via a failure from write(). */
|
||||
|
||||
if (signal (SIGPIPE, SIG_IGN) == SIG_ERR)
|
||||
errExit("signal");
|
||||
|
||||
lfd = inetListen (PORT_NUM_STR, 5, &addrlen);
|
||||
if (lfd == -1)
|
||||
fatal ("inetListen() failed");
|
||||
|
||||
/* Allocate a buffer large enough to hold the client's socket address */
|
||||
|
||||
claddr = malloc (addrlen);
|
||||
if (claddr == NULL)
|
||||
errExit ("malloc");
|
||||
|
||||
/* Fork a child to handle client request. */
|
||||
|
||||
if (socketpair (AF_UNIX, SOCK_STREAM, 0, pipefd) < 0)
|
||||
errExit ("socketpair");
|
||||
pfd = pipefd[1];
|
||||
cfd = pipefd[0];
|
||||
|
||||
if ((pid = fork ()) < 0)
|
||||
errExit ("fork");
|
||||
else if (pid > 0) /* parent */
|
||||
{
|
||||
int connfd, junk;
|
||||
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
if (debug)
|
||||
{
|
||||
printf ("parent pid %d, child pid %d, sleeping...\n", getpid (), pid);
|
||||
sleep (30);
|
||||
}
|
||||
|
||||
/* Accept a client connection, obtaining client's address */
|
||||
|
||||
alen = addrlen;
|
||||
connfd = accept (lfd, (struct sockaddr *) claddr, &alen);
|
||||
if (connfd == -1)
|
||||
errExit ("accept");
|
||||
printf ("Connection from %s\n", inetAddressStr (claddr, alen, addrStr,
|
||||
IS_ADDR_STR_LEN));
|
||||
printf ("Sending fd %d to child\n", connfd);
|
||||
if (sendfd (pfd, connfd) < 0)
|
||||
errExit ("sendfd");
|
||||
if (close (connfd) < 0)
|
||||
errExit ("close");
|
||||
/* Wait for child. */
|
||||
if (read (pfd, &junk, sizeof junk) != sizeof junk)
|
||||
errMsg ("read");
|
||||
if (close (pfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (lfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
||||
else /* child */
|
||||
{
|
||||
int connfd, junk;
|
||||
|
||||
if (close (pfd) < 0)
|
||||
errExit ("close");
|
||||
if (close (lfd) < 0)
|
||||
errExit ("close");
|
||||
if (debug)
|
||||
sleep (30);
|
||||
|
||||
/* Get connection fd from parent. */
|
||||
|
||||
connfd = recvfd (cfd);
|
||||
if (connfd < 0)
|
||||
errExit ("recvfd");
|
||||
|
||||
/* Read client request, send sequence number back */
|
||||
|
||||
if (readLine (connfd, reqLenStr, INT_LEN) <= 0)
|
||||
{
|
||||
close (connfd);
|
||||
errExit ("readLine");
|
||||
}
|
||||
|
||||
reqLen = atoi (reqLenStr);
|
||||
if (reqLen <= 0)
|
||||
{
|
||||
close(cfd);
|
||||
errExit ("Bad request");
|
||||
}
|
||||
|
||||
snprintf (seqNumStr, INT_LEN, "%d\n", seqNum);
|
||||
if (write (connfd, seqNumStr, strlen (seqNumStr)) != strlen(seqNumStr))
|
||||
errExit ("write");
|
||||
|
||||
seqNum += reqLen; /* Update sequence number */
|
||||
|
||||
if (close (connfd) == -1) /* Close connection */
|
||||
errMsg ("close");
|
||||
/* Tell parent we're done. */
|
||||
if (write (cfd, &junk, sizeof junk) != sizeof junk)
|
||||
errMsg ("write");
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Create a new version of the file ename.c.inc by parsing symbolic
|
||||
# error names defined in errno.h
|
||||
#
|
||||
echo '#include <errno.h>' | cpp -dM |
|
||||
sed -n -e '/#define *E/s/#define *//p' |sort -k2n |
|
||||
awk '
|
||||
BEGIN {
|
||||
entries_per_line = 4
|
||||
line_len = 68;
|
||||
last = 0;
|
||||
varname =" enames";
|
||||
print "static char *ename[] = {";
|
||||
line = " /* 0 */ \"\"";
|
||||
}
|
||||
|
||||
{
|
||||
if ($2 ~ /^E[A-Z0-9]*$/) { # These entries are sorted at top
|
||||
synonym[$1] = $2;
|
||||
} else {
|
||||
while (last + 1 < $2) {
|
||||
last++;
|
||||
line = line ", ";
|
||||
if (length(line ename) > line_len || last == 1) {
|
||||
print line;
|
||||
line = " /* " last " */ ";
|
||||
line = sprintf(" /* %3d */ ", last);
|
||||
}
|
||||
line = line "\"" "\"" ;
|
||||
}
|
||||
last = $2;
|
||||
ename = $1;
|
||||
for (k in synonym)
|
||||
if (synonym[k] == $1) ename = ename "/" k;
|
||||
|
||||
line = line ", ";
|
||||
if (length(line ename) > line_len || last == 1) {
|
||||
print line;
|
||||
line = " /* " last " */ ";
|
||||
line = sprintf(" /* %3d */ ", last);;
|
||||
}
|
||||
line = line "\"" ename "\"" ;
|
||||
}
|
||||
}
|
||||
END {
|
||||
print line;
|
||||
print "};"
|
||||
print "";
|
||||
print "#define MAX_ENAME " last;
|
||||
}
|
||||
'
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
include ../Makefile.inc
|
||||
|
||||
AF_UNIX_LIB = ../libafunix.a
|
||||
|
||||
all: ${AF_UNIX_LIB}
|
||||
|
||||
${AF_UNIX_LIB}: *.c *.h ename.c.inc
|
||||
${CC} -c ${CFLAGS} *.c
|
||||
${RM} ${AF_UNIX_LIB}
|
||||
${AR} rs ${AF_UNIX_LIB} *.o
|
||||
|
||||
ename.c.inc:
|
||||
sh Build_ename.sh > ename.c.inc
|
||||
echo 1>&2 "ename.c.inc built"
|
||||
|
||||
*.o: af_unix_hdr.h
|
||||
|
||||
error_functions.o: error_functions.h
|
||||
|
||||
scm_functions.o: scm_functions.h
|
||||
|
||||
unix_sockets.o: unix_sockets.h
|
||||
|
||||
inet_sockets.o: inet_sockets.h
|
||||
|
||||
read_line.o: read_line.h
|
||||
|
||||
get_num.o: get_num.h
|
||||
|
||||
clean:
|
||||
${RM} *.o ename.c.inc ${AF_UNIX_LIB}
|
|
@ -0,0 +1,55 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 3-1 */
|
||||
|
||||
/* af_unix_hdr.h
|
||||
|
||||
Standard header file used by nearly all of our example programs.
|
||||
*/
|
||||
#ifndef AF_UNIX_HDR_H
|
||||
#define AF_UNIX_HDR_H /* Prevent accidental double inclusion */
|
||||
|
||||
#include <sys/types.h> /* Type definitions used by many programs */
|
||||
#include <stdio.h> /* Standard I/O functions */
|
||||
#include <stdlib.h> /* Prototypes of commonly used library functions,
|
||||
plus EXIT_SUCCESS and EXIT_FAILURE constants */
|
||||
#include <unistd.h> /* Prototypes for many system calls */
|
||||
#include <errno.h> /* Declares errno and defines error constants */
|
||||
#include <string.h> /* Commonly used string-handling functions */
|
||||
|
||||
#include "get_num.h" /* Declares our functions for handling numeric
|
||||
arguments (getInt(), getLong()) */
|
||||
|
||||
#include "error_functions.h" /* Declares our error-handling functions */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include "unix_sockets.h" /* Declares our socket functions */
|
||||
#include "scm_functions.h"
|
||||
/* #include "unp.h" /\* Stevens, Unix Network Programming *\/ */
|
||||
|
||||
#undef AF_UNIX
|
||||
#define AF_UNIX 31
|
||||
|
||||
#ifdef TRUE
|
||||
#undef TRUE
|
||||
#endif
|
||||
|
||||
#ifdef FALSE
|
||||
#undef FALSE
|
||||
#endif
|
||||
|
||||
typedef enum { FALSE, TRUE } Boolean;
|
||||
|
||||
#define min(m,n) ((m) < (n) ? (m) : (n))
|
||||
#define max(m,n) ((m) > (n) ? (m) : (n))
|
||||
|
||||
#endif
|
|
@ -0,0 +1,201 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 3-3 */
|
||||
|
||||
/* error_functions.c
|
||||
|
||||
Some standard error handling routines used by various programs.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include "error_functions.h"
|
||||
#include "af_unix_hdr.h"
|
||||
#include "ename.c.inc" /* Defines ename and MAX_ENAME */
|
||||
|
||||
#ifdef __GNUC__ /* Prevent 'gcc -Wall' complaining */
|
||||
__attribute__ ((__noreturn__)) /* if we call this function as last */
|
||||
#endif /* statement in a non-void function */
|
||||
static void
|
||||
terminate(Boolean useExit3)
|
||||
{
|
||||
char *s;
|
||||
|
||||
/* Dump core if EF_DUMPCORE environment variable is defined and
|
||||
is a nonempty string; otherwise call exit(3) or _exit(2),
|
||||
depending on the value of 'useExit3'. */
|
||||
|
||||
s = getenv("EF_DUMPCORE");
|
||||
|
||||
if (s != NULL && *s != '\0')
|
||||
abort();
|
||||
else if (useExit3)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Diagnose 'errno' error by:
|
||||
|
||||
* outputting a string containing the error name (if available
|
||||
in 'ename' array) corresponding to the value in 'err', along
|
||||
with the corresponding error message from strerror(), and
|
||||
|
||||
* outputting the caller-supplied error message specified in
|
||||
'format' and 'ap'. */
|
||||
|
||||
static void
|
||||
outputError(Boolean useErr, int err, Boolean flushStdout,
|
||||
const char *format, va_list ap)
|
||||
{
|
||||
#define BUF_SIZE 500
|
||||
char buf[BUF_SIZE], userMsg[BUF_SIZE], errText[BUF_SIZE];
|
||||
|
||||
vsnprintf(userMsg, BUF_SIZE, format, ap);
|
||||
|
||||
if (useErr)
|
||||
snprintf(errText, BUF_SIZE, " [%s %s]",
|
||||
(err > 0 && err <= MAX_ENAME) ?
|
||||
ename[err] : "?UNKNOWN?", strerror(err));
|
||||
else
|
||||
snprintf(errText, BUF_SIZE, ":");
|
||||
|
||||
snprintf(buf, BUF_SIZE, "ERROR%s %s\n", errText, userMsg);
|
||||
|
||||
if (flushStdout)
|
||||
fflush(stdout); /* Flush any pending stdout */
|
||||
fputs(buf, stderr);
|
||||
fflush(stderr); /* In case stderr is not line-buffered */
|
||||
}
|
||||
|
||||
/* Display error message including 'errno' diagnostic, and
|
||||
return to caller */
|
||||
|
||||
void
|
||||
errMsg(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
int savedErrno;
|
||||
|
||||
savedErrno = errno; /* In case we change it here */
|
||||
|
||||
va_start(argList, format);
|
||||
outputError(TRUE, errno, TRUE, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
errno = savedErrno;
|
||||
}
|
||||
|
||||
/* Display error message including 'errno' diagnostic, and
|
||||
terminate the process */
|
||||
|
||||
void
|
||||
errExit(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
va_start(argList, format);
|
||||
outputError(TRUE, errno, TRUE, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
terminate(TRUE);
|
||||
}
|
||||
|
||||
/* Display error message including 'errno' diagnostic, and
|
||||
terminate the process by calling _exit().
|
||||
|
||||
The relationship between this function and errExit() is analogous
|
||||
to that between _exit(2) and exit(3): unlike errExit(), this
|
||||
function does not flush stdout and calls _exit(2) to terminate the
|
||||
process (rather than exit(3), which would cause exit handlers to be
|
||||
invoked).
|
||||
|
||||
These differences make this function especially useful in a library
|
||||
function that creates a child process that must then terminate
|
||||
because of an error: the child must terminate without flushing
|
||||
stdio buffers that were partially filled by the caller and without
|
||||
invoking exit handlers that were established by the caller. */
|
||||
|
||||
void
|
||||
err_exit(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
va_start(argList, format);
|
||||
outputError(TRUE, errno, FALSE, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
terminate(FALSE);
|
||||
}
|
||||
|
||||
/* The following function does the same as errExit(), but expects
|
||||
the error number in 'errnum' */
|
||||
|
||||
void
|
||||
errExitEN(int errnum, const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
va_start(argList, format);
|
||||
outputError(TRUE, errnum, TRUE, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
terminate(TRUE);
|
||||
}
|
||||
|
||||
/* Print an error message (without an 'errno' diagnostic) */
|
||||
|
||||
void
|
||||
fatal(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
va_start(argList, format);
|
||||
outputError(FALSE, 0, TRUE, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
terminate(TRUE);
|
||||
}
|
||||
|
||||
/* Print a command usage error message and terminate the process */
|
||||
|
||||
void
|
||||
usageErr(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
fflush(stdout); /* Flush any pending stdout */
|
||||
|
||||
fprintf(stderr, "Usage: ");
|
||||
va_start(argList, format);
|
||||
vfprintf(stderr, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
fflush(stderr); /* In case stderr is not line-buffered */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Diagnose an error in command-line arguments and
|
||||
terminate the process */
|
||||
|
||||
void
|
||||
cmdLineErr(const char *format, ...)
|
||||
{
|
||||
va_list argList;
|
||||
|
||||
fflush(stdout); /* Flush any pending stdout */
|
||||
|
||||
fprintf(stderr, "Command-line usage error: ");
|
||||
va_start(argList, format);
|
||||
vfprintf(stderr, format, argList);
|
||||
va_end(argList);
|
||||
|
||||
fflush(stderr); /* In case stderr is not line-buffered */
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 3-2 */
|
||||
|
||||
/* error_functions.h
|
||||
|
||||
Header file for error_functions.c.
|
||||
*/
|
||||
#ifndef ERROR_FUNCTIONS_H
|
||||
#define ERROR_FUNCTIONS_H
|
||||
|
||||
/* Error diagnostic routines */
|
||||
|
||||
void errMsg(const char *format, ...);
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
/* This macro stops 'gcc -Wall' complaining that "control reaches
|
||||
end of non-void function" if we use the following functions to
|
||||
terminate main() or some other non-void function. */
|
||||
|
||||
#define NORETURN __attribute__ ((__noreturn__))
|
||||
#else
|
||||
#define NORETURN
|
||||
#endif
|
||||
|
||||
void errExit(const char *format, ...) NORETURN ;
|
||||
|
||||
void err_exit(const char *format, ...) NORETURN ;
|
||||
|
||||
void errExitEN(int errnum, const char *format, ...) NORETURN ;
|
||||
|
||||
void fatal(const char *format, ...) NORETURN ;
|
||||
|
||||
void usageErr(const char *format, ...) NORETURN ;
|
||||
|
||||
void cmdLineErr(const char *format, ...) NORETURN ;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 3-6 */
|
||||
|
||||
/* get_num.c
|
||||
|
||||
Functions to process numeric command-line arguments.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include "get_num.h"
|
||||
|
||||
/* Print a diagnostic message that contains a function name ('fname'),
|
||||
the value of a command-line argument ('arg'), the name of that
|
||||
command-line argument ('name'), and a diagnostic error message ('msg'). */
|
||||
|
||||
static void
|
||||
gnFail(const char *fname, const char *msg, const char *arg, const char *name)
|
||||
{
|
||||
fprintf(stderr, "%s error", fname);
|
||||
if (name != NULL)
|
||||
fprintf(stderr, " (in %s)", name);
|
||||
fprintf(stderr, ": %s\n", msg);
|
||||
if (arg != NULL && *arg != '\0')
|
||||
fprintf(stderr, " offending text: %s\n", arg);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Convert a numeric command-line argument ('arg') into a long integer,
|
||||
returned as the function result. 'flags' is a bit mask of flags controlling
|
||||
how the conversion is done and what diagnostic checks are performed on the
|
||||
numeric result; see get_num.h for details.
|
||||
|
||||
'fname' is the name of our caller, and 'name' is the name associated with
|
||||
the command-line argument 'arg'. 'fname' and 'name' are used to print a
|
||||
diagnostic message in case an error is detected when processing 'arg'. */
|
||||
|
||||
static long
|
||||
getNum(const char *fname, const char *arg, int flags, const char *name)
|
||||
{
|
||||
long res;
|
||||
char *endptr;
|
||||
int base;
|
||||
|
||||
if (arg == NULL || *arg == '\0')
|
||||
gnFail(fname, "null or empty string", arg, name);
|
||||
|
||||
base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 :
|
||||
(flags & GN_BASE_16) ? 16 : 10;
|
||||
|
||||
errno = 0;
|
||||
res = strtol(arg, &endptr, base);
|
||||
if (errno != 0)
|
||||
gnFail(fname, "strtol() failed", arg, name);
|
||||
|
||||
if (*endptr != '\0')
|
||||
gnFail(fname, "nonnumeric characters", arg, name);
|
||||
|
||||
if ((flags & GN_NONNEG) && res < 0)
|
||||
gnFail(fname, "negative value not allowed", arg, name);
|
||||
|
||||
if ((flags & GN_GT_0) && res <= 0)
|
||||
gnFail(fname, "value must be > 0", arg, name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Convert a numeric command-line argument string to a long integer. See the
|
||||
comments for getNum() for a description of the arguments to this function. */
|
||||
|
||||
long
|
||||
getLong(const char *arg, int flags, const char *name)
|
||||
{
|
||||
return getNum("getLong", arg, flags, name);
|
||||
}
|
||||
|
||||
/* Convert a numeric command-line argument string to an integer. See the
|
||||
comments for getNum() for a description of the arguments to this function. */
|
||||
|
||||
int
|
||||
getInt(const char *arg, int flags, const char *name)
|
||||
{
|
||||
long res;
|
||||
|
||||
res = getNum("getInt", arg, flags, name);
|
||||
|
||||
if (res > INT_MAX || res < INT_MIN)
|
||||
gnFail("getInt", "integer out of range", arg, name);
|
||||
|
||||
return (int) res;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 3-5 */
|
||||
|
||||
/* get_num.h
|
||||
|
||||
Header file for get_num.c.
|
||||
*/
|
||||
#ifndef GET_NUM_H
|
||||
#define GET_NUM_H
|
||||
|
||||
#define GN_NONNEG 01 /* Value must be >= 0 */
|
||||
#define GN_GT_0 02 /* Value must be > 0 */
|
||||
|
||||
/* By default, integers are decimal */
|
||||
#define GN_ANY_BASE 0100 /* Can use any base - like strtol(3) */
|
||||
#define GN_BASE_8 0200 /* Value is expressed in octal */
|
||||
#define GN_BASE_16 0400 /* Value is expressed in hexadecimal */
|
||||
|
||||
long getLong(const char *arg, int flags, const char *name);
|
||||
|
||||
int getInt(const char *arg, int flags, const char *name);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,188 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 59-9 */
|
||||
|
||||
/* inet_sockets.c
|
||||
|
||||
A package of useful routines for Internet domain sockets.
|
||||
*/
|
||||
#define _BSD_SOURCE /* To get NI_MAXHOST and NI_MAXSERV
|
||||
definitions from <netdb.h> */
|
||||
#include "af_unix_hdr.h"
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include "inet_sockets.h" /* Declares functions defined here */
|
||||
|
||||
/* The following arguments are common to several of the routines
|
||||
below:
|
||||
|
||||
'host': NULL for loopback IP address, or
|
||||
a host name or numeric IP address
|
||||
'service': either a name or a port number
|
||||
'type': either SOCK_STREAM or SOCK_DGRAM
|
||||
*/
|
||||
|
||||
/* Create socket and connect it to the address specified by
|
||||
'host' + 'service'/'type'. Return socket descriptor on success,
|
||||
or -1 on error */
|
||||
|
||||
int
|
||||
inetConnect(const char *host, const char *service, int type)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result, *rp;
|
||||
int sfd, s;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
hints.ai_family = AF_UNSPEC; /* Allows IPv4 or IPv6 */
|
||||
hints.ai_socktype = type;
|
||||
|
||||
s = getaddrinfo(host, service, &hints, &result);
|
||||
if (s != 0) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Walk through returned list until we find an address structure
|
||||
that can be used to successfully connect a socket */
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sfd == -1)
|
||||
continue; /* On error, try next address */
|
||||
|
||||
if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
|
||||
break; /* Success */
|
||||
|
||||
/* Connect failed: close this socket and try next address */
|
||||
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return (rp == NULL) ? -1 : sfd;
|
||||
}
|
||||
|
||||
/* Create an Internet domain socket and bind it to the address
|
||||
{ wildcard-IP-address + 'service'/'type' }.
|
||||
If 'doListen' is TRUE, then make this a listening socket (by
|
||||
calling listen() with 'backlog'), with the SO_REUSEADDR option set.
|
||||
If 'addrLen' is not NULL, then use it to return the size of the
|
||||
address structure for the address family for this socket.
|
||||
Return the socket descriptor on success, or -1 on error. */
|
||||
|
||||
static int /* Public interfaces: inetBind() and inetListen() */
|
||||
inetPassiveSocket(const char *service, int type, socklen_t *addrlen,
|
||||
Boolean doListen, int backlog)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result, *rp;
|
||||
int sfd, optval, s;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
hints.ai_socktype = type;
|
||||
hints.ai_family = AF_UNSPEC; /* Allows IPv4 or IPv6 */
|
||||
hints.ai_flags = AI_PASSIVE; /* Use wildcard IP address */
|
||||
|
||||
s = getaddrinfo(NULL, service, &hints, &result);
|
||||
if (s != 0)
|
||||
return -1;
|
||||
|
||||
/* Walk through returned list until we find an address structure
|
||||
that can be used to successfully create and bind a socket */
|
||||
|
||||
optval = 1;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sfd == -1)
|
||||
continue; /* On error, try next address */
|
||||
|
||||
if (doListen) {
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval,
|
||||
sizeof(optval)) == -1) {
|
||||
close(sfd);
|
||||
freeaddrinfo(result);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
|
||||
break; /* Success */
|
||||
|
||||
/* bind() failed: close this socket and try next address */
|
||||
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
if (rp != NULL && doListen) {
|
||||
if (listen(sfd, backlog) == -1) {
|
||||
freeaddrinfo(result);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (rp != NULL && addrlen != NULL)
|
||||
*addrlen = rp->ai_addrlen; /* Return address structure size */
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return (rp == NULL) ? -1 : sfd;
|
||||
}
|
||||
|
||||
/* Create stream socket, bound to wildcard IP address + port given in
|
||||
'service'. Make the socket a listening socket, with the specified
|
||||
'backlog'. Return socket descriptor on success, or -1 on error. */
|
||||
|
||||
int
|
||||
inetListen(const char *service, int backlog, socklen_t *addrlen)
|
||||
{
|
||||
return inetPassiveSocket(service, SOCK_STREAM, addrlen, TRUE, backlog);
|
||||
}
|
||||
|
||||
/* Create socket bound to wildcard IP address + port given in
|
||||
'service'. Return socket descriptor on success, or -1 on error. */
|
||||
|
||||
int
|
||||
inetBind(const char *service, int type, socklen_t *addrlen)
|
||||
{
|
||||
return inetPassiveSocket(service, type, addrlen, FALSE, 0);
|
||||
}
|
||||
|
||||
/* Given a socket address in 'addr', whose length is specified in
|
||||
'addrlen', return a null-terminated string containing the host and
|
||||
service names in the form "(hostname, port#)". The string is
|
||||
returned in the buffer pointed to by 'addrStr', and this value is
|
||||
also returned as the function result. The caller must specify the
|
||||
size of the 'addrStr' buffer in 'addrStrLen'. */
|
||||
|
||||
char *
|
||||
inetAddressStr(const struct sockaddr *addr, socklen_t addrlen,
|
||||
char *addrStr, int addrStrLen)
|
||||
{
|
||||
char host[NI_MAXHOST], service[NI_MAXSERV];
|
||||
|
||||
if (getnameinfo(addr, addrlen, host, NI_MAXHOST,
|
||||
service, NI_MAXSERV, NI_NUMERICSERV) == 0)
|
||||
snprintf(addrStr, addrStrLen, "(%s, %s)", host, service);
|
||||
else
|
||||
snprintf(addrStr, addrStrLen, "(?UNKNOWN?)");
|
||||
|
||||
return addrStr;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 59-8 */
|
||||
|
||||
/* inet_sockets.h
|
||||
|
||||
Header file for inet_sockets.c.
|
||||
*/
|
||||
#ifndef INET_SOCKETS_H
|
||||
#define INET_SOCKETS_H /* Prevent accidental double inclusion */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
int inetConnect(const char *host, const char *service, int type);
|
||||
|
||||
int inetListen(const char *service, int backlog, socklen_t *addrlen);
|
||||
|
||||
int inetBind(const char *service, int type, socklen_t *addrlen);
|
||||
|
||||
char *inetAddressStr(const struct sockaddr *addr, socklen_t addrlen,
|
||||
char *addrStr, int addrStrLen);
|
||||
|
||||
#define IS_ADDR_STR_LEN 4096
|
||||
/* Suggested length for string buffer that caller
|
||||
should pass to inetAddressStr(). Must be greater
|
||||
than (NI_MAXHOST + NI_MAXSERV + 4) */
|
||||
#endif
|
|
@ -0,0 +1,117 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 64-2 */
|
||||
|
||||
/* pty_fork.c
|
||||
|
||||
Implements ptyFork(), a function that creates a child process connected to
|
||||
the parent (i.e., the calling process) via a pseudoterminal (pty). The child
|
||||
is placed in a new session, with the pty slave as its controlling terminal,
|
||||
and its standard input, output, and error connected to the pty slave.
|
||||
|
||||
In the parent, 'masterFd' is used to return the file descriptor for the
|
||||
pty master.
|
||||
|
||||
If 'slaveName' is non-NULL, then it is used to return the name of the pty
|
||||
slave. If 'slaveName' is not NULL, then 'snLen' should be set to indicate
|
||||
the size of the buffer pointed to by 'slaveName'.
|
||||
|
||||
If 'slaveTermios' and 'slaveWS' are non-NULL, then they are used respectively
|
||||
to set the terminal attributes and window size of the pty slave.
|
||||
|
||||
Returns:
|
||||
in child: 0
|
||||
in parent: PID of child or -1 on error
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "pty_master_open.h"
|
||||
#include "pty_fork.h" /* Declares ptyFork() */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define MAX_SNAME 1000 /* Maximum size for pty slave name */
|
||||
|
||||
pid_t
|
||||
ptyFork(int *masterFd, char *slaveName, size_t snLen,
|
||||
const struct termios *slaveTermios, const struct winsize *slaveWS)
|
||||
{
|
||||
int mfd, slaveFd, savedErrno;
|
||||
pid_t childPid;
|
||||
char slname[MAX_SNAME];
|
||||
|
||||
mfd = ptyMasterOpen(slname, MAX_SNAME);
|
||||
if (mfd == -1)
|
||||
return -1;
|
||||
|
||||
if (slaveName != NULL) { /* Return slave name to caller */
|
||||
if (strlen(slname) < snLen) {
|
||||
strncpy(slaveName, slname, snLen);
|
||||
|
||||
} else { /* 'slaveName' was too small */
|
||||
close(mfd);
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
childPid = fork();
|
||||
|
||||
if (childPid == -1) { /* fork() failed */
|
||||
savedErrno = errno; /* close() might change 'errno' */
|
||||
close(mfd); /* Don't leak file descriptors */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (childPid != 0) { /* Parent */
|
||||
*masterFd = mfd; /* Only parent gets master fd */
|
||||
return childPid; /* Like parent of fork() */
|
||||
}
|
||||
|
||||
/* Child falls through to here */
|
||||
|
||||
if (setsid() == -1) /* Start a new session */
|
||||
err_exit("ptyFork:setsid");
|
||||
|
||||
close(mfd); /* Not needed in child */
|
||||
|
||||
slaveFd = open(slname, O_RDWR); /* Becomes controlling tty */
|
||||
if (slaveFd == -1)
|
||||
err_exit("ptyFork:open-slave");
|
||||
|
||||
/* #ifdef TIOCSCTTY /\* Acquire controlling tty on BSD *\/ */
|
||||
/* if (ioctl(slaveFd, TIOCSCTTY, 0) == -1) */
|
||||
/* err_exit("ptyFork:ioctl-TIOCSCTTY"); */
|
||||
/* #endif */
|
||||
|
||||
if (slaveTermios != NULL) /* Set slave tty attributes */
|
||||
if (tcsetattr(slaveFd, TCSANOW, slaveTermios) == -1)
|
||||
err_exit("ptyFork:tcsetattr");
|
||||
|
||||
if (slaveWS != NULL) /* Set slave tty window size */
|
||||
if (ioctl(slaveFd, TIOCSWINSZ, slaveWS) == -1)
|
||||
err_exit("ptyFork:ioctl-TIOCSWINSZ");
|
||||
|
||||
/* Duplicate pty slave to be child's stdin, stdout, and stderr */
|
||||
|
||||
if (dup2(slaveFd, STDIN_FILENO) != STDIN_FILENO)
|
||||
err_exit("ptyFork:dup2-STDIN_FILENO");
|
||||
if (dup2(slaveFd, STDOUT_FILENO) != STDOUT_FILENO)
|
||||
err_exit("ptyFork:dup2-STDOUT_FILENO");
|
||||
if (dup2(slaveFd, STDERR_FILENO) != STDERR_FILENO)
|
||||
err_exit("ptyFork:dup2-STDERR_FILENO");
|
||||
|
||||
if (slaveFd > STDERR_FILENO) /* Safety check */
|
||||
close(slaveFd); /* No longer need this fd */
|
||||
|
||||
return 0; /* Like child of fork() */
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Header file for Listing 64-2 */
|
||||
|
||||
/* pty_fork.h
|
||||
|
||||
Header file for pty_fork.c.
|
||||
*/
|
||||
#ifndef FORK_PTY_H
|
||||
#define FORK_PTY_H
|
||||
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
pid_t ptyFork(int *masterFd, char *slaveName, size_t snLen,
|
||||
const struct termios *slaveTermios, const struct winsize *slaveWS);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,93 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 64-1 */
|
||||
|
||||
/* pty_master_open.c
|
||||
|
||||
Implement our ptyMasterOpen() function, based on UNIX 98 pseudoterminals.
|
||||
See comments below.
|
||||
|
||||
See also pty_master_open_bsd.c.
|
||||
*/
|
||||
#if ! defined(__sun)
|
||||
/* Prevents ptsname() declaration being visible on Solaris 8 */
|
||||
#if ! defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include "pty_master_open.h" /* Declares ptyMasterOpen() */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
/* Some implementations don't have posix_openpt() */
|
||||
|
||||
#if defined(__sun) /* Not on Solaris 8 */
|
||||
#define NO_POSIX_OPENPT
|
||||
#endif
|
||||
|
||||
#ifdef NO_POSIX_OPENPT
|
||||
|
||||
static int
|
||||
posix_openpt(int flags)
|
||||
{
|
||||
return open("/dev/ptmx", flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Open a pty master, returning file descriptor, or -1 on error.
|
||||
On successful completion, the name of the corresponding pty
|
||||
slave is returned in 'slaveName'. 'snLen' should be set to
|
||||
indicate the size of the buffer pointed to by 'slaveName'. */
|
||||
|
||||
int
|
||||
ptyMasterOpen(char *slaveName, size_t snLen)
|
||||
{
|
||||
int masterFd, savedErrno;
|
||||
char *p;
|
||||
|
||||
masterFd = posix_openpt(O_RDWR | O_NOCTTY); /* Open pty master */
|
||||
if (masterFd == -1)
|
||||
return -1;
|
||||
|
||||
if (grantpt(masterFd) == -1) { /* Grant access to slave pty */
|
||||
savedErrno = errno;
|
||||
close(masterFd); /* Might change 'errno' */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (unlockpt(masterFd) == -1) { /* Unlock slave pty */
|
||||
savedErrno = errno;
|
||||
close(masterFd); /* Might change 'errno' */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = ptsname(masterFd); /* Get slave pty name */
|
||||
if (p == NULL) {
|
||||
savedErrno = errno;
|
||||
close(masterFd); /* Might change 'errno' */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(p) < snLen) {
|
||||
strncpy(slaveName, p, snLen);
|
||||
} else { /* Return an error if buffer too small */
|
||||
close(masterFd);
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return masterFd;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Header file for Listing 64-1 */
|
||||
|
||||
/* pty_open.h
|
||||
|
||||
Header file for pty_open.c (and pty_master_open_bsd.c).
|
||||
*/
|
||||
#ifndef PTY_MASTER_OPEN_H
|
||||
#define PTY_MASTER_OPEN_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int ptyMasterOpen(char *slaveName, size_t snLen);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,73 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 59-1 */
|
||||
|
||||
/* read_line.c
|
||||
|
||||
Implementation of readLine().
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "read_line.h" /* Declaration of readLine() */
|
||||
|
||||
/* Read characters from 'fd' until a newline is encountered. If a newline
|
||||
character is not encountered in the first (n - 1) bytes, then the excess
|
||||
characters are discarded. The returned string placed in 'buf' is
|
||||
null-terminated and includes the newline character if it was read in the
|
||||
first (n - 1) bytes. The function return value is the number of bytes
|
||||
placed in buffer (which includes the newline character if encountered,
|
||||
but excludes the terminating null byte). */
|
||||
|
||||
ssize_t
|
||||
readLine(int fd, void *buffer, size_t n)
|
||||
{
|
||||
ssize_t numRead; /* # of bytes fetched by last read() */
|
||||
size_t totRead; /* Total bytes read so far */
|
||||
char *buf;
|
||||
char ch;
|
||||
|
||||
if (n <= 0 || buffer == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = buffer; /* No pointer arithmetic on "void *" */
|
||||
|
||||
totRead = 0;
|
||||
for (;;) {
|
||||
numRead = read(fd, &ch, 1);
|
||||
|
||||
if (numRead == -1) {
|
||||
if (errno == EINTR) /* Interrupted --> restart read() */
|
||||
continue;
|
||||
else
|
||||
return -1; /* Some other error */
|
||||
|
||||
} else if (numRead == 0) { /* EOF */
|
||||
if (totRead == 0) /* No bytes read; return 0 */
|
||||
return 0;
|
||||
else /* Some bytes read; add '\0' */
|
||||
break;
|
||||
|
||||
} else { /* 'numRead' must be 1 if we get here */
|
||||
if (totRead < n - 1) { /* Discard > (n - 1) bytes */
|
||||
totRead++;
|
||||
*buf++ = ch;
|
||||
}
|
||||
|
||||
if (ch == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*buf = '\0';
|
||||
return totRead;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Header file for Listing 59-1 */
|
||||
|
||||
/* read_line.h
|
||||
|
||||
Header file for read_line.c.
|
||||
*/
|
||||
#ifndef READ_LINE_H
|
||||
#define READ_LINE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
ssize_t readLine(int fd, void *buffer, size_t n);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,146 @@
|
|||
/*************************************************************************\
|
||||
* 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));
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*************************************************************************\
|
||||
* 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.h
|
||||
|
||||
Functions to exchange ancillary data over a UNIX domain socket.
|
||||
*/
|
||||
#ifndef SCM_FUNCTIONS_H
|
||||
#define SCM_FUNCTIONS_H /* Prevent accidental double inclusion */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
int sendfd(int sockfd, int fd);
|
||||
|
||||
int recvfd(int sockfd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,89 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 62-3 */
|
||||
|
||||
/* tty_functions.c
|
||||
|
||||
Implement ttySetCbreak() and ttySetRaw().
|
||||
*/
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include "tty_functions.h" /* Declares functions defined here */
|
||||
|
||||
/* Place terminal referred to by 'fd' in cbreak mode (noncanonical mode
|
||||
with echoing turned off). This function assumes that the terminal is
|
||||
currently in cooked mode (i.e., we shouldn't call it if the terminal
|
||||
is currently in raw mode, since it does not undo all of the changes
|
||||
made by the ttySetRaw() function below). Return 0 on success, or -1
|
||||
on error. If 'prevTermios' is non-NULL, then use the buffer to which
|
||||
it points to return the previous terminal settings. */
|
||||
|
||||
int
|
||||
ttySetCbreak(int fd, struct termios *prevTermios)
|
||||
{
|
||||
struct termios t;
|
||||
|
||||
if (tcgetattr(fd, &t) == -1)
|
||||
return -1;
|
||||
|
||||
if (prevTermios != NULL)
|
||||
*prevTermios = t;
|
||||
|
||||
t.c_lflag &= ~(ICANON | ECHO);
|
||||
t.c_lflag |= ISIG;
|
||||
|
||||
t.c_iflag &= ~ICRNL;
|
||||
|
||||
t.c_cc[VMIN] = 1; /* Character-at-a-time input */
|
||||
t.c_cc[VTIME] = 0; /* with blocking */
|
||||
|
||||
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Place terminal referred to by 'fd' in raw mode (noncanonical mode
|
||||
with all input and output processing disabled). Return 0 on success,
|
||||
or -1 on error. If 'prevTermios' is non-NULL, then use the buffer to
|
||||
which it points to return the previous terminal settings. */
|
||||
|
||||
int
|
||||
ttySetRaw(int fd, struct termios *prevTermios)
|
||||
{
|
||||
struct termios t;
|
||||
|
||||
if (tcgetattr(fd, &t) == -1)
|
||||
return -1;
|
||||
|
||||
if (prevTermios != NULL)
|
||||
*prevTermios = t;
|
||||
|
||||
t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
|
||||
/* Noncanonical mode, disable signals, extended
|
||||
input processing, and echoing */
|
||||
|
||||
t.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
|
||||
INPCK | ISTRIP | IXON | PARMRK);
|
||||
/* Disable special handling of CR, NL, and BREAK.
|
||||
No 8th-bit stripping or parity error handling.
|
||||
Disable START/STOP output flow control. */
|
||||
|
||||
t.c_oflag &= ~OPOST; /* Disable all output processing */
|
||||
|
||||
t.c_cc[VMIN] = 1; /* Character-at-a-time input */
|
||||
t.c_cc[VTIME] = 0; /* with blocking */
|
||||
|
||||
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Header file for Listing 62-3 */
|
||||
|
||||
/* tty_functions.h
|
||||
|
||||
Header file for tty_functions.c.
|
||||
*/
|
||||
#ifndef TTY_FUNCTIONS_H
|
||||
#define TTY_FUNCTIONS_H
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
int ttySetCbreak(int fd, struct termios *prevTermios);
|
||||
|
||||
int ttySetRaw(int fd, struct termios *prevTermios);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,94 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-3:b */
|
||||
|
||||
/* unix_sockets.c
|
||||
|
||||
A package of useful routines for UNIX domain sockets.
|
||||
*/
|
||||
#include "unix_sockets.h" /* Declares functions defined here */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
/* Build a UNIX domain socket address structure for 'path', returning
|
||||
it in 'addr'. Returns -1 on success, or 0 on error. */
|
||||
|
||||
int
|
||||
unixBuildAddress(const char *path, struct sockaddr_un *addr)
|
||||
{
|
||||
if (addr == NULL || path == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(addr, 0, sizeof(struct sockaddr_un));
|
||||
addr->sun_family = AF_UNIX;
|
||||
if (strlen(path) < sizeof(addr->sun_path)) {
|
||||
strncpy(addr->sun_path, path, sizeof(addr->sun_path) - 1);
|
||||
return 0;
|
||||
} else {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a UNIX domain socket of type 'type' and connect it
|
||||
to the remote address specified by the 'path'.
|
||||
Return the socket descriptor on success, or -1 on error */
|
||||
|
||||
int
|
||||
unixConnect(const char *path, int type)
|
||||
{
|
||||
int sd, savedErrno;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (unixBuildAddress(path, &addr) == -1)
|
||||
return -1;
|
||||
|
||||
sd = socket(AF_UNIX, type, 0);
|
||||
if (sd == -1)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr *) &addr,
|
||||
sizeof(struct sockaddr_un)) == -1) {
|
||||
savedErrno = errno;
|
||||
close(sd); /* Might change 'errno' */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
/* Create a UNIX domain socket and bind it to 'path'.
|
||||
Return the socket descriptor on success, or -1 on error. */
|
||||
|
||||
int
|
||||
unixBind(const char *path, int type)
|
||||
{
|
||||
int sd, savedErrno;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if (unixBuildAddress(path, &addr) == -1)
|
||||
return -1;
|
||||
|
||||
sd = socket(AF_UNIX, type, 0);
|
||||
if (sd == -1)
|
||||
return -1;
|
||||
|
||||
if (bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) {
|
||||
savedErrno = errno;
|
||||
close(sd); /* Might change 'errno' */
|
||||
errno = savedErrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sd;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-3:a */
|
||||
|
||||
/* unix_sockets.h
|
||||
|
||||
Header file for unix_sockets.c.
|
||||
*/
|
||||
#ifndef UNIX_SOCKETS_H
|
||||
#define UNIX_SOCKETS_H /* Prevent accidental double inclusion */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
int unixBuildAddress(const char *path, struct sockaddr_un *addr);
|
||||
|
||||
int unixConnect(const char *path, int type);
|
||||
|
||||
int unixBind(const char *path, int type);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
/* Header for msg_peek_sv.c and msg_peek_cl.c */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/peek"
|
||||
|
||||
#define BUF_SIZE 100
|
||||
|
||||
#define BACKLOG 5
|
|
@ -0,0 +1,20 @@
|
|||
#include "msg_peek.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd;
|
||||
ssize_t nread;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
|
||||
/* Copy stdin to socket. */
|
||||
while ((nread = read (STDIN_FILENO, buf, BUF_SIZE)) > 0)
|
||||
if (write (sfd, buf, nread) != nread)
|
||||
errExit ("partial/failed write");
|
||||
|
||||
if (nread < 0)
|
||||
errExit("read");
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#include "msg_peek.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd, cfd;
|
||||
ssize_t nread;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
|
||||
errExit ("remove");
|
||||
|
||||
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixBind");
|
||||
|
||||
if (listen (sfd, BACKLOG) < 0)
|
||||
errExit ("listen");
|
||||
|
||||
cfd = accept (sfd, NULL, NULL);
|
||||
if (cfd < 0)
|
||||
errExit ("accept");
|
||||
|
||||
printf ("peeking...\n");
|
||||
if ((nread = recv (cfd, buf, BUF_SIZE - 1, MSG_PEEK)) < 0)
|
||||
errExit ("recv");
|
||||
buf[nread] = '\0';
|
||||
printf ("reading would yield %zd bytes: %s\n", nread, buf);
|
||||
sleep (1);
|
||||
printf ("reading...\n");
|
||||
if ((nread = read (cfd, buf, BUF_SIZE)) < 0)
|
||||
errExit ("read");
|
||||
buf[nread] = '\0';
|
||||
printf ("read %zd bytes: %s\n", nread, buf);
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
/* pty_slave.h
|
||||
|
||||
Header file for send_pty_master.c and recv_pty_master.c
|
||||
*/
|
||||
#define SOCK_PATH "/tmp/pty_master"
|
|
@ -0,0 +1,5 @@
|
|||
/* pty_slave.h
|
||||
|
||||
Header file for send_pty_slave.c and recv_pty_slave.c
|
||||
*/
|
||||
#define SOCK_PATH "/tmp/pty_slave"
|
|
@ -0,0 +1,41 @@
|
|||
/* Adapted from https://www.oreilly.com/library/view/linux-system-programming/0596009585/ch04.html */
|
||||
|
||||
#include "scatter_gather.h"
|
||||
|
||||
int main ()
|
||||
{
|
||||
char foo[48], bar[51], baz[49];
|
||||
struct iovec iov[3];
|
||||
ssize_t nr;
|
||||
int sfd, cfd;
|
||||
|
||||
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
|
||||
errExit ("remove");
|
||||
|
||||
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixBind");
|
||||
|
||||
if (listen (sfd, BACKLOG) < 0)
|
||||
errExit ("listen");
|
||||
|
||||
cfd = accept (sfd, NULL, NULL);
|
||||
if (cfd < 0)
|
||||
errExit ("accept");
|
||||
|
||||
iov[0].iov_base = foo;
|
||||
iov[0].iov_len = sizeof (foo);
|
||||
iov[1].iov_base = bar;
|
||||
iov[1].iov_len = sizeof (bar);
|
||||
iov[2].iov_base = baz;
|
||||
iov[2].iov_len = sizeof (baz);
|
||||
|
||||
nr = readv (cfd, iov, 3);
|
||||
if (nr < 0)
|
||||
errExit ("readv");
|
||||
printf ("read %zd bytes\n", nr);
|
||||
for (int i = 0; i < 3; i++)
|
||||
printf ("%d: %s", i, (char *) iov[i].iov_base);
|
||||
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#include "af_unix_hdr.h"
|
||||
#include "pty_slave.h"
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "af_unix_hdr.h"
|
||||
#include "pty_master.h"
|
||||
#define BUF_SIZE 100
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int lfd, connfd, ptyfd, junk;
|
||||
|
||||
if (remove (SOCK_PATH) == -1 && errno != ENOENT)
|
||||
errExit ("remove-%s", SOCK_PATH);
|
||||
|
||||
lfd = unixBind (SOCK_PATH, SOCK_STREAM);
|
||||
if (lfd < 0)
|
||||
errExit ("unixBind");
|
||||
printf ("Waiting for sender to connect and send descriptor...\n");
|
||||
if (listen (lfd, 5) < 0)
|
||||
errExit ("listen");
|
||||
connfd = accept (lfd, NULL, NULL);
|
||||
if (connfd < 0)
|
||||
errExit ("accept");
|
||||
ptyfd = recvfd (connfd);
|
||||
if (ptyfd < 0)
|
||||
errExit ("recvfd");
|
||||
printf ("Received descriptor %d.\n", ptyfd);
|
||||
printf ("Writing \"ps\" to that descriptor.\n"
|
||||
"This should appear in the other terminal\n"
|
||||
"and be executed by the shell running there.\n");
|
||||
if (write (ptyfd, "ps\n", 3) != 3)
|
||||
errExit ("write");
|
||||
printf ("Waiting for sender to finish...\n");
|
||||
if (read (connfd, &junk, sizeof junk) < 0)
|
||||
errExit ("read");
|
||||
if (close (ptyfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (lfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (connfd) < 0)
|
||||
errMsg ("close");
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include "af_unix_hdr.h"
|
||||
#include "pty_slave.h"
|
||||
#define BUF_SIZE 100
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int lfd, connfd, ptyfd;
|
||||
|
||||
if (remove (SOCK_PATH) == -1 && errno != ENOENT)
|
||||
errExit ("remove-%s", SOCK_PATH);
|
||||
|
||||
lfd = unixBind (SOCK_PATH, SOCK_STREAM);
|
||||
if (lfd < 0)
|
||||
errExit ("unixBind");
|
||||
printf ("Waiting for sender to connect and send descriptor...\n");
|
||||
if (listen (lfd, 5) < 0)
|
||||
errExit ("listen");
|
||||
connfd = accept (lfd, NULL, NULL);
|
||||
if (connfd < 0)
|
||||
errExit ("accept");
|
||||
ptyfd = recvfd (connfd);
|
||||
if (ptyfd < 0)
|
||||
errExit ("recvfd");
|
||||
printf ("Received descriptor %d.\n", ptyfd);
|
||||
printf ("Writing \"hello\" to that descriptor.\n"
|
||||
"This should appear in the other terminal "
|
||||
"as though it were output by the shell.\n");
|
||||
if (write (ptyfd, "hello\n", 6) != 6)
|
||||
errExit ("write");
|
||||
if (close (ptyfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (lfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (connfd) < 0)
|
||||
errMsg ("close");
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/* Header for readv_socket.c and writev_socket.c */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/scatter"
|
||||
|
||||
#define BACKLOG 5
|
|
@ -0,0 +1,21 @@
|
|||
/*************************************************************************\
|
||||
* 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_cred.h
|
||||
|
||||
Header file used by scm_cred_send.c and scm_cred_recv.c.
|
||||
*/
|
||||
#define _GNU_SOURCE /* To get SCM_CREDENTIALS definition from
|
||||
<sys/socket.h> */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SOCK_PATH "/tmp/scm_cred"
|
|
@ -0,0 +1,173 @@
|
|||
/*************************************************************************\
|
||||
* 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_cred_recv.c
|
||||
|
||||
Used in conjunction with scm_cred_send.c to demonstrate passing of
|
||||
process credentials via a UNIX domain socket.
|
||||
|
||||
This program receives credentials sent to a UNIX domain socket.
|
||||
|
||||
Usage is as shown in the usageErr() call below.
|
||||
|
||||
Credentials 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_cred.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int data, lfd, sfd, optval, opt;
|
||||
ssize_t nr;
|
||||
Boolean useDatagramSocket;
|
||||
struct msghdr msgh;
|
||||
struct iovec iov;
|
||||
struct ucred *ucredp, ucred;
|
||||
|
||||
/* 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(struct ucred))];
|
||||
/* Space large enough to hold a 'ucred' structure */
|
||||
struct cmsghdr align;
|
||||
} controlMsg;
|
||||
struct cmsghdr *cmsgp; /* Pointer used to iterate through
|
||||
headers in ancillary data */
|
||||
socklen_t len;
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* We must set the SO_PASSCRED socket option in order to receive
|
||||
credentials */
|
||||
|
||||
optval = 1;
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
|
||||
errExit("setsockopt");
|
||||
|
||||
/* 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 (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");
|
||||
printf("recvmsg() returned %ld\n", (long) nr);
|
||||
|
||||
if (nr > 0)
|
||||
printf("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(struct ucred)))
|
||||
fatal("bad cmsg header / message length");
|
||||
if (cmsgp->cmsg_level != SOL_SOCKET)
|
||||
fatal("cmsg_level != SOL_SOCKET");
|
||||
if (cmsgp->cmsg_type != SCM_CREDENTIALS)
|
||||
fatal("cmsg_type != SCM_CREDENTIALS");
|
||||
|
||||
/* The data area of the 'cmsghdr' is a 'struct ucred', so assign
|
||||
the address of the data area to a suitable pointer */
|
||||
|
||||
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
|
||||
|
||||
/* Display the credentials from the received data area */
|
||||
|
||||
printf("Received credentials pid=%ld, uid=%ld, gid=%ld\n",
|
||||
(long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid);
|
||||
|
||||
/* The Linux-specific, read-only SO_PEERCRED socket option returns
|
||||
credential information about the peer, as described in socket(7).
|
||||
This operation can be performed on UNIX domain stream sockets and on
|
||||
UNIX domain sockets (stream or datagram) created with socketpair(). */
|
||||
|
||||
len = sizeof(struct ucred);
|
||||
if (getsockopt(sfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
|
||||
errExit("getsockopt");
|
||||
|
||||
printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
|
||||
(long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*************************************************************************\
|
||||
* 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_cred_send.c
|
||||
|
||||
Used in conjunction with scm_cred_recv.c to demonstrate passing of
|
||||
process credentials via a UNIX domain socket.
|
||||
|
||||
This program sends credentials to a UNIX domain socket.
|
||||
|
||||
Usage is as shown in the usageErr() call below.
|
||||
|
||||
Credentials 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_send.c.
|
||||
*/
|
||||
#include "scm_cred.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int data, sfd, opt;
|
||||
ssize_t ns;
|
||||
Boolean useDatagramSocket, noExplicitCreds;
|
||||
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(struct ucred))];
|
||||
/* Space large enough to hold a ucred structure */
|
||||
struct cmsghdr align;
|
||||
} controlMsg;
|
||||
struct cmsghdr *cmsgp; /* Pointer used to iterate through
|
||||
headers in ancillary data */
|
||||
|
||||
/* Parse command-line options */
|
||||
|
||||
useDatagramSocket = FALSE;
|
||||
noExplicitCreds = FALSE;
|
||||
|
||||
while ((opt = getopt(argc, argv, "dn")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
useDatagramSocket = TRUE;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
noExplicitCreds = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
usageErr("%s [-d] [-n] [data [PID [UID [GID]]]]\n"
|
||||
" -d use datagram socket\n"
|
||||
" -n don't construct explicit "
|
||||
"credentials structure\n", argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
msgh.msg_iov = &iov;
|
||||
msgh.msg_iovlen = 1;
|
||||
iov.iov_base = &data;
|
||||
iov.iov_len = sizeof(int);
|
||||
|
||||
/* Data is optionally taken from command line */
|
||||
|
||||
data = (argc > optind) ? atoi(argv[optind]) : 12345;
|
||||
fprintf(stderr, "Sending data = %d\n", data);
|
||||
|
||||
if (noExplicitCreds) {
|
||||
|
||||
/* Don't construct an explicit credentials structure. (It is not
|
||||
necessary to do so, if we just want the receiver to receive
|
||||
our real credentials.) */
|
||||
|
||||
printf("Not explicitly sending a credentials structure\n");
|
||||
msgh.msg_control = NULL;
|
||||
msgh.msg_controllen = 0;
|
||||
|
||||
} else {
|
||||
struct ucred *ucredp;
|
||||
|
||||
/* Set 'msgh' fields to describe the ancillary data buffer */
|
||||
|
||||
msgh.msg_control = controlMsg.buf;
|
||||
msgh.msg_controllen = sizeof(controlMsg.buf);
|
||||
|
||||
/* The control message buffer must be zero-initialized in order for the
|
||||
CMSG_NXTHDR() macro to work correctly. Although we don't need to use
|
||||
CMSG_NXTHDR() in this example (because there is only one block of
|
||||
ancillary data), we show this step to demonstrate best practice */
|
||||
|
||||
memset(controlMsg.buf, 0, sizeof(controlMsg.buf));
|
||||
|
||||
/* Set message header to describe the ancillary data that
|
||||
we want to send */
|
||||
|
||||
cmsgp = CMSG_FIRSTHDR(&msgh);
|
||||
cmsgp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
cmsgp->cmsg_level = SOL_SOCKET;
|
||||
cmsgp->cmsg_type = SCM_CREDENTIALS;
|
||||
|
||||
/* Set 'ucredp' to point to the data area in the 'cmsghdr' */
|
||||
|
||||
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
|
||||
|
||||
/* Use sender's own PID, real UID, and real GID, unless
|
||||
alternate values were supplied on the command line */
|
||||
|
||||
ucredp->pid = getpid();
|
||||
if (argc > optind + 1 && strcmp(argv[optind + 1], "-") != 0)
|
||||
ucredp->pid = atoi(argv[optind + 1]);
|
||||
|
||||
ucredp->uid = getuid();
|
||||
if (argc > optind + 2 && strcmp(argv[optind + 2], "-") != 0)
|
||||
ucredp->uid = atoi(argv[optind + 2]);
|
||||
|
||||
ucredp->gid = getgid();
|
||||
if (argc > optind + 3 && strcmp(argv[optind + 3], "-") != 0)
|
||||
ucredp->gid = atoi(argv[optind + 3]);
|
||||
|
||||
printf("Send credentials pid=%ld, uid=%ld, gid=%ld\n",
|
||||
(long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid);
|
||||
}
|
||||
|
||||
/* Connect to the peer socket */
|
||||
|
||||
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
|
||||
if (sfd == -1)
|
||||
errExit("unixConnect");
|
||||
|
||||
/* Send real plus ancillary data */
|
||||
|
||||
ns = sendmsg(sfd, &msgh, 0);
|
||||
if (ns == -1)
|
||||
errExit("sendmsg");
|
||||
|
||||
printf("sendmsg() returned %ld\n", (long) ns);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*************************************************************************\
|
||||
* 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.h
|
||||
|
||||
Header file used by scm_multi_send.c and scm_multi_recv.c.
|
||||
*/
|
||||
#define _GNU_SOURCE /* To get SCM_CREDENTIALS definition from
|
||||
<sys/socket.h> */
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "unix_sockets.h" /* Declares our socket functions */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SOCK_PATH "/tmp/scm_multi"
|
||||
#define MAX_FDS 1024 /* Maximum number of file descriptors we'll
|
||||
attempt to exchange in ancillary data */
|
|
@ -0,0 +1,249 @@
|
|||
/*************************************************************************\
|
||||
* 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_recv.c
|
||||
|
||||
Used in conjunction with scm_multi_send.c to demonstrate passing of
|
||||
ancillary data containing multiple 'msghdr' structures on a UNIX
|
||||
domain socket.
|
||||
|
||||
Usage is as shown in the usageErr() call below.
|
||||
|
||||
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"
|
||||
|
||||
#define BUF_SIZE 100
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int data, lfd, sfd, opt, optval, j;
|
||||
ssize_t NumReceived;
|
||||
Boolean useDatagramSocket;
|
||||
int optControlMsgSize;
|
||||
struct msghdr msgh;
|
||||
struct iovec iov;
|
||||
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 */
|
||||
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 */
|
||||
|
||||
/* 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[MAX_FDS])) +
|
||||
CMSG_SPACE(sizeof(struct ucred));
|
||||
controlMsg = malloc(controlMsgSize);
|
||||
if (controlMsg == NULL)
|
||||
errExit("malloc");
|
||||
|
||||
/* Parse command-line options */
|
||||
|
||||
useDatagramSocket = FALSE;
|
||||
optControlMsgSize = -1;
|
||||
|
||||
while ((opt = getopt(argc, argv, "dn:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
useDatagramSocket = TRUE;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
optControlMsgSize = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
usageErr("%s [-d]\n"
|
||||
" -d use datagram socket\n"
|
||||
" -n nbytes limit on size of received "
|
||||
"ancillary data\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");
|
||||
}
|
||||
|
||||
/* We must set the SO_PASSCRED socket option in order to receive
|
||||
credentials */
|
||||
|
||||
optval = 1;
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
|
||||
errExit("setsockopt");
|
||||
|
||||
/* 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 a 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.
|
||||
|
||||
The 'optControlMsgSize' value (specified as a command-line option)
|
||||
can be used to artifically limit the size of the received ancillary
|
||||
data. This can be used to demonstrate that when the buffer size is
|
||||
too small, the list of received file descriptors is truncated, and
|
||||
the excess file descriptors are automatically closed. */
|
||||
|
||||
msgh.msg_control = controlMsg;
|
||||
msgh.msg_controllen = (optControlMsgSize == -1) ?
|
||||
controlMsgSize : optControlMsgSize;
|
||||
|
||||
/* Receive real plus ancillary data */
|
||||
|
||||
NumReceived = recvmsg(sfd, &msgh, 0);
|
||||
if (NumReceived == -1)
|
||||
errExit("recvmsg");
|
||||
|
||||
printf("recvmsg() returned %ld\n", (long) NumReceived);
|
||||
|
||||
if (NumReceived > 0)
|
||||
printf("Received data = %d\n", data);
|
||||
|
||||
if (optControlMsgSize != -1) {
|
||||
char cbuf[1000];
|
||||
|
||||
/* Display this process's set of open file descriptors via */
|
||||
/* /proc/PID/fd */
|
||||
|
||||
printf("=================================\n");
|
||||
snprintf(cbuf, sizeof(cbuf), "ls -l /proc/%ld/fd", (long) getpid());
|
||||
system(cbuf);
|
||||
printf("=================================\n");
|
||||
}
|
||||
|
||||
/* Check to see if the ancillary data was truncated */
|
||||
|
||||
if (msgh.msg_flags & MSG_CTRUNC)
|
||||
printf("********** Ancillary data was truncated!!! **********\n");
|
||||
|
||||
/* Walk through the series of headers in the ancillary data */
|
||||
|
||||
for (cmsgp = CMSG_FIRSTHDR(&msgh);
|
||||
cmsgp != NULL;
|
||||
cmsgp = CMSG_NXTHDR(&msgh, cmsgp)) {
|
||||
|
||||
printf("=================================\n");
|
||||
printf("cmsg_len: %ld\n", (long) cmsgp->cmsg_len);
|
||||
|
||||
/* Check that 'cmsg_level' is as expected */
|
||||
|
||||
if (cmsgp->cmsg_level != SOL_SOCKET)
|
||||
fatal("cmsg_level != SOL_SOCKET");
|
||||
|
||||
switch (cmsgp->cmsg_type) {
|
||||
|
||||
case SCM_RIGHTS: /* Header containing file descriptors */
|
||||
|
||||
/* The number of file descriptors is the size of the control
|
||||
message block minus the size that would be allocated for
|
||||
a zero-length data block (i.e., the size of the 'cmsghdr'
|
||||
structure plus padding), divided by the size of a file
|
||||
descriptor */
|
||||
|
||||
fdCnt = (cmsgp->cmsg_len - CMSG_LEN(0)) / sizeof(int);
|
||||
printf("SCM_RIGHTS: received %d file descriptors\n", fdCnt);
|
||||
|
||||
/* Set 'fdList' to point to the first descriptor in the
|
||||
control message data */
|
||||
|
||||
fdList = ((int *) CMSG_DATA(cmsgp));
|
||||
|
||||
/* For each of the received file descriptors, display the file
|
||||
descriptor number and read and display the file content */
|
||||
|
||||
for (j = 0; j < fdCnt; j++) {
|
||||
printf("--- [%d] Received FD %d\n", j, fdList[j]);
|
||||
|
||||
for (;;) {
|
||||
char buf[BUF_SIZE];
|
||||
ssize_t numRead;
|
||||
|
||||
numRead = read(fdList[j], buf, BUF_SIZE);
|
||||
if (numRead == -1)
|
||||
errExit("read");
|
||||
|
||||
if (numRead == 0)
|
||||
break;
|
||||
|
||||
write(STDOUT_FILENO, buf, numRead);
|
||||
}
|
||||
|
||||
if (close(fdList[j]) == -1)
|
||||
errExit("close");
|
||||
}
|
||||
break;
|
||||
|
||||
case SCM_CREDENTIALS: /* Header containing credentials */
|
||||
|
||||
/* Check validity of the 'cmsghdr' */
|
||||
|
||||
if (cmsgp->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
|
||||
fatal("cmsg data has incorrect size");
|
||||
|
||||
/* The data in this control message block is a 'struct ucred' */
|
||||
|
||||
ucredp = (struct ucred *) CMSG_DATA(cmsgp);
|
||||
printf("SCM_CREDENTIALS: pid=%ld, uid=%ld, gid=%ld\n",
|
||||
(long) ucredp->pid, (long) ucredp->uid,
|
||||
(long) ucredp->gid);
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("Bad cmsg_type (%d)", cmsgp->cmsg_type);
|
||||
}
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
/*************************************************************************\
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*************************************************************************\
|
||||
* 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.h
|
||||
|
||||
Header file used by scm_rights_send.c and scm_rights_recv.c.
|
||||
*/
|
||||
#include <fcntl.h>
|
||||
#include "unix_sockets.h" /* Declares our unix*() socket functions */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SOCK_PATH "/tmp/scm_rights"
|
|
@ -0,0 +1,169 @@
|
|||
/*************************************************************************\
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*************************************************************************\
|
||||
* 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_send.c
|
||||
|
||||
Used in conjunction with scm_rights_recv.c to demonstrate passing of
|
||||
file descriptors via a UNIX domain socket.
|
||||
|
||||
This program sends a file descriptor 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"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int data, sfd, opt, fd;
|
||||
ssize_t ns;
|
||||
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] file\n"
|
||||
" -d use datagram socket\n", argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != optind + 1)
|
||||
usageErr("%s [-d] file\n", argv[0]);
|
||||
|
||||
/* Open the file named on the command line */
|
||||
|
||||
fd = open(argv[optind], O_RDONLY);
|
||||
if (fd == -1)
|
||||
errExit("open");
|
||||
|
||||
/* 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 */
|
||||
|
||||
msgh.msg_iov = &iov;
|
||||
msgh.msg_iovlen = 1;
|
||||
iov.iov_base = &data;
|
||||
iov.iov_len = sizeof(int);
|
||||
data = 12345;
|
||||
fprintf(stderr, "Sending data = %d\n", data);
|
||||
|
||||
/* Set 'msgh' fields to describe the ancillary data buffer */
|
||||
|
||||
msgh.msg_control = controlMsg.buf;
|
||||
msgh.msg_controllen = sizeof(controlMsg.buf);
|
||||
|
||||
/* The control message buffer must be zero-initialized in order
|
||||
for the CMSG_NXTHDR() macro to work correctly. Although we
|
||||
don't need to use CMSG_NXTHDR() in this example (because
|
||||
there is only one block of ancillary data), we show this
|
||||
step to demonstrate best practice */
|
||||
|
||||
memset(controlMsg.buf, 0, sizeof(controlMsg.buf));
|
||||
|
||||
/* Set message header to describe the ancillary data that
|
||||
we want to send */
|
||||
|
||||
cmsgp = CMSG_FIRSTHDR(&msgh);
|
||||
cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsgp->cmsg_level = SOL_SOCKET;
|
||||
cmsgp->cmsg_type = SCM_RIGHTS;
|
||||
*((int *) CMSG_DATA(cmsgp)) = fd;
|
||||
|
||||
/* Connect to the peer socket */
|
||||
|
||||
sfd = unixConnect(SOCK_PATH, useDatagramSocket ? SOCK_DGRAM : SOCK_STREAM);
|
||||
if (sfd == -1)
|
||||
errExit("unixConnect");
|
||||
|
||||
fprintf(stderr, "Sending FD %d\n", fd);
|
||||
|
||||
/* Send real plus ancillary data */
|
||||
|
||||
ns = sendmsg(sfd, &msgh, 0);
|
||||
if (ns == -1)
|
||||
errExit("sendmsg");
|
||||
|
||||
fprintf(stderr, "sendmsg() returned %ld\n", (long) ns);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#include "select_test.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd, flags;
|
||||
fd_set writefds;
|
||||
size_t nwritten = 0;
|
||||
ssize_t nw;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
flags = fcntl (sfd, F_GETFL);
|
||||
if (fcntl (sfd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||
errExit ("fcntl");
|
||||
|
||||
printf ("waiting for socket to be ready for write...\n");
|
||||
FD_ZERO (&writefds);
|
||||
FD_SET (sfd, &writefds);
|
||||
if (select (sfd + 1, NULL, &writefds, NULL, NULL) < 0)
|
||||
errExit ("select");
|
||||
if (FD_ISSET (sfd, &writefds))
|
||||
printf ("ready for write, writing until buffer full\n");
|
||||
else
|
||||
errExit ("something's wrong");
|
||||
while (1)
|
||||
{
|
||||
nw = write (sfd, buf, BUF_SIZE);
|
||||
if (nw < 0)
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
{
|
||||
printf ("buffer full\n");
|
||||
break;
|
||||
}
|
||||
else
|
||||
errExit ("write");
|
||||
}
|
||||
nwritten += nw;
|
||||
}
|
||||
printf ("wrote %zu bytes\n", nwritten);
|
||||
printf ("waiting for write ready again...\n");
|
||||
FD_ZERO (&writefds);
|
||||
FD_SET (sfd, &writefds);
|
||||
if (select (sfd + 1, NULL, &writefds, NULL, NULL) < 0)
|
||||
errExit ("select");
|
||||
if (FD_ISSET (sfd, &writefds))
|
||||
printf ("ready for write, writing once more\n");
|
||||
if ((nw = write (sfd, buf, BUF_SIZE)) < 0)
|
||||
errExit ("write");
|
||||
nwritten += nw;
|
||||
printf ("wrote %zd more bytes for a total of %zu\n", nw, nwritten);
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#include "select_test.h"
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd, cfd, flags;
|
||||
fd_set readfds;
|
||||
size_t nread = 0;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
|
||||
errExit ("remove");
|
||||
|
||||
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixBind");
|
||||
|
||||
if (listen (sfd, BACKLOG) < 0)
|
||||
errExit ("listen");
|
||||
|
||||
printf ("waiting for connection request...\n");
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (sfd, &readfds);
|
||||
if (select (sfd + 1, &readfds, NULL, NULL, NULL) < 0)
|
||||
errExit ("select");
|
||||
if (FD_ISSET (sfd, &readfds))
|
||||
printf ("connection request received; accepting\n");
|
||||
else
|
||||
errExit ("something's wrong");
|
||||
cfd = accept (sfd, NULL, NULL);
|
||||
if (cfd < 0)
|
||||
errExit ("accept");
|
||||
|
||||
flags = fcntl (cfd, F_GETFL);
|
||||
if (fcntl (cfd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||
errExit ("fcntl");
|
||||
|
||||
printf ("slowly reading from socket...\n");
|
||||
while (1)
|
||||
{
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (cfd, &readfds);
|
||||
if (select (cfd + 1, &readfds, NULL, NULL, NULL) < 0)
|
||||
errExit ("select");
|
||||
if (!FD_ISSET (cfd, &readfds))
|
||||
errExit ("something's wrong");
|
||||
ssize_t nr = read (cfd, buf, 10);
|
||||
if (nr < 0)
|
||||
{
|
||||
if (errno == EPIPE)
|
||||
break;
|
||||
else
|
||||
errExit ("read");
|
||||
}
|
||||
else if (nr == 0)
|
||||
break;
|
||||
nread += nr;
|
||||
}
|
||||
printf ("read %zu bytes\n", nread);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/* Header for select_sv.c and select_cl.c */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/select"
|
||||
|
||||
#define BUF_SIZE 65527 /* MAX_AF_PKT_LEN - sizeof (af_unix_pkt_hdr_t) */
|
||||
|
||||
#define BACKLOG 5
|
|
@ -0,0 +1,143 @@
|
|||
/* Adapted from Kerrisk's script.c by Ken Brown */
|
||||
|
||||
/*
|
||||
Create a pty pair and fork/exec a shell running on the slave. Send
|
||||
the master fd across an AF_UNIX socket to a process running
|
||||
recv_pty_slave.
|
||||
*/
|
||||
|
||||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 64-3 */
|
||||
|
||||
/* script.c
|
||||
|
||||
A simple version of script(1).
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <termios.h>
|
||||
#if ! defined(__hpux)
|
||||
/* HP-UX 11 doesn't have this header file */
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include "pty_fork.h" /* Declaration of ptyFork() */
|
||||
#include "tty_functions.h" /* Declaration of ttySetRaw() */
|
||||
#include "af_unix_hdr.h"
|
||||
#include "pty_master.h"
|
||||
|
||||
#define BUF_SIZE 256
|
||||
#define MAX_SNAME 1000
|
||||
|
||||
struct termios ttyOrig;
|
||||
|
||||
static void /* Reset terminal mode on program exit */
|
||||
ttyReset(void)
|
||||
{
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &ttyOrig) == -1)
|
||||
errExit("tcsetattr");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char slaveName[MAX_SNAME];
|
||||
char *shell;
|
||||
int masterFd, connFd, junk;
|
||||
struct winsize ws;
|
||||
fd_set inFds;
|
||||
char buf[BUF_SIZE];
|
||||
ssize_t numRead;
|
||||
pid_t childPid;
|
||||
|
||||
/* Retrieve the attributes of terminal on which we are started */
|
||||
|
||||
if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1)
|
||||
errExit("tcgetattr");
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
|
||||
errExit("ioctl-TIOCGWINSZ");
|
||||
|
||||
/* Create a child process, with parent and child connected via a
|
||||
pty pair. The child is connected to the pty slave and its terminal
|
||||
attributes are set to be the same as those retrieved above. */
|
||||
|
||||
childPid = ptyFork(&masterFd, slaveName, MAX_SNAME, &ttyOrig, &ws);
|
||||
if (childPid == -1)
|
||||
errExit("ptyFork");
|
||||
|
||||
if (childPid == 0) { /* Child: execute a shell on pty slave */
|
||||
|
||||
/* If the SHELL variable is set, use its value to determine
|
||||
the shell execed in child. Otherwise use /bin/sh. */
|
||||
|
||||
shell = getenv("SHELL");
|
||||
if (shell == NULL || *shell == '\0')
|
||||
shell = "/bin/sh";
|
||||
|
||||
execlp(shell, shell, (char *) NULL);
|
||||
errExit("execlp"); /* If we get here, something went wrong */
|
||||
}
|
||||
|
||||
/* Parent */
|
||||
|
||||
if ((connFd = unixConnect (SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
|
||||
/* Send master fd across the socket. */
|
||||
if (sendfd (connFd, masterFd) < 0)
|
||||
errExit ("sendfd");
|
||||
|
||||
/* Place terminal in raw mode so that we can pass all terminal
|
||||
input to the pseudoterminal master untouched */
|
||||
|
||||
ttySetRaw(STDIN_FILENO, &ttyOrig);
|
||||
|
||||
if (atexit(ttyReset) != 0)
|
||||
errExit("atexit");
|
||||
|
||||
/* Loop monitoring terminal and pty master for input. If the
|
||||
terminal is ready for input, then read some bytes and write
|
||||
them to the pty master. If the pty master is ready for input,
|
||||
then read some bytes and write them to the terminal. */
|
||||
|
||||
for (;;) {
|
||||
FD_ZERO(&inFds);
|
||||
FD_SET(STDIN_FILENO, &inFds);
|
||||
FD_SET(masterFd, &inFds);
|
||||
|
||||
if (select(masterFd + 1, &inFds, NULL, NULL, NULL) == -1)
|
||||
errExit("select");
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &inFds)) { /* stdin --> pty */
|
||||
numRead = read(STDIN_FILENO, buf, BUF_SIZE);
|
||||
if (numRead <= 0)
|
||||
break;
|
||||
|
||||
if (write(masterFd, buf, numRead) != numRead)
|
||||
fatal("partial/failed write (masterFd)");
|
||||
}
|
||||
|
||||
if (FD_ISSET(masterFd, &inFds)) { /* pty --> stdout */
|
||||
numRead = read(masterFd, buf, BUF_SIZE);
|
||||
if (numRead <= 0)
|
||||
break;
|
||||
|
||||
if (write(STDOUT_FILENO, buf, numRead) != numRead)
|
||||
fatal("partial/failed write (STDOUT_FILENO)");
|
||||
}
|
||||
}
|
||||
/* Notify receiver that we're done. */
|
||||
if (write (connFd, &junk, sizeof junk) != sizeof junk)
|
||||
errMsg ("write");
|
||||
if (close (connFd) < 0)
|
||||
errMsg ("close");
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/* Adapted from Kerrisk's script.c by Ken Brown */
|
||||
|
||||
/*
|
||||
Create a pty pair and fork/exec a shell running on the slave. Send
|
||||
the slave fd across an AF_UNIX socket to a process running
|
||||
recv_pty_slave.
|
||||
*/
|
||||
|
||||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 64-3 */
|
||||
|
||||
/* script.c
|
||||
|
||||
A simple version of script(1).
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <termios.h>
|
||||
#if ! defined(__hpux)
|
||||
/* HP-UX 11 doesn't have this header file */
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include "pty_fork.h" /* Declaration of ptyFork() */
|
||||
#include "tty_functions.h" /* Declaration of ttySetRaw() */
|
||||
#include "af_unix_hdr.h"
|
||||
#include "pty_slave.h"
|
||||
|
||||
#define BUF_SIZE 256
|
||||
#define MAX_SNAME 1000
|
||||
|
||||
struct termios ttyOrig;
|
||||
|
||||
static void /* Reset terminal mode on program exit */
|
||||
ttyReset(void)
|
||||
{
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &ttyOrig) == -1)
|
||||
errExit("tcsetattr");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char slaveName[MAX_SNAME];
|
||||
char *shell;
|
||||
int masterFd, slaveFd, connFd;
|
||||
struct winsize ws;
|
||||
fd_set inFds;
|
||||
char buf[BUF_SIZE];
|
||||
ssize_t numRead;
|
||||
pid_t childPid;
|
||||
|
||||
/* Retrieve the attributes of terminal on which we are started */
|
||||
|
||||
if (tcgetattr(STDIN_FILENO, &ttyOrig) == -1)
|
||||
errExit("tcgetattr");
|
||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
|
||||
errExit("ioctl-TIOCGWINSZ");
|
||||
|
||||
/* Create a child process, with parent and child connected via a
|
||||
pty pair. The child is connected to the pty slave and its terminal
|
||||
attributes are set to be the same as those retrieved above. */
|
||||
|
||||
childPid = ptyFork(&masterFd, slaveName, MAX_SNAME, &ttyOrig, &ws);
|
||||
if (childPid == -1)
|
||||
errExit("ptyFork");
|
||||
|
||||
if (childPid == 0) { /* Child: execute a shell on pty slave */
|
||||
|
||||
/* If the SHELL variable is set, use its value to determine
|
||||
the shell execed in child. Otherwise use /bin/sh. */
|
||||
|
||||
shell = getenv("SHELL");
|
||||
if (shell == NULL || *shell == '\0')
|
||||
shell = "/bin/sh";
|
||||
|
||||
execlp(shell, shell, (char *) NULL);
|
||||
errExit("execlp"); /* If we get here, something went wrong */
|
||||
}
|
||||
|
||||
/* Parent */
|
||||
|
||||
if ((connFd = unixConnect (SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
|
||||
/* Open slave and send its fd across the socket. */
|
||||
if ((slaveFd = open (slaveName, O_RDWR | O_NOCTTY)) < 0)
|
||||
errExit ("open");
|
||||
if (sendfd (connFd, slaveFd) < 0)
|
||||
errExit ("sendfd");
|
||||
if (close (slaveFd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (connFd) < 0)
|
||||
errMsg ("close");
|
||||
|
||||
/* Place terminal in raw mode so that we can pass all terminal
|
||||
input to the pseudoterminal master untouched */
|
||||
|
||||
ttySetRaw(STDIN_FILENO, &ttyOrig);
|
||||
|
||||
if (atexit(ttyReset) != 0)
|
||||
errExit("atexit");
|
||||
|
||||
/* Loop monitoring terminal and pty master for input. If the
|
||||
terminal is ready for input, then read some bytes and write
|
||||
them to the pty master. If the pty master is ready for input,
|
||||
then read some bytes and write them to the terminal. */
|
||||
|
||||
for (;;) {
|
||||
FD_ZERO(&inFds);
|
||||
FD_SET(STDIN_FILENO, &inFds);
|
||||
FD_SET(masterFd, &inFds);
|
||||
|
||||
if (select(masterFd + 1, &inFds, NULL, NULL, NULL) == -1)
|
||||
errExit("select");
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &inFds)) { /* stdin --> pty */
|
||||
numRead = read(STDIN_FILENO, buf, BUF_SIZE);
|
||||
if (numRead <= 0)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
if (write(masterFd, buf, numRead) != numRead)
|
||||
fatal("partial/failed write (masterFd)");
|
||||
}
|
||||
|
||||
if (FD_ISSET(masterFd, &inFds)) { /* pty --> stdout */
|
||||
numRead = read(masterFd, buf, BUF_SIZE);
|
||||
if (numRead <= 0)
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
if (write(STDOUT_FILENO, buf, numRead) != numRead)
|
||||
fatal("partial/failed write (STDOUT_FILENO)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Fork a subprocess, open a pty pair, and send the pty slave file
|
||||
descriptor to the subprocess over an AF_UNIX socket. Invoke with
|
||||
--debug to allow time to attach gdb to the child.
|
||||
*/
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
#include "pty_master_open.h"
|
||||
#include "read_line.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#define BUF_SIZE 100
|
||||
#define MAX_SNAME 100 /* Maximum size for pty slave name */
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Boolean debug = FALSE;
|
||||
pid_t pid;
|
||||
int pipefd[2];
|
||||
int pfd; /* parent's end */
|
||||
int cfd; /* child's end */
|
||||
|
||||
if (argc > 1 && strcmp (argv[1], "--debug") == 0)
|
||||
debug = TRUE;
|
||||
|
||||
if (socketpair (AF_UNIX, SOCK_STREAM, 0, pipefd) < 0)
|
||||
errExit ("socketpair");
|
||||
pfd = pipefd[1];
|
||||
cfd = pipefd[0];
|
||||
|
||||
if ((pid = fork ()) < 0)
|
||||
errExit ("fork");
|
||||
else if (pid > 0) /* parent */
|
||||
{
|
||||
int mfd, sfd, junk;
|
||||
char slname[MAX_SNAME];
|
||||
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
if ((mfd = ptyMasterOpen (slname, MAX_SNAME)) < 0)
|
||||
errExit ("ptyMasterOpen");
|
||||
if ((sfd = open (slname, O_RDWR | O_NOCTTY)) < 0)
|
||||
errExit ("open");
|
||||
if (debug)
|
||||
{
|
||||
printf ("parent pid %d, child pid %d, sleeping...\n", getpid (), pid);
|
||||
sleep (30);
|
||||
}
|
||||
|
||||
printf ("parent sending descriptor %d for %s to child\n", sfd, slname);
|
||||
if (sendfd (pfd, sfd) < 0)
|
||||
errExit ("sendfd");
|
||||
if (close (sfd) < 0)
|
||||
errMsg ("close");
|
||||
if (write (mfd, "hello\n", 6) < 0)
|
||||
errExit ("write");
|
||||
/* Wait for child. */
|
||||
if (read (pfd, &junk, sizeof junk) != sizeof junk)
|
||||
errMsg ("read");
|
||||
if (close (pfd) < 0)
|
||||
errMsg ("close");
|
||||
if (close (mfd) < 0)
|
||||
errMsg ("close");
|
||||
}
|
||||
else /* child */
|
||||
{
|
||||
int fd, junk;
|
||||
ssize_t nr;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if (close (pfd) < 0)
|
||||
errExit ("close");
|
||||
if (debug)
|
||||
sleep (30);
|
||||
|
||||
/* Read fd from parent. */
|
||||
fd = recvfd (cfd);
|
||||
if (fd < 0)
|
||||
errExit ("recvfd");
|
||||
|
||||
/* Read a line from fd. */
|
||||
if ((nr = readLine (fd, buf, BUF_SIZE)) < 0)
|
||||
{
|
||||
close (fd);
|
||||
errExit ("readLine");
|
||||
}
|
||||
|
||||
/* Kill newline. */
|
||||
buf[nr - 1] = '\0';
|
||||
printf ("child read %zd bytes (including newline) from fd %d: %s\n", nr,
|
||||
fd, buf);
|
||||
if (close (fd) == -1)
|
||||
errMsg ("close");
|
||||
/* Tell parent we're done. */
|
||||
if (write (cfd, &junk, sizeof junk) != sizeof junk)
|
||||
errMsg ("write");
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-5 */
|
||||
|
||||
/* ud_ucase.h
|
||||
|
||||
Header file for ud_ucase_sv.c and ud_ucase_cl.c.
|
||||
|
||||
These programs employ sockets in /tmp. This makes it easy to compile
|
||||
and run the programs. However, for a security reasons, a real-world
|
||||
application should never create sensitive files in /tmp. (As a simple of
|
||||
example of the kind of security problems that can result, a malicious
|
||||
user could create a file using the name defined in SV_SOCK_PATH, and
|
||||
thereby cause a denial of service attack against this application.
|
||||
See Section 38.7 of "The Linux Programming Interface" for more details
|
||||
on this subject.)
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define BUF_SIZE 10 /* Maximum size of messages exchanged
|
||||
between client and server */
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/ud_ucase"
|
|
@ -0,0 +1,71 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-7 */
|
||||
|
||||
/* ud_ucase_cl.c
|
||||
|
||||
A UNIX domain client that communicates with the server in ud_ucase_sv.c.
|
||||
This client sends each command-line argument as a datagram to the server,
|
||||
and then displays the contents of the server's response datagram.
|
||||
*/
|
||||
#include "ud_ucase.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un svaddr, claddr;
|
||||
int sfd, j;
|
||||
size_t msgLen;
|
||||
ssize_t numBytes;
|
||||
char resp[BUF_SIZE];
|
||||
|
||||
if (argc < 2 || strcmp(argv[1], "--help") == 0)
|
||||
usageErr("%s msg...\n", argv[0]);
|
||||
|
||||
/* Create client socket; bind to unique pathname (based on PID) */
|
||||
|
||||
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sfd == -1)
|
||||
errExit("socket");
|
||||
|
||||
memset(&claddr, 0, sizeof(struct sockaddr_un));
|
||||
claddr.sun_family = AF_UNIX;
|
||||
snprintf(claddr.sun_path, sizeof(claddr.sun_path),
|
||||
"/tmp/ud_ucase_cl.%ld", (long) getpid());
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &claddr, sizeof(struct sockaddr_un)) == -1)
|
||||
errExit("bind");
|
||||
|
||||
/* Construct address of server */
|
||||
|
||||
memset(&svaddr, 0, sizeof(struct sockaddr_un));
|
||||
svaddr.sun_family = AF_UNIX;
|
||||
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
|
||||
|
||||
/* Send messages to server; echo responses on stdout */
|
||||
|
||||
for (j = 1; j < argc; j++) {
|
||||
msgLen = strlen(argv[j]); /* May be longer than BUF_SIZE */
|
||||
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
|
||||
sizeof(struct sockaddr_un)) != msgLen)
|
||||
fatal("sendto");
|
||||
|
||||
numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
|
||||
/* Or equivalently: numBytes = recv(sfd, resp, BUF_SIZE, 0);
|
||||
or: numBytes = read(sfd, resp, BUF_SIZE); */
|
||||
if (numBytes == -1)
|
||||
errExit("recvfrom");
|
||||
printf("Response %d: %.*s\n", j, (int) numBytes, resp);
|
||||
}
|
||||
|
||||
remove(claddr.sun_path); /* Remove client socket pathname */
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-6 */
|
||||
|
||||
/* ud_ucase_sv.c
|
||||
|
||||
A server that uses a UNIX domain datagram socket to receive datagrams,
|
||||
convert their contents to uppercase, and then return them to the senders.
|
||||
|
||||
See also ud_ucase_cl.c.
|
||||
*/
|
||||
#include "ud_ucase.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un svaddr, claddr;
|
||||
int sfd, j;
|
||||
ssize_t numBytes;
|
||||
socklen_t len;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket */
|
||||
if (sfd == -1)
|
||||
errExit("socket");
|
||||
|
||||
/* Construct well-known address and bind server socket to it */
|
||||
|
||||
/* For an explanation of the following check, see the erratum note for
|
||||
page 1168 at http://www.man7.org/tlpi/errata/. */
|
||||
|
||||
if (strlen(SV_SOCK_PATH) > sizeof(svaddr.sun_path) - 1)
|
||||
fatal("Server socket path too long: %s", SV_SOCK_PATH);
|
||||
|
||||
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
|
||||
errExit("remove-%s", SV_SOCK_PATH);
|
||||
|
||||
memset(&svaddr, 0, sizeof(struct sockaddr_un));
|
||||
svaddr.sun_family = AF_UNIX;
|
||||
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
|
||||
errExit("bind");
|
||||
|
||||
/* Receive messages, convert to uppercase, and return to client */
|
||||
|
||||
for (;;) {
|
||||
len = sizeof(struct sockaddr_un);
|
||||
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
|
||||
(struct sockaddr *) &claddr, &len);
|
||||
if (numBytes == -1)
|
||||
errExit("recvfrom");
|
||||
|
||||
printf("Server received %ld bytes from %s\n", (long) numBytes,
|
||||
claddr.sun_path);
|
||||
|
||||
for (j = 0; j < numBytes; j++)
|
||||
buf[j] = toupper((unsigned char) buf[j]);
|
||||
|
||||
if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
|
||||
numBytes)
|
||||
fatal("sendto");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-8 */
|
||||
|
||||
/* us_abstract_bind.c
|
||||
|
||||
Demonstrate how to bind a UNIX domain socket to a name in the
|
||||
Linux-specific abstract namespace.
|
||||
|
||||
This program is Linux-specific.
|
||||
|
||||
The first printing of the book used slightly different code. The code was
|
||||
correct, but could have been better (to understand why, see the errata
|
||||
for page 1176 of the book). The old code is shown in comments below.
|
||||
*/
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_un addr;
|
||||
char *str;
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un)); /* Clear address structure */
|
||||
addr.sun_family = AF_UNIX; /* UNIX domain address */
|
||||
|
||||
/* addr.sun_path[0] has already been set to 0 by memset() */
|
||||
|
||||
str = "xyz"; /* Abstract name is "\0xyz" */
|
||||
strncpy(&addr.sun_path[1], str, strlen(str));
|
||||
|
||||
// In early printings of the book, the above two lines were instead:
|
||||
//
|
||||
// strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) - 2);
|
||||
// /* Abstract name is "xyz" followed by null bytes */
|
||||
|
||||
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sockfd == -1)
|
||||
errExit("socket");
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *) &addr,
|
||||
sizeof(sa_family_t) + strlen(str) + 1) == -1)
|
||||
errExit("bind");
|
||||
|
||||
// In early printings of the book, the final part of the bind() call
|
||||
// above was instead:
|
||||
// sizeof(struct sockaddr_un)) == -1)
|
||||
|
||||
sleep(60);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-2 */
|
||||
|
||||
/* us_xfr.h
|
||||
|
||||
Header file for us_xfr_sv.c and us_xfr_cl.c.
|
||||
|
||||
These programs employ a socket in /tmp. This makes it easy to compile
|
||||
and run the programs. However, for a security reasons, a real-world
|
||||
application should never create sensitive files in /tmp. (As a simple of
|
||||
example of the kind of security problems that can result, a malicious
|
||||
user could create a file using the name defined in SV_SOCK_PATH, and
|
||||
thereby cause a denial of service attack against this application.
|
||||
See Section 38.7 of "The Linux Programming Interface" for more details
|
||||
on this subject.)
|
||||
*/
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/us_xfr"
|
||||
|
||||
#define BUF_SIZE 100
|
|
@ -0,0 +1,55 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-4 */
|
||||
|
||||
/* us_xfr_cl.c
|
||||
|
||||
An example UNIX domain stream socket client. This client transmits contents
|
||||
of stdin to a server socket.
|
||||
|
||||
See also us_xfr_sv.c.
|
||||
*/
|
||||
|
||||
#include "us_xfr.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sfd;
|
||||
ssize_t numRead;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0); /* Create client socket */
|
||||
if (sfd == -1)
|
||||
errExit("socket");
|
||||
|
||||
/* Construct server address, and make the connection */
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(sfd, (struct sockaddr *) &addr,
|
||||
sizeof(struct sockaddr_un)) == -1)
|
||||
errExit("connect");
|
||||
|
||||
/* Copy stdin to socket */
|
||||
|
||||
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
|
||||
if (write(sfd, buf, numRead) != numRead)
|
||||
fatal("partial/failed write");
|
||||
|
||||
if (numRead == -1)
|
||||
errExit("read");
|
||||
|
||||
exit(EXIT_SUCCESS); /* Closes our socket; server sees EOF */
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Listing 57-3 */
|
||||
|
||||
/* us_xfr_sv.c
|
||||
|
||||
An example UNIX stream socket server. Accepts incoming connections
|
||||
and copies data sent from clients to stdout.
|
||||
|
||||
See also us_xfr_cl.c.
|
||||
*/
|
||||
#include "us_xfr.h"
|
||||
#define BACKLOG 5
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sfd, cfd;
|
||||
ssize_t numRead;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sfd == -1)
|
||||
errExit("socket");
|
||||
|
||||
/* Construct server socket address, bind socket to it,
|
||||
and make this a listening socket */
|
||||
|
||||
/* For an explanation of the following check, see the errata notes for
|
||||
pages 1168 and 1172 at http://www.man7.org/tlpi/errata/. */
|
||||
|
||||
if (strlen(SV_SOCK_PATH) > sizeof(addr.sun_path) - 1)
|
||||
fatal("Server socket path too long: %s", SV_SOCK_PATH);
|
||||
|
||||
if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
|
||||
errExit("remove-%s", SV_SOCK_PATH);
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SV_SOCK_PATH, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
|
||||
errExit("bind");
|
||||
|
||||
if (listen(sfd, BACKLOG) == -1)
|
||||
errExit("listen");
|
||||
|
||||
for (;;) { /* Handle client connections iteratively */
|
||||
|
||||
/* Accept a connection. The connection is returned on a new
|
||||
socket, 'cfd'; the listening socket ('sfd') remains open
|
||||
and can be used to accept further connections. */
|
||||
|
||||
cfd = accept(sfd, NULL, NULL);
|
||||
if (cfd == -1)
|
||||
errExit("accept");
|
||||
|
||||
/* Transfer data from connected socket to stdout until EOF */
|
||||
|
||||
while ((numRead = read(cfd, buf, BUF_SIZE)) > 0)
|
||||
if (write(STDOUT_FILENO, buf, numRead) != numRead)
|
||||
fatal("partial/failed write");
|
||||
|
||||
if (numRead == -1)
|
||||
errExit("read");
|
||||
|
||||
if (close(cfd) == -1)
|
||||
errMsg("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-3:c */
|
||||
|
||||
/* us_xfr_v2.h
|
||||
|
||||
Header file for us_xfr_sv.c and us_xfr_cl.c.
|
||||
*/
|
||||
#include "unix_sockets.h" /* Declares our socket functions */
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/us_xfr_v2"
|
||||
|
||||
#define BUF_SIZE 100
|
|
@ -0,0 +1,44 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-3:e */
|
||||
|
||||
/* us_xfr_v2_cl.c
|
||||
|
||||
An example UNIX domain stream socket client. This client transmits contents
|
||||
of stdin to a server socket. This program is similar to us_xfr_cl.c, except
|
||||
that it uses the functions in unix_sockets.c to simplify working with UNIX
|
||||
domain sockets.
|
||||
|
||||
See also us_xfr_v2_sv.c.
|
||||
*/
|
||||
#include "us_xfr_v2.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int sfd;
|
||||
ssize_t numRead;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
sfd = unixConnect(SV_SOCK_PATH, SOCK_STREAM);
|
||||
if (sfd == -1)
|
||||
errExit("unixConnect");
|
||||
|
||||
/* Copy stdin to socket */
|
||||
|
||||
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE)) > 0)
|
||||
if (write(sfd, buf, numRead) != numRead)
|
||||
fatal("partial/failed write");
|
||||
|
||||
if (numRead == -1)
|
||||
errExit("read");
|
||||
exit(EXIT_SUCCESS); /* Closes our socket; server sees EOF */
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*************************************************************************\
|
||||
* 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. *
|
||||
\*************************************************************************/
|
||||
|
||||
/* Solution for Exercise 59-3:d */
|
||||
|
||||
/* us_xfr_v2_sv.c
|
||||
|
||||
An example UNIX stream socket server. Accepts incoming connections and
|
||||
copies data sent from clients to stdout. This program is similar to
|
||||
us_xfr_sv.c, except that it uses the functions in unix_sockets.c to
|
||||
simplify working with UNIX domain sockets.
|
||||
|
||||
See also us_xfr_v2_cl.c.
|
||||
*/
|
||||
#include "us_xfr_v2.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int sfd, cfd;
|
||||
ssize_t numRead;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
|
||||
errExit ("remove");
|
||||
|
||||
sfd = unixBind(SV_SOCK_PATH, SOCK_STREAM);
|
||||
if (sfd == -1)
|
||||
errExit("unixBind");
|
||||
|
||||
if (listen(sfd, 5) == -1)
|
||||
errExit("listen");
|
||||
|
||||
for (;;) { /* Handle client connections iteratively */
|
||||
cfd = accept(sfd, NULL, NULL);
|
||||
if (cfd == -1)
|
||||
errExit("accept");
|
||||
|
||||
/* Transfer data from connected socket to stdout until EOF */
|
||||
|
||||
while ((numRead = read(cfd, buf, BUF_SIZE)) > 0)
|
||||
if (write(STDOUT_FILENO, buf, numRead) != numRead)
|
||||
fatal("partial/failed write");
|
||||
|
||||
if (numRead == -1)
|
||||
errExit("read");
|
||||
if (close(cfd) == -1)
|
||||
errMsg("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/* Header for waitall_sv.c and waitall_cl.c */
|
||||
|
||||
#include "af_unix_hdr.h"
|
||||
|
||||
#define SV_SOCK_PATH "/tmp/waitall"
|
||||
|
||||
#define BACKLOG 5
|
|
@ -0,0 +1,22 @@
|
|||
#include "waitall.h"
|
||||
|
||||
#define BUF_SIZE 100
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd;
|
||||
ssize_t nread;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
|
||||
/* Copy stdin to socket. */
|
||||
while ((nread = read (STDIN_FILENO, buf, BUF_SIZE)) > 0)
|
||||
if (write (sfd, buf, nread) != nread)
|
||||
errExit ("partial/failed write");
|
||||
|
||||
if (nread < 0)
|
||||
errExit ("read");
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#include "waitall.h"
|
||||
|
||||
#define BUF_SIZE 10
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int sfd, cfd;
|
||||
ssize_t nread;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
if (remove (SV_SOCK_PATH) < 0 && errno != ENOENT)
|
||||
errExit ("remove");
|
||||
|
||||
if ((sfd = unixBind (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixBind");
|
||||
|
||||
if (listen (sfd, BACKLOG) < 0)
|
||||
errExit ("listen");
|
||||
|
||||
while (1)
|
||||
{
|
||||
cfd = accept (sfd, NULL, NULL);
|
||||
if (cfd < 0)
|
||||
errExit ("accept");
|
||||
|
||||
/* Transfer data from connected socket to stdout until EOF. */
|
||||
while ((nread = recv (cfd, buf, BUF_SIZE, MSG_WAITALL)) > 0)
|
||||
if (write (STDOUT_FILENO, buf, nread) != nread)
|
||||
errExit ("partial/failed write");
|
||||
|
||||
if (nread < 0)
|
||||
errExit ("read");
|
||||
|
||||
if (close (cfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* Adapted from https://www.oreilly.com/library/view/linux-system-programming/0596009585/ch04.html */
|
||||
|
||||
#include "scatter_gather.h"
|
||||
|
||||
int main ( )
|
||||
{
|
||||
struct iovec iov[3];
|
||||
ssize_t nr;
|
||||
int sfd;
|
||||
|
||||
char *buf[] = {
|
||||
"The term buccaneer comes from the word boucan.\n",
|
||||
"A boucan is a wooden frame used for cooking meat.\n",
|
||||
"Buccaneer is the West Indies name for a pirate.\n"
|
||||
};
|
||||
|
||||
if ((sfd = unixConnect (SV_SOCK_PATH, SOCK_STREAM)) < 0)
|
||||
errExit ("unixConnect");
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
iov[i].iov_base = buf[i];
|
||||
iov[i].iov_len = strlen (buf[i]) + 1;
|
||||
}
|
||||
|
||||
nr = writev (sfd, iov, 3);
|
||||
if (nr < 0)
|
||||
errExit ("writev");
|
||||
printf ("wrote %zd bytes\n", nr);
|
||||
if (close (sfd) < 0)
|
||||
errExit ("close");
|
||||
}
|
Loading…
Reference in New Issue