Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Brown <kbrown@cornell.edu>2021-05-11 17:34:51 +0300
committerKen Brown <kbrown@cornell.edu>2021-06-04 18:19:28 +0300
commit315920326a0b8eb66a16ac576022b777097865a1 (patch)
tree62e13e359b921650d9bf24ce438f299d08910805
parentdfe5988f961ff97d283a9c460e75499db168163a (diff)
Cygwin: AF_UNIX: add socket tests
-rw-r--r--.gitignore4
-rw-r--r--winsup/cygwin/socket_tests/Makefile62
-rw-r--r--winsup/cygwin/socket_tests/Makefile.inc11
-rw-r--r--winsup/cygwin/socket_tests/README.txt279
-rw-r--r--winsup/cygwin/socket_tests/fork_socketpair.c72
-rw-r--r--winsup/cygwin/socket_tests/is_seqnum_v2.h27
-rw-r--r--winsup/cygwin/socket_tests/is_seqnum_v2_cl.c56
-rw-r--r--winsup/cygwin/socket_tests/is_seqnum_v2_sv.c94
-rw-r--r--winsup/cygwin/socket_tests/is_seqnum_v3_sv.c158
-rw-r--r--winsup/cygwin/socket_tests/lib/Build_ename.sh53
-rw-r--r--winsup/cygwin/socket_tests/lib/Makefile31
-rw-r--r--winsup/cygwin/socket_tests/lib/af_unix_hdr.h55
-rw-r--r--winsup/cygwin/socket_tests/lib/error_functions.c201
-rw-r--r--winsup/cygwin/socket_tests/lib/error_functions.h47
-rw-r--r--winsup/cygwin/socket_tests/lib/get_num.c103
-rw-r--r--winsup/cygwin/socket_tests/lib/get_num.h32
-rw-r--r--winsup/cygwin/socket_tests/lib/inet_sockets.c188
-rw-r--r--winsup/cygwin/socket_tests/lib/inet_sockets.h36
-rw-r--r--winsup/cygwin/socket_tests/lib/pty_fork.c117
-rw-r--r--winsup/cygwin/socket_tests/lib/pty_fork.h27
-rw-r--r--winsup/cygwin/socket_tests/lib/pty_master_open.c93
-rw-r--r--winsup/cygwin/socket_tests/lib/pty_master_open.h24
-rw-r--r--winsup/cygwin/socket_tests/lib/read_line.c73
-rw-r--r--winsup/cygwin/socket_tests/lib/read_line.h24
-rw-r--r--winsup/cygwin/socket_tests/lib/scm_functions.c146
-rw-r--r--winsup/cygwin/socket_tests/lib/scm_functions.h26
-rw-r--r--winsup/cygwin/socket_tests/lib/tty_functions.c89
-rw-r--r--winsup/cygwin/socket_tests/lib/tty_functions.h26
-rw-r--r--winsup/cygwin/socket_tests/lib/unix_sockets.c94
-rw-r--r--winsup/cygwin/socket_tests/lib/unix_sockets.h28
-rw-r--r--winsup/cygwin/socket_tests/msg_peek.h9
-rw-r--r--winsup/cygwin/socket_tests/msg_peek_cl.c20
-rw-r--r--winsup/cygwin/socket_tests/msg_peek_sv.c36
-rw-r--r--winsup/cygwin/socket_tests/pty_master.h5
-rw-r--r--winsup/cygwin/socket_tests/pty_slave.h5
-rw-r--r--winsup/cygwin/socket_tests/readv_socket.c41
-rw-r--r--winsup/cygwin/socket_tests/rec_pty_slave.c3
-rw-r--r--winsup/cygwin/socket_tests/recv_pty_master.c40
-rw-r--r--winsup/cygwin/socket_tests/recv_pty_slave.c37
-rw-r--r--winsup/cygwin/socket_tests/scatter_gather.h7
-rw-r--r--winsup/cygwin/socket_tests/scm_cred.h21
-rw-r--r--winsup/cygwin/socket_tests/scm_cred_recv.c173
-rw-r--r--winsup/cygwin/socket_tests/scm_cred_send.c171
-rw-r--r--winsup/cygwin/socket_tests/scm_multi.h26
-rw-r--r--winsup/cygwin/socket_tests/scm_multi_recv.c249
-rw-r--r--winsup/cygwin/socket_tests/scm_multi_send.c226
-rw-r--r--winsup/cygwin/socket_tests/scm_rights.h21
-rw-r--r--winsup/cygwin/socket_tests/scm_rights_recv.c169
-rw-r--r--winsup/cygwin/socket_tests/scm_rights_send.c138
-rw-r--r--winsup/cygwin/socket_tests/select_cl.c54
-rw-r--r--winsup/cygwin/socket_tests/select_sv.c59
-rw-r--r--winsup/cygwin/socket_tests/select_test.h11
-rw-r--r--winsup/cygwin/socket_tests/send_pty_master.c143
-rw-r--r--winsup/cygwin/socket_tests/send_pty_slave.c144
-rw-r--r--winsup/cygwin/socket_tests/send_pty_slave_fork.c102
-rw-r--r--winsup/cygwin/socket_tests/ud_ucase.h32
-rw-r--r--winsup/cygwin/socket_tests/ud_ucase_cl.c71
-rw-r--r--winsup/cygwin/socket_tests/ud_ucase_sv.c72
-rw-r--r--winsup/cygwin/socket_tests/us_abstract_bind.c61
-rw-r--r--winsup/cygwin/socket_tests/us_xfr.h30
-rw-r--r--winsup/cygwin/socket_tests/us_xfr_cl.c55
-rw-r--r--winsup/cygwin/socket_tests/us_xfr_sv.c79
-rw-r--r--winsup/cygwin/socket_tests/us_xfr_v2.h22
-rw-r--r--winsup/cygwin/socket_tests/us_xfr_v2_cl.c44
-rw-r--r--winsup/cygwin/socket_tests/us_xfr_v2_sv.c57
-rw-r--r--winsup/cygwin/socket_tests/waitall.h7
-rw-r--r--winsup/cygwin/socket_tests/waitall_cl.c22
-rw-r--r--winsup/cygwin/socket_tests/waitall_sv.c38
-rw-r--r--winsup/cygwin/socket_tests/writev_socket.c32
69 files changed, 4808 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 9bff2ff60..28cd86649 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
.#*
*#
+*.exe
*.flt
*.gmo
*.info
@@ -47,3 +48,6 @@ core
!core/
lost+found
+
+ename.c.inc
+libafunix.a
diff --git a/winsup/cygwin/socket_tests/Makefile b/winsup/cygwin/socket_tests/Makefile
new file mode 100644
index 000000000..18fe2a96b
--- /dev/null
+++ b/winsup/cygwin/socket_tests/Makefile
@@ -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}
diff --git a/winsup/cygwin/socket_tests/Makefile.inc b/winsup/cygwin/socket_tests/Makefile.inc
new file mode 100644
index 000000000..cd5db5328
--- /dev/null
+++ b/winsup/cygwin/socket_tests/Makefile.inc
@@ -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
diff --git a/winsup/cygwin/socket_tests/README.txt b/winsup/cygwin/socket_tests/README.txt
new file mode 100644
index 000000000..274473173
--- /dev/null
+++ b/winsup/cygwin/socket_tests/README.txt
@@ -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.
diff --git a/winsup/cygwin/socket_tests/fork_socketpair.c b/winsup/cygwin/socket_tests/fork_socketpair.c
new file mode 100644
index 000000000..5d9fd14c0
--- /dev/null
+++ b/winsup/cygwin/socket_tests/fork_socketpair.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/is_seqnum_v2.h b/winsup/cygwin/socket_tests/is_seqnum_v2.h
new file mode 100644
index 000000000..495553aee
--- /dev/null
+++ b/winsup/cygwin/socket_tests/is_seqnum_v2.h
@@ -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') */
diff --git a/winsup/cygwin/socket_tests/is_seqnum_v2_cl.c b/winsup/cygwin/socket_tests/is_seqnum_v2_cl.c
new file mode 100644
index 000000000..a6df5d0a3
--- /dev/null
+++ b/winsup/cygwin/socket_tests/is_seqnum_v2_cl.c
@@ -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' */
+}
diff --git a/winsup/cygwin/socket_tests/is_seqnum_v2_sv.c b/winsup/cygwin/socket_tests/is_seqnum_v2_sv.c
new file mode 100644
index 000000000..3d1c37b5c
--- /dev/null
+++ b/winsup/cygwin/socket_tests/is_seqnum_v2_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/is_seqnum_v3_sv.c b/winsup/cygwin/socket_tests/is_seqnum_v3_sv.c
new file mode 100644
index 000000000..f52a90d44
--- /dev/null
+++ b/winsup/cygwin/socket_tests/is_seqnum_v3_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/lib/Build_ename.sh b/winsup/cygwin/socket_tests/lib/Build_ename.sh
new file mode 100644
index 000000000..d2eb014c8
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/Build_ename.sh
@@ -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;
+}
+'
+
diff --git a/winsup/cygwin/socket_tests/lib/Makefile b/winsup/cygwin/socket_tests/lib/Makefile
new file mode 100644
index 000000000..a94e4b53b
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/Makefile
@@ -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}
diff --git a/winsup/cygwin/socket_tests/lib/af_unix_hdr.h b/winsup/cygwin/socket_tests/lib/af_unix_hdr.h
new file mode 100644
index 000000000..bfbee9c53
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/af_unix_hdr.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/error_functions.c b/winsup/cygwin/socket_tests/lib/error_functions.c
new file mode 100644
index 000000000..62e4961da
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/error_functions.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/lib/error_functions.h b/winsup/cygwin/socket_tests/lib/error_functions.h
new file mode 100644
index 000000000..8954fa95e
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/error_functions.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/get_num.c b/winsup/cygwin/socket_tests/lib/get_num.c
new file mode 100644
index 000000000..e8e1ad225
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/get_num.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/get_num.h b/winsup/cygwin/socket_tests/lib/get_num.h
new file mode 100644
index 000000000..7b5a0b1e4
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/get_num.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/inet_sockets.c b/winsup/cygwin/socket_tests/lib/inet_sockets.c
new file mode 100644
index 000000000..396dbf906
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/inet_sockets.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/inet_sockets.h b/winsup/cygwin/socket_tests/lib/inet_sockets.h
new file mode 100644
index 000000000..1aa263050
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/inet_sockets.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/pty_fork.c b/winsup/cygwin/socket_tests/lib/pty_fork.c
new file mode 100644
index 000000000..4e78c9e9b
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/pty_fork.c
@@ -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() */
+}
diff --git a/winsup/cygwin/socket_tests/lib/pty_fork.h b/winsup/cygwin/socket_tests/lib/pty_fork.h
new file mode 100644
index 000000000..99e0c0683
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/pty_fork.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/pty_master_open.c b/winsup/cygwin/socket_tests/lib/pty_master_open.c
new file mode 100644
index 000000000..b0de3a1fb
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/pty_master_open.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/pty_master_open.h b/winsup/cygwin/socket_tests/lib/pty_master_open.h
new file mode 100644
index 000000000..5f5c7b991
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/pty_master_open.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/read_line.c b/winsup/cygwin/socket_tests/lib/read_line.c
new file mode 100644
index 000000000..f6c5f96c1
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/read_line.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/read_line.h b/winsup/cygwin/socket_tests/lib/read_line.h
new file mode 100644
index 000000000..1da5b874e
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/read_line.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/scm_functions.c b/winsup/cygwin/socket_tests/lib/scm_functions.c
new file mode 100644
index 000000000..8dbbca108
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/scm_functions.c
@@ -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));
+}
diff --git a/winsup/cygwin/socket_tests/lib/scm_functions.h b/winsup/cygwin/socket_tests/lib/scm_functions.h
new file mode 100644
index 000000000..a94129186
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/scm_functions.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/tty_functions.c b/winsup/cygwin/socket_tests/lib/tty_functions.c
new file mode 100644
index 000000000..61b024e0b
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/tty_functions.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/tty_functions.h b/winsup/cygwin/socket_tests/lib/tty_functions.h
new file mode 100644
index 000000000..4ed7aaca8
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/tty_functions.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/lib/unix_sockets.c b/winsup/cygwin/socket_tests/lib/unix_sockets.c
new file mode 100644
index 000000000..cc93b3ac6
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/unix_sockets.c
@@ -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;
+}
diff --git a/winsup/cygwin/socket_tests/lib/unix_sockets.h b/winsup/cygwin/socket_tests/lib/unix_sockets.h
new file mode 100644
index 000000000..5ad394317
--- /dev/null
+++ b/winsup/cygwin/socket_tests/lib/unix_sockets.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/msg_peek.h b/winsup/cygwin/socket_tests/msg_peek.h
new file mode 100644
index 000000000..ae2b7c962
--- /dev/null
+++ b/winsup/cygwin/socket_tests/msg_peek.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/msg_peek_cl.c b/winsup/cygwin/socket_tests/msg_peek_cl.c
new file mode 100644
index 000000000..db3c52c5f
--- /dev/null
+++ b/winsup/cygwin/socket_tests/msg_peek_cl.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/msg_peek_sv.c b/winsup/cygwin/socket_tests/msg_peek_sv.c
new file mode 100644
index 000000000..3303f05a6
--- /dev/null
+++ b/winsup/cygwin/socket_tests/msg_peek_sv.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/pty_master.h b/winsup/cygwin/socket_tests/pty_master.h
new file mode 100644
index 000000000..4a30752a0
--- /dev/null
+++ b/winsup/cygwin/socket_tests/pty_master.h
@@ -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"
diff --git a/winsup/cygwin/socket_tests/pty_slave.h b/winsup/cygwin/socket_tests/pty_slave.h
new file mode 100644
index 000000000..232440eba
--- /dev/null
+++ b/winsup/cygwin/socket_tests/pty_slave.h
@@ -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"
diff --git a/winsup/cygwin/socket_tests/readv_socket.c b/winsup/cygwin/socket_tests/readv_socket.c
new file mode 100644
index 000000000..dceec2f62
--- /dev/null
+++ b/winsup/cygwin/socket_tests/readv_socket.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/rec_pty_slave.c b/winsup/cygwin/socket_tests/rec_pty_slave.c
new file mode 100644
index 000000000..b5197a80e
--- /dev/null
+++ b/winsup/cygwin/socket_tests/rec_pty_slave.c
@@ -0,0 +1,3 @@
+#include "af_unix_hdr.h"
+#include "pty_slave.h"
+
diff --git a/winsup/cygwin/socket_tests/recv_pty_master.c b/winsup/cygwin/socket_tests/recv_pty_master.c
new file mode 100644
index 000000000..81e16b4f2
--- /dev/null
+++ b/winsup/cygwin/socket_tests/recv_pty_master.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/recv_pty_slave.c b/winsup/cygwin/socket_tests/recv_pty_slave.c
new file mode 100644
index 000000000..6095d302a
--- /dev/null
+++ b/winsup/cygwin/socket_tests/recv_pty_slave.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/scatter_gather.h b/winsup/cygwin/socket_tests/scatter_gather.h
new file mode 100644
index 000000000..99502222c
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scatter_gather.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/scm_cred.h b/winsup/cygwin/socket_tests/scm_cred.h
new file mode 100644
index 000000000..26858a678
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_cred.h
@@ -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"
diff --git a/winsup/cygwin/socket_tests/scm_cred_recv.c b/winsup/cygwin/socket_tests/scm_cred_recv.c
new file mode 100644
index 000000000..121b472a4
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_cred_recv.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/scm_cred_send.c b/winsup/cygwin/socket_tests/scm_cred_send.c
new file mode 100644
index 000000000..d023ace67
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_cred_send.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/scm_multi.h b/winsup/cygwin/socket_tests/scm_multi.h
new file mode 100644
index 000000000..af3d503b3
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_multi.h
@@ -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 */
diff --git a/winsup/cygwin/socket_tests/scm_multi_recv.c b/winsup/cygwin/socket_tests/scm_multi_recv.c
new file mode 100644
index 000000000..e861c0976
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_multi_recv.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/scm_multi_send.c b/winsup/cygwin/socket_tests/scm_multi_send.c
new file mode 100644
index 000000000..21151a86f
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_multi_send.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/scm_rights.h b/winsup/cygwin/socket_tests/scm_rights.h
new file mode 100644
index 000000000..fb779537a
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_rights.h
@@ -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"
diff --git a/winsup/cygwin/socket_tests/scm_rights_recv.c b/winsup/cygwin/socket_tests/scm_rights_recv.c
new file mode 100644
index 000000000..4a23a2d99
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_rights_recv.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/scm_rights_send.c b/winsup/cygwin/socket_tests/scm_rights_send.c
new file mode 100644
index 000000000..a0ca76672
--- /dev/null
+++ b/winsup/cygwin/socket_tests/scm_rights_send.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/select_cl.c b/winsup/cygwin/socket_tests/select_cl.c
new file mode 100644
index 000000000..44aebd45c
--- /dev/null
+++ b/winsup/cygwin/socket_tests/select_cl.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/select_sv.c b/winsup/cygwin/socket_tests/select_sv.c
new file mode 100644
index 000000000..e9cf6dc8c
--- /dev/null
+++ b/winsup/cygwin/socket_tests/select_sv.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/select_test.h b/winsup/cygwin/socket_tests/select_test.h
new file mode 100644
index 000000000..67a85ca37
--- /dev/null
+++ b/winsup/cygwin/socket_tests/select_test.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/send_pty_master.c b/winsup/cygwin/socket_tests/send_pty_master.c
new file mode 100644
index 000000000..b1595e67f
--- /dev/null
+++ b/winsup/cygwin/socket_tests/send_pty_master.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/send_pty_slave.c b/winsup/cygwin/socket_tests/send_pty_slave.c
new file mode 100644
index 000000000..606b8a529
--- /dev/null
+++ b/winsup/cygwin/socket_tests/send_pty_slave.c
@@ -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)");
+ }
+ }
+}
diff --git a/winsup/cygwin/socket_tests/send_pty_slave_fork.c b/winsup/cygwin/socket_tests/send_pty_slave_fork.c
new file mode 100644
index 000000000..a88715035
--- /dev/null
+++ b/winsup/cygwin/socket_tests/send_pty_slave_fork.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/ud_ucase.h b/winsup/cygwin/socket_tests/ud_ucase.h
new file mode 100644
index 000000000..55cac451a
--- /dev/null
+++ b/winsup/cygwin/socket_tests/ud_ucase.h
@@ -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"
diff --git a/winsup/cygwin/socket_tests/ud_ucase_cl.c b/winsup/cygwin/socket_tests/ud_ucase_cl.c
new file mode 100644
index 000000000..7d60f4077
--- /dev/null
+++ b/winsup/cygwin/socket_tests/ud_ucase_cl.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/ud_ucase_sv.c b/winsup/cygwin/socket_tests/ud_ucase_sv.c
new file mode 100644
index 000000000..7c4987b35
--- /dev/null
+++ b/winsup/cygwin/socket_tests/ud_ucase_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/us_abstract_bind.c b/winsup/cygwin/socket_tests/us_abstract_bind.c
new file mode 100644
index 000000000..897072ec3
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_abstract_bind.c
@@ -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);
+}
diff --git a/winsup/cygwin/socket_tests/us_xfr.h b/winsup/cygwin/socket_tests/us_xfr.h
new file mode 100644
index 000000000..5ac855a6e
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/us_xfr_cl.c b/winsup/cygwin/socket_tests/us_xfr_cl.c
new file mode 100644
index 000000000..249765952
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr_cl.c
@@ -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 */
+}
diff --git a/winsup/cygwin/socket_tests/us_xfr_sv.c b/winsup/cygwin/socket_tests/us_xfr_sv.c
new file mode 100644
index 000000000..a2193dad7
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/us_xfr_v2.h b/winsup/cygwin/socket_tests/us_xfr_v2.h
new file mode 100644
index 000000000..aef7e2435
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr_v2.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/us_xfr_v2_cl.c b/winsup/cygwin/socket_tests/us_xfr_v2_cl.c
new file mode 100644
index 000000000..da2b27a0d
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr_v2_cl.c
@@ -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 */
+}
diff --git a/winsup/cygwin/socket_tests/us_xfr_v2_sv.c b/winsup/cygwin/socket_tests/us_xfr_v2_sv.c
new file mode 100644
index 000000000..517129a4b
--- /dev/null
+++ b/winsup/cygwin/socket_tests/us_xfr_v2_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/waitall.h b/winsup/cygwin/socket_tests/waitall.h
new file mode 100644
index 000000000..73aad8490
--- /dev/null
+++ b/winsup/cygwin/socket_tests/waitall.h
@@ -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
diff --git a/winsup/cygwin/socket_tests/waitall_cl.c b/winsup/cygwin/socket_tests/waitall_cl.c
new file mode 100644
index 000000000..49aba3e87
--- /dev/null
+++ b/winsup/cygwin/socket_tests/waitall_cl.c
@@ -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");
+}
diff --git a/winsup/cygwin/socket_tests/waitall_sv.c b/winsup/cygwin/socket_tests/waitall_sv.c
new file mode 100644
index 000000000..175ca4337
--- /dev/null
+++ b/winsup/cygwin/socket_tests/waitall_sv.c
@@ -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");
+ }
+}
diff --git a/winsup/cygwin/socket_tests/writev_socket.c b/winsup/cygwin/socket_tests/writev_socket.c
new file mode 100644
index 000000000..793857222
--- /dev/null
+++ b/winsup/cygwin/socket_tests/writev_socket.c
@@ -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");
+}