]> Zhao Yanbai Git Server - minix.git/commitdiff
Add test56 to test our UDS implementation. Contributed by Thomas Cort
authorThomas Veerman <thomas@minix3.org>
Thu, 15 Jul 2010 14:52:29 +0000 (14:52 +0000)
committerThomas Veerman <thomas@minix3.org>
Thu, 15 Jul 2010 14:52:29 +0000 (14:52 +0000)
test/Makefile
test/test56.c [new file with mode: 0644]

index 2b725b63be4dd91a56844f1640f16e666648a23f..83c9b2b4027b0d42d6488f37f2d8cfa13e5f48a0 100644 (file)
@@ -12,7 +12,7 @@ OBJ=  test1  test2  test3  test4  test5  test6  test7  test8  test9  \
        test30 test31 test32        test34 test35 test36 test37 test38 \
        test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
        test42 test45 test47 test48 test49 test50 test51 test52 test53 \
-       test54 test55
+       test54 test55 test56
 
 BIGOBJ=  test20 test24
 ROOTOBJ= test11 test33 test43 test44 test46
@@ -111,3 +111,4 @@ test52: test52.c
 test52-gcc: test52.c
 test54: test54.c
 test55: test55.c
+test56: test56.c
diff --git a/test/test56.c b/test/test56.c
new file mode 100644 (file)
index 0000000..f60bea7
--- /dev/null
@@ -0,0 +1,1939 @@
+/*
+ * Test Program for Unix Domain Sockets
+ *
+ * Overview: This program tests Unix Domain Sockets. It attempts
+ * to exercise the functions associated with Unix Domain Sockets.
+ * It also attempts to make sure all of the functions which handle
+ * file/socket descriptors work correctly when given a socket
+ * descriptor for a Unix domain socket. It also implicitly checks
+ * for the existance of constants like AF_UNIX and structures like
+ * sockaddr_un (it won't compile if they aren't defined). Besides
+ * checking that the sockets work properly, this test program also
+ * checks that the errors returned conform to the POSIX 2008
+ * standards. Some tests are omitted as they could adversely affect
+ * the operation of the host system. For example, implementing a test
+ * for socket() failing with errno = ENFILE would require using up all
+ * of the file descriptors supported by the OS (defined in
+ * /proc/sys/fs/file-max on Linux); this could cause problems for
+ * daemons and other processes running on the system. Some tests are
+ * omitted because they would require changes to libc or the kernel.
+ * For example, getting EINTR would require  delaying the system call
+ * execution time long enough to raise a signal to interupt it. Some
+ * tests were omitted because the particular errors cannot occur when
+ * using Unix domain sockets. For example, write() will never fail with
+ * ENETDOWN because Unix domain sockets don't use network interfaces.
+ *
+ * Structure: Some functions can be tested or partially tested without
+ * making a connection, socket() for example. These have test
+ * functions like test_NAME(). The functionality that needs two way
+ * communication is contained within test_xfer().
+ *
+ * Functions Tested: accept(), bind(), close(), connect(), dup(),
+ * dup2(), fstat(), getpeername(), getsockname(), getsockopt(),
+ * listen(), read(), readv(), recv(), recvfrom(), recvmsg(), select(), 
+ * send(), sendmsg(), sendto(), setsockopt(), shutdown(), socket(), 
+ * socketpair(), write(), writev()
+ */
+
+#define DEBUG 0
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ansi.h"
+
+/* Maximum number of errors that we'll allow to occur before this test 
+ * program gives us and quits.
+ */
+#define MAX_ERROR 4
+
+/* Use the common testing code instead of reinventing the wheel. */
+#include "common.c"
+
+/* path of the unix domain socket */
+#define TEST_SUN_PATH "test.sock"
+#define TEST_SUN_PATHB "testb.sock"
+
+/* filenames for symlinks -- we link these to each other to test ELOOP .*/
+#define TEST_SYM_A "test.a"
+#define TEST_SYM_B "test.b"
+
+/* buffer for send/recv */
+#define BUFSIZE (128)
+
+#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S"
+
+/* socket types supported */
+int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM};
+
+/* timestamps for debug and error logs */
+char *get_timestamp(void)
+{
+       struct tm *tm;
+       time_t t;
+       size_t len;
+       char *s;
+
+       len = sizeof(char) * 32;
+
+       t = time(NULL);
+       if (t == -1) {
+               return NULL;
+       }
+       tm = gmtime(&t);
+       if (tm == NULL) {
+               return NULL;
+       }
+
+       s = (char *) malloc(len);
+       if (!s) {
+               perror("malloc");
+               return NULL;
+       }
+       memset(s, '\0', len);
+
+       strftime(s, len - 1, ISO8601_FORMAT, tm);
+       return s;
+}
+
+/* macro to display information about a failed test and increment the errct */
+#define test_fail(msg)                                                 \
+       do {                                                            \
+               char *timestamp;                                        \
+               timestamp = get_timestamp();                            \
+               if (errct == 0) fprintf(stderr, "\n");                  \
+fprintf(stderr, "[ERROR][%s] (%s Line %d) %s [pid=%d:errno=%d:%s]\n",   \
+                       timestamp, __FILE__, __LINE__, msg, getpid(),   \
+                                       errno, strerror(errno));        \
+               fflush(stderr);                                         \
+               if (timestamp != NULL) {                                \
+                       free(timestamp);                                \
+                       timestamp = NULL;                               \
+               }                                                       \
+               errct++;                                                \
+               if (errct++ > MAX_ERROR) {                              \
+                       printf("Too many errors; test aborted\n");      \
+                       cleanup();                                      \
+                       exit(1);                                        \
+               }                                                       \
+       } while (0)
+
+#if DEBUG == 1
+/* macros to display debugging information */
+#define debug(msg)                                                     \
+       do {                                                            \
+               char *timestamp;                                        \
+               timestamp = get_timestamp();                            \
+               fprintf(stdout,"[DEBUG][%s] (%s:%d) %s [pid=%d]\n",     \
+                       timestamp, __FILE__, __LINE__, msg, getpid());  \
+               fflush(stdout);                                         \
+               if (timestamp != NULL) {                                \
+                       free(timestamp);                                \
+                       timestamp = NULL;                               \
+               }                                                       \
+       } while (0)
+#else
+#define debug(msg)     \
+       do { /* nothing */; } while (0)
+#endif
+
+#define SOCKET(sd,domain,type,protocol)                                        \
+       do {                                                            \
+               errno = 0;                                              \
+               sd = socket(domain, type, protocol);                    \
+               if (sd == -1) {                                         \
+               test_fail("sd = socket(domain, type, protocol) failed");\
+               }                                                       \
+       } while (0)
+
+#define UNLINK(path)                                           \
+       do {                                                    \
+               int rc;                                         \
+               errno = 0;                                      \
+               rc = unlink(path);                              \
+               if (rc == -1 && errno != ENOENT) {              \
+                       test_fail("unlink(path) failed");       \
+               }                                               \
+       } while(0)
+
+#define SYMLINK(oldpath,newpath)                                       \
+       do {                                                            \
+               int rc;                                                 \
+               errno = 0;                                              \
+               rc = symlink(oldpath,newpath);                          \
+               if (rc == -1) {                                         \
+                       test_fail("symlink(oldpath,newpath) failed");   \
+               }                                                       \
+       } while(0)
+
+#define CLOSE(sd)                                      \
+       do {                                            \
+               int rc;                                 \
+               errno = 0;                              \
+               rc = close(sd);                         \
+               if (rc == -1) {                         \
+                       test_fail("close(sd) failed");  \
+               }                                       \
+       } while (0)
+
+void test_socket(void)
+{
+       struct stat statbuf, statbuf2;
+       int sd, sd2;
+       int rc;
+       int i;
+
+       debug("entering test_socket()");
+
+       debug("Test socket() with an unsupported address family");
+
+       errno = 0;
+       sd = socket(-1, SOCK_STREAM, 0);
+       if (!(sd == -1 && errno == EAFNOSUPPORT)) {
+               test_fail("socket");
+               if (sd != -1) {
+                       CLOSE(sd);
+               }
+       }
+
+       debug("Test socket() with all available FDs open by this process");
+
+       for (i = 3; i < getdtablesize(); i++) {
+               rc = open("/dev/null", O_RDONLY);
+               if (rc == -1) {
+                       test_fail("we couldn't open /dev/null for read");
+               }
+       }
+
+       errno = 0;
+       sd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (!(sd == -1 && errno == EMFILE)) {
+               test_fail("socket() call with all fds open should fail");
+               if (sd != -1) {
+                       CLOSE(sd);
+               }
+       }
+
+       for (i = 3; i < getdtablesize(); i++) {
+               CLOSE(i);
+       }
+
+       debug("Test socket() with an mismatched protocol");
+
+       errno = 0;
+       sd = socket(PF_UNIX, SOCK_STREAM, 4);
+       if (!(sd == -1 && errno == EPROTONOSUPPORT)) {
+               test_fail("socket() should fail with errno = EPROTONOSUPPORT");
+               if (sd != -1) {
+                       CLOSE(sd);
+               }
+       }
+
+       debug("Test socket() success");
+
+       /*
+        * open 2 sockets at once and *then* close them.
+        * This will test that /dev/uds is cloning properly.
+        */
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
+
+       rc = fstat(sd, &statbuf);
+       if (rc == -1) {
+               test_fail("fstat failed on sd");
+       }
+
+       rc = fstat(sd2, &statbuf2);
+       if (rc == -1) {
+               test_fail("fstat failed on sd2");
+       }
+
+
+       if (statbuf.st_dev == statbuf2.st_dev) {
+               test_fail("/dev/uds isn't being cloned");
+       }
+
+       CLOSE(sd2);
+       CLOSE(sd);
+
+       debug("leaving test_socket()");
+}
+
+void test_header(void)
+{
+       struct sockaddr_un sun;
+       debug("entering test_header()");
+
+       sun.sun_family = AF_UNIX;
+       sun.sun_path[0] = 'x';
+       sun.sun_path[1] = 'x';
+       sun.sun_path[2] = 'x';
+       sun.sun_path[3] = '\0';
+
+       if (SUN_LEN(&sun) != 4) {
+               test_fail("SUN_LEN(&sun) should be 4");
+       }
+
+       if (PF_UNIX != PF_LOCAL || PF_UNIX != PF_FILE || PF_UNIX != AF_UNIX) {
+               test_fail("PF_UNIX, PF_LOCAL, PF_FILE, and AF_UNIX");
+       }
+}
+
+void test_socketpair(void)
+{
+       char buf[128];
+       struct sockaddr_un addr;
+       int socket_vector[2];
+       int rc;
+       int i;
+
+       debug("entering test_socketpair()");
+
+       UNLINK(TEST_SUN_PATH);
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+
+       debug("Testing socketpair() success");
+
+       rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector);
+       if (rc == -1) {
+               test_fail("socketpair() should have worked");
+       }
+
+       debug("Testing a simple read/write using sockets from socketpair()");
+       memset(buf, '\0', sizeof(buf));
+
+       strncpy(buf, "Howdy Partner", sizeof(buf) - 1);
+
+       rc = write(socket_vector[0], buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("write(sd, buf, sizeof(buf)) failed unexpectedly");
+       }
+
+       memset(buf, '\0', sizeof(buf));
+
+       rc = read(socket_vector[1], buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("read() failed unexpectedly");
+       }
+
+       if (strncmp(buf, "Howdy Partner", strlen("Howdy Partner")) != 0) {
+               test_fail("We did not read what we wrote");
+       }
+
+       CLOSE(socket_vector[0]);
+       CLOSE(socket_vector[1]);
+
+       debug("Test socketpair() with all FDs open by this process");
+
+       for (i = 3; i < getdtablesize(); i++) {
+               rc = open("/dev/null", O_RDONLY);
+               if (rc == -1) {
+                       test_fail("we couldn't open /dev/null for read");
+               }
+       }
+
+       rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector);
+       if (!(rc == -1 && errno == EMFILE)) {
+               test_fail("socketpair() should have failed with EMFILE");
+       }
+
+       for (i = 3; i < getdtablesize(); i++) {
+               CLOSE(i);
+       }
+
+       rc = socketpair(PF_UNIX, SOCK_STREAM, 4, socket_vector);
+       if (!(rc == -1 && errno == EPROTONOSUPPORT)) {
+               test_fail("socketpair() should have failed");
+       }
+
+       debug("leaving test_socketpair()");
+}
+
+void test_ucred(void)
+{
+       struct ucred credentials;
+       socklen_t ucred_length;
+       uid_t euid = geteuid();
+       gid_t egid = getegid();
+       int sv[2];
+       int rc;
+
+       debug("Test credentials passing");
+
+       ucred_length = sizeof(struct ucred);
+
+       rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
+       if (rc == -1) {
+               test_fail("socketpair(PF_UNIX, SOCK_STREAM, 0, sv) failed");
+       }
+
+       memset(&credentials, '\0', ucred_length);
+       rc = getsockopt(sv[0], SOL_SOCKET, SO_PEERCRED, &credentials, 
+                                                       &ucred_length);
+       if (rc == -1) {
+               test_fail("getsockopt(SO_PEERCRED) failed");
+       } else if (credentials.pid != getpid() ||
+                       credentials.uid != geteuid() ||
+                       credentials.gid != getegid()) {
+               /* printf("%d=%d %d=%d %d=%d",credentials.pid, getpid(), 
+                credentials.uid, geteuid(), credentials.gid, getegid()); */
+               test_fail("Credential passing gave us the wrong cred");
+       }
+
+       rc = getpeereid(sv[0], &euid, &egid);
+       if (rc == -1) {
+               test_fail("getpeereid(sv[0], &euid, &egid) failed");
+       } else if (credentials.uid != euid || credentials.gid != egid) {
+               test_fail("getpeereid() didn't give the correct euid/egid");
+       }
+
+       CLOSE(sv[0]);
+       CLOSE(sv[1]);
+}
+
+void test_getsockname(void)
+{
+       int sd;
+       int rc;
+       struct sockaddr_un addr, sock_addr;
+       socklen_t sock_addr_len;
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+
+       debug("Test bind() success");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc == -1) {
+               test_fail("bind() should have worked");
+       }
+
+       debug("Test getsockname() success");
+
+       memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
+       sock_addr_len = sizeof(struct sockaddr_un);
+
+       rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
+       if (rc == -1) {
+               test_fail("getsockname() should have worked");
+       }
+
+       if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path,
+               addr.sun_path, sizeof(sock_addr.sun_path) - 1) == 0)) {
+               test_fail("getsockname() did return the right address");
+       }
+
+       CLOSE(sd);
+}
+
+void test_bind(void)
+{
+       struct sockaddr_un addr;
+       struct sockaddr_un sock_addr;
+       socklen_t sock_addr_len;
+       int sd;
+       int sd2;
+       int rc;
+       int on;
+
+       debug("entering test_bind()");
+       on = 1;
+       UNLINK(TEST_SUN_PATH);
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+
+       debug("Test bind() success");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc == -1) {
+               test_fail("bind() should have worked");
+       }
+
+       debug("Test getsockname() success");
+
+       memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
+       sock_addr_len = sizeof(struct sockaddr_un);
+
+       rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
+       if (rc == -1) {
+               test_fail("getsockname() should have worked");
+       }
+
+       if (!(sock_addr.sun_family == AF_UNIX &&
+                       strncmp(sock_addr.sun_path, addr.sun_path, 
+                       sizeof(sock_addr.sun_path) - 1) == 0)) {
+
+               test_fail("getsockname() didn't return the right addr");
+       }
+
+       debug("Test bind() with a address that has already been bind()'d");
+
+       SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
+       errno = 0;
+       rc = bind(sd2, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (!((rc == -1) && (errno == EADDRINUSE))) {
+               test_fail("bind() should have failed with EADDRINUSE");
+       }
+       CLOSE(sd2);
+       CLOSE(sd);
+       UNLINK(TEST_SUN_PATH);
+
+       debug("Test bind() with an empty sun_path");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       memset(addr.sun_path, '\0', sizeof(addr.sun_path));
+       errno = 0;
+
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (!(rc == -1 && errno == ENOENT)) {
+               test_fail("bind() should have failed with ENOENT");
+       }
+       CLOSE(sd);
+
+       debug("Test bind() with a NULL address");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       errno = 0;
+       rc = bind(sd, (struct sockaddr *) NULL, sizeof(struct sockaddr_un));
+       if (!((rc == -1) && (errno == EFAULT))) {
+               test_fail("bind() should have failed with EFAULT");
+       }
+       CLOSE(sd);
+
+       debug("Test bind() using a symlink loop");
+
+       UNLINK(TEST_SUN_PATH);
+       UNLINK(TEST_SYM_A);
+       UNLINK(TEST_SYM_B);
+
+       SYMLINK(TEST_SYM_A, TEST_SYM_B);
+       SYMLINK(TEST_SYM_B, TEST_SYM_A);
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1);
+       errno = 0;
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (!((rc == -1) && (errno == ELOOP))) {
+               test_fail("bind() should have failed with ELOOP");
+       }
+       CLOSE(sd);
+
+       UNLINK(TEST_SUN_PATH);
+       UNLINK(TEST_SYM_A);
+       UNLINK(TEST_SYM_B);
+
+       debug("leaving test_bind()");
+}
+
+void test_listen(void)
+{
+       int sd;
+       int rc;
+
+       debug("entering test_listen()");
+
+       debug("Test listen() with a bad file descriptor");
+
+       errno = 0;
+       rc = listen(-1, 0);
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("listen(-1, 0) should have failed");
+       }
+
+       debug("Test listen() with a non-socket file descriptor");
+
+       errno = 0;
+       rc = listen(0, 0);
+       if (!(rc == -1 && errno == ENOTTY)) {
+               test_fail("listen(0, 0) should have failed");
+       }
+
+       debug("leaving test_listen()");
+}
+
+void test_shutdown(void)
+{
+       int how[3] = { SHUT_RD, SHUT_WR, SHUT_RDWR };
+       int sd;
+       int rc;
+       int i;
+
+       debug("entering test_shutdown()");
+
+       /* test for each direction (read, write, read-write) */
+       for (i = 0; i < 3; i++) {
+
+               debug("test shutdown() with an invalid descriptor");
+
+               errno = 0;
+               rc = shutdown(-1, how[i]);
+               if (!(rc == -1 && errno == EBADF)) {
+                       test_fail("shutdown(-1, how[i]) should have failed");
+               }
+
+               debug("test shutdown() with a non-socket descriptor");
+
+               errno = 0;
+               rc = shutdown(0, how[i]);
+               if (!(rc == -1 && errno == ENOSYS)) {
+                       test_fail("shutdown() should have failed with ENOSYS");
+               }
+
+               debug("test shutdown() with a socket that is not connected");
+
+               SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+               errno = 0;
+               rc = shutdown(sd, how[i]);
+               if (!(rc == -1 && errno == ENOTCONN)) {
+                       test_fail("shutdown() should have failed");
+               }
+               CLOSE(sd);
+       }
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       errno = 0;
+       rc = shutdown(sd, -1);
+       if (!(rc == -1 && errno == ENOTCONN)) {
+               test_fail("shutdown(sd, -1) should have failed with ENOTCONN");
+       }
+       CLOSE(sd);
+
+       debug("leaving test_shutdown()");
+}
+
+void test_close(void)
+{
+       struct sockaddr_un addr;
+       int sd, sd2;
+       int rc;
+
+       debug("entering test_close()");
+
+       UNLINK(TEST_SUN_PATH);
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       debug("Test close() success");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc != 0) {
+               test_fail("bind() should have worked");
+       }
+
+       CLOSE(sd);
+
+       debug("Close an already closed file descriptor");
+
+       errno = 0;
+       rc = close(sd);
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("close(sd) should have failed with EBADF");
+       }
+
+       UNLINK(TEST_SUN_PATH);
+
+       debug("dup()'ing a file descriptor and closing both should work");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc != 0) {
+               test_fail("bind() should have worked");
+       }
+
+       errno = 0;
+       sd2 = dup(sd);
+       if (sd2 == -1) {
+               test_fail("dup(sd) should have worked");
+       } else {
+               CLOSE(sd2);
+               CLOSE(sd);
+       }
+
+       UNLINK(TEST_SUN_PATH);
+
+       debug("leaving test_close()");
+}
+
+void test_sockopts(void)
+{
+       int i;
+       int rc;
+       int sd;
+       int option_value;
+       int option_value_orig;
+       socklen_t option_len;
+
+       debug("entering test_sockopts()");
+
+       for (i = 0; i < 3; i++) {
+
+               SOCKET(sd, PF_UNIX, types[i], 0);
+
+               debug("Test setsockopt() works");
+
+               option_value = 0;
+               option_len = sizeof(option_value);
+               errno = 0;
+               rc = getsockopt(sd, SOL_SOCKET, SO_TYPE, &option_value, 
+                                                       &option_len);
+               if (rc != 0) {
+                       test_fail("setsockopt() should have worked");
+               }
+
+               if (option_value != types[i]) {
+                       test_fail("SO_TYPE didn't seem to work.");
+               }
+
+               CLOSE(sd);
+       }
+
+
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       debug("Test setsockopt() works");
+
+       option_value = 0;
+       option_len = sizeof(option_value);
+       errno = 0;
+       rc = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &option_value, &option_len);
+       if (rc != 0) {
+               test_fail("getsockopt() should have worked");
+       }
+
+       if (option_value != PIPE_BUF) {
+               test_fail("SO_SNDBUF didn't seem to work.");
+       }
+
+       CLOSE(sd);
+
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       debug("Test setsockopt() works");
+
+       option_value = 0;
+       option_len = sizeof(option_value);
+       errno = 0;
+       rc = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &option_value, &option_len);
+       if (rc != 0) {
+               test_fail("getsockopt() should have worked");
+       }
+
+       if (option_value != PIPE_BUF) {
+               test_fail("SO_RCVBUF didn't seem to work.");
+       }
+
+       CLOSE(sd);
+
+
+       debug("leaving test_sockopts()");
+}
+
+void test_read(void)
+{
+       int rc;
+       int fd;
+       char buf[BUFSIZE];
+
+       debug("entering test_read()");
+
+       errno = 0;
+       rc = read(-1, buf, sizeof(buf));
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("read() should have failed with EBADF");
+       }
+
+       fd = open("/tmp", O_RDONLY);
+       if (fd == -1) {
+               test_fail("open(\"/tmp\", O_RDONLY) should have worked");
+       }
+
+       CLOSE(fd);
+
+       debug("leaving test_read()");
+}
+
+void test_write(void)
+{
+       int rc;
+       char buf[BUFSIZE];
+
+       debug("entering test_write()");
+
+       errno = 0;
+       rc = write(-1, buf, sizeof(buf));
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("write() should have failed with EBADF");
+       }
+
+       debug("leaving test_write()");
+}
+
+void test_dup(void)
+{
+       struct stat info1;
+       struct stat info2;
+       struct sockaddr_un addr;
+       int sd, sd2;
+       int rc;
+       int i;
+
+       debug("entering test_dup()");
+
+       UNLINK(TEST_SUN_PATH);
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       debug("Test dup()");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc != 0) {
+               test_fail("bind() should have worked");
+       }
+
+       errno = 0;
+       sd2 = dup(sd);
+       if (sd2 == -1) {
+               test_fail("dup(sd) should have worked");
+       }
+
+       rc = fstat(sd, &info1);
+       if (rc == -1) {
+               test_fail("fstat(fd, &info1) failed");
+       }
+
+       rc = fstat(sd2, &info2);
+       if (rc == -1) {
+               test_fail("fstat(sd, &info2) failed");
+       }
+
+       if (info1.st_ino != info2.st_ino) {
+               test_fail("dup() failed info1.st_ino != info2.st_ino");
+       }
+
+       CLOSE(sd);
+       CLOSE(sd2);
+
+       debug("Test dup() with a closed socket");
+
+       errno = 0;
+       rc = dup(sd);
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("dup(sd) on a closed socket shouldn't have worked");
+       }
+
+       debug("Test dup() with socket descriptor of -1");
+
+       errno = 0;
+       rc = dup(-1);
+       if (!(rc == -1 && errno == EBADF)) {
+               test_fail("dup(-1) shouldn't have worked");
+       }
+
+       debug("Test dup() when all of the file descriptors are taken");
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       for (i = 4; i < getdtablesize(); i++) {
+               rc = open("/dev/null", O_RDONLY);
+               if (rc == -1) {
+                       test_fail("we couldn't open /dev/null for read");
+               }
+       }
+
+       errno = 0;
+       sd2 = dup(sd);
+       if (!(sd2 == -1 && errno == EMFILE)) {
+               test_fail("dup(sd) should have failed with errno = EMFILE");
+       }
+
+       for (i = 3; i < getdtablesize(); i++) {
+               CLOSE(i);
+       }
+
+       UNLINK(TEST_SUN_PATH);
+
+       debug("leaving test_dup()");
+}
+
+void test_dup2(void)
+{
+       struct stat info1;
+       struct stat info2;
+       struct sockaddr_un addr;
+       int sd;
+       int fd;
+       int rc;
+
+       debug("entering test_dup2()");
+       UNLINK(TEST_SUN_PATH);
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc != 0) {
+               test_fail("bind() should have worked");
+       }
+
+       fd = open("/dev/null", O_RDONLY);
+       if (fd == -1) {
+               test_fail("open(\"/dev/null\", O_RDONLY) failed");
+       }
+
+       fd = dup2(sd, fd);
+       if (fd == -1) {
+               test_fail("dup2(sd, fd) failed.");
+       }
+
+       memset(&info1, '\0', sizeof(struct stat));
+       memset(&info2, '\0', sizeof(struct stat));
+
+       rc = fstat(fd, &info1);
+       if (rc == -1) {
+               test_fail("fstat(fd, &info1) failed");
+       }
+
+       rc = fstat(sd, &info2);
+       if (rc == -1) {
+               test_fail("fstat(sd, &info2) failed");
+       }
+
+       if (!(info1.st_ino == info2.st_ino &&
+               major(info1.st_dev) == major(info2.st_dev) && 
+               minor(info1.st_dev) == minor(info2.st_dev))) {
+
+               test_fail("dup2() failed");
+       }
+
+       CLOSE(fd);
+       CLOSE(sd);
+
+       UNLINK(TEST_SUN_PATH);
+       debug("leaving test_dup2()");
+
+}
+
+/*
+ * A toupper() server. This toy server converts a string to upper case.
+ */
+void test_xfer_server(pid_t pid)
+{
+       struct ucred credentials;
+       socklen_t ucred_length;
+       int i;
+       int on;
+       struct timeval tv;
+       fd_set readfds;
+       int status;
+       int rc;
+       int sd;
+       char buf[BUFSIZE];
+       socklen_t client_addr_size;
+       int client_sd;
+       struct sockaddr_un addr;
+       struct sockaddr_un client_addr;
+       uid_t euid;
+       gid_t egid;
+
+       on = 1;
+       status = 0;
+       rc = 0;
+       sd = 0;
+       ucred_length = sizeof(struct ucred);
+       client_sd = 0;
+       client_addr_size = sizeof(struct sockaddr_un);
+
+       memset(&buf, '\0', sizeof(buf));
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       memset(&client_addr, '\0', sizeof(struct sockaddr_un));
+
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc == -1) {
+               test_fail("bind() should have worked");
+       }
+
+       rc = listen(sd, 8);
+       if (rc == -1) {
+               test_fail("listen(sd, 8) should have worked");
+       }
+
+       /* we're ready for connections, time to tell the client to start
+        * the test
+        */
+       kill(pid, SIGUSR1);
+
+       tv.tv_sec = 10;
+       tv.tv_usec = 0;
+
+       FD_ZERO(&readfds);
+       FD_SET(sd, &readfds);
+
+       /* use select() in case the client is really broken and never 
+        * attempts to connect (we don't want to block on accept() 
+        * forever).
+        */
+       rc = select(sd + 1, &readfds, NULL, NULL, &tv);
+       if (rc == -1) {
+               test_fail("[server] select() should not have failed");
+       }
+
+       if (rc != 1) {
+               test_fail("[server] select() should have returned 1");
+               printf("[server] select returned %d\n", rc);
+       }
+
+       if (!(FD_ISSET(sd, &readfds))) {
+               test_fail("[server] client didn't connect within 10 seconds");
+               kill(pid, SIGKILL);
+               return;
+       }
+
+       client_sd = accept(sd, (struct sockaddr *) &client_addr,
+                                               &client_addr_size);
+
+       if (client_sd == -1) {
+               test_fail("accept() should have worked");
+               kill(pid, SIGKILL);
+               return;
+       } else {
+               debug("[server] client accept()'d");
+       }
+
+       debug("[server] Reading message");
+       rc = read(client_sd, buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("read() failed unexpectedly");
+               kill(pid, SIGKILL);
+               return;
+       }
+       debug("[server] we got the following message:");
+       debug(buf);
+
+       for (i = 0; i < rc && i < 127; i++) {
+               buf[i] = toupper(buf[i]);
+       }
+
+       debug("[server] Writing message...");
+       rc = write(client_sd, buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("write(client_sd, buf, sizeof(buf)) failed");
+               kill(pid, SIGKILL);
+               return;
+       }
+
+       if (rc < strlen(buf)) {
+               test_fail("[server] write didn't write all the bytes");
+       }
+
+       memset(&buf, '\0', sizeof(buf));
+
+       debug("[server] Recv message");
+       rc = recv(client_sd, buf, sizeof(buf), 0);
+       if (rc == -1) {
+               test_fail("recv() failed unexpectedly");
+               kill(pid, SIGKILL);
+               return;
+       }
+       debug("[server] we got the following message:");
+       debug(buf);
+
+       for (i = 0; i < rc && i < 127; i++) {
+               buf[i] = toupper(buf[i]);
+       }
+
+       debug("[server] Sending message...");
+       rc = send(client_sd, buf, sizeof(buf), 0);
+       if (rc == -1) {
+               test_fail("send(client_sd, buf, sizeof(buf), 0) failed");
+               kill(pid, SIGKILL);
+               return;
+       }
+
+       if (rc < strlen(buf)) {
+               test_fail("[server] write didn't write all the bytes");
+       }
+
+       memset(&buf, '\0', sizeof(buf));
+
+       debug("[server] Recvfrom message");
+       rc = recvfrom(client_sd, buf, sizeof(buf), 0, NULL, 0);
+       if (rc == -1) {
+               test_fail("recvfrom() failed unexpectedly");
+               kill(pid, SIGKILL);
+               return;
+       }
+       debug("[server] we got the following message:");
+       debug(buf);
+
+       for (i = 0; i < rc && i < 127; i++) {
+               buf[i] = toupper(buf[i]);
+       }
+
+       debug("[server] Sendto message...");
+       rc = sendto(client_sd, buf, sizeof(buf), 0, NULL, 0);
+       if (rc == -1) {
+               test_fail("sendto() failed");
+               kill(pid, SIGKILL);
+               return;
+       }
+
+       if (rc < strlen(buf)) {
+               test_fail("[server] write didn't write all the bytes");
+       }
+
+       shutdown(client_sd, SHUT_RDWR);
+       CLOSE(client_sd);
+
+       shutdown(sd, SHUT_RDWR);
+       CLOSE(sd);
+
+       /* wait for client to exit */
+       do {
+               errno = 0;
+               rc = waitpid(pid, &status, 0);
+       } while (rc == -1 && errno == EINTR);
+
+       /* we use the exit status to get it's error count */
+       errct += WEXITSTATUS(status);
+}
+
+int server_ready = 0;
+
+/* signal handler for the client */
+void test_xfer_sighdlr(int sig)
+{
+       debug("entering signal handler");
+       switch (sig) {
+               /* the server will send SIGUSR1 when it is time for us 
+                * to start the tests
+                */
+       case SIGUSR1:
+               server_ready = 1;
+               debug("got SIGUSR1, the server is ready for the client");
+               break;
+       default:
+               debug("didn't get SIGUSR1");
+       }
+       debug("leaving signal handler");
+}
+
+/*
+ * A toupper() client.
+ */
+void test_xfer_client(void)
+{
+       struct ucred credentials;
+       socklen_t ucred_length;
+       struct timeval tv;
+       fd_set readfds;
+       struct sockaddr_un addr;
+       struct sockaddr_un peer_addr;
+       socklen_t peer_addr_len;
+       int sd;
+       int rc;
+       char buf[BUFSIZE];
+       uid_t uid;
+       gid_t gid;
+
+       debug("[client] entering test_xfer_client()");
+       errct = 0;      /* reset error count */
+       ucred_length = sizeof(struct ucred);
+       memset(&buf, '\0', sizeof(buf));
+
+       while (server_ready == 0) {
+               debug("[client] waiting for the server to signal");
+               sleep(1);
+       }
+
+       peer_addr_len = sizeof(struct sockaddr_un);
+
+       debug("Creating symlink to TEST_SUN_PATH");
+
+       SYMLINK(TEST_SUN_PATH, TEST_SYM_A);
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       debug("[client] creating client socket");
+       SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
+
+       debug("[client] connecting to server through the symlink");
+       rc = connect(sd, (struct sockaddr *) &addr,
+                                               sizeof(struct sockaddr_un));
+       if (rc == -1) {
+               test_fail("[client] connect() should have worked");
+       } else {
+               debug("[client] connected");
+       }
+
+       debug("[client] testing getpeername()");
+       memset(&peer_addr, '\0', sizeof(struct sockaddr_un));
+       rc = getpeername(sd, (struct sockaddr *) &peer_addr, &peer_addr_len);
+       if (rc == -1) {
+               test_fail("[client] getpeername() should have worked");
+       }
+
+       /* we need to use the full path "/usr/src/test/DIR_56/test.sock"
+        * because that is what is returned by getpeername().
+        */
+       if (!(peer_addr.sun_family == AF_UNIX &&
+                       strncmp(peer_addr.sun_path,
+                       "/usr/src/test/DIR_56/test.sock", 
+                       sizeof(peer_addr.sun_path) - 1) == 0)) {
+
+               test_fail("getpeername() didn't return the right address");
+       }
+
+       strncpy(buf, "Hello, World!", sizeof(buf) - 1);
+       debug("[client] send to server");
+       rc = write(sd, buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("[client] write() failed unexpectedly");
+       }
+
+       memset(buf, '\0', sizeof(buf));
+       debug("[client] read from server");
+       rc = read(sd, buf, sizeof(buf));
+       if (rc == -1) {
+               test_fail("[client] read() failed unexpectedly");
+       } else {
+               debug("[client] we got the following message:");
+               debug(buf);
+       }
+
+       if (strncmp(buf, "HELLO, WORLD!", sizeof(buf)) != 0) {
+               test_fail("[client] We didn't get the correct response");
+       }
+
+       memset(&buf, '\0', sizeof(buf));
+       strncpy(buf, "Bonjour!", sizeof(buf) - 1);
+
+       debug("[client] send to server");
+       rc = send(sd, buf, sizeof(buf), 0);
+       if (rc == -1) {
+               test_fail("[client] send() failed unexpectedly");
+       }
+
+       debug("Test passing the client credentials to the server");
+
+       memset(&credentials, '\0', ucred_length);
+       rc = getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &credentials,
+                                                       &ucred_length);
+
+       if (rc == -1) {
+               test_fail("[client] getsockopt() failed");
+       }  else if (credentials.uid != geteuid() ||
+                                       credentials.gid != getegid()) {
+               printf("%d=%d=%d %d=%d=%d\n", credentials.uid, getuid(),
+                       geteuid(), credentials.gid, getgid(), getegid());
+               test_fail("[client] Credential passing gave us a bad UID/GID");
+       }
+
+       debug("Testing select()");
+
+       tv.tv_sec = 2;
+       tv.tv_usec = 500000;
+
+       FD_ZERO(&readfds);
+       FD_SET(sd, &readfds);
+
+       rc = select(sd + 1, &readfds, NULL, NULL, &tv);
+       if (rc == -1) {
+               test_fail("[client] select() should not have failed");
+       }
+
+       if (rc != 1) {
+               test_fail("[client] select() should have returned 1");
+       }
+
+       if (!(FD_ISSET(sd, &readfds))) {
+               test_fail("The server didn't respond within 2.5 seconds");
+       }
+
+       memset(buf, '\0', sizeof(buf));
+       debug("[client] recv from server");
+       rc = recv(sd, buf, sizeof(buf), 0);
+       if (rc == -1) {
+               test_fail("[client] recv() failed unexpectedly");
+       } else {
+               debug("[client] we got the following message:");
+               debug(buf);
+       }
+
+       if (strncmp(buf, "BONJOUR!", sizeof(buf)) != 0) {
+               test_fail("[client] We didn't get the right response.");
+       }
+
+       memset(&buf, '\0', sizeof(buf));
+       strncpy(buf, "Hola!", sizeof(buf) - 1);
+
+       debug("[client] sendto to server");
+       rc = sendto(sd, buf, sizeof(buf), 0, NULL, 0);
+       if (rc == -1) {
+               test_fail("[client] sendto() failed");
+       }
+
+       debug("Testing select()");
+
+       tv.tv_sec = 2;
+       tv.tv_usec = 500000;
+
+       FD_ZERO(&readfds);
+       FD_SET(sd, &readfds);
+
+       rc = select(sd + 1, &readfds, NULL, NULL, &tv);
+       if (rc == -1) {
+               test_fail("[client] select() should not have failed");
+       }
+
+       if (rc != 1) {
+               test_fail("[client] select() should have returned 1");
+       }
+
+       if (!(FD_ISSET(sd, &readfds))) {
+               test_fail("[client] The server didn't respond in 2.5 seconds");
+       }
+
+       memset(buf, '\0', sizeof(buf));
+       debug("[client] recvfrom from server");
+       rc = recvfrom(sd, buf, sizeof(buf), 0, NULL, 0);
+       if (rc == -1) {
+               test_fail("[cleint] recvfrom() failed unexpectedly");
+       } else {
+               debug("[client] we got the following message:");
+               debug(buf);
+       }
+
+       if (strncmp(buf, "HOLA!", sizeof(buf)) != 0) {
+               test_fail("[client] We didn't get the right response.");
+       }
+
+       debug("[client] closing socket");
+       CLOSE(sd);
+
+       UNLINK(TEST_SYM_A);
+
+       debug("[client] leaving test_xfer_client()");
+       exit(errct);
+}
+
+void test_xfer(void)
+{
+       pid_t pid;
+
+       UNLINK(TEST_SUN_PATH);
+
+       /* the signal handler is only used by the client, but we have to 
+        * install it now. if we don't the server may signal the client 
+        * before the handler is installed.
+        */
+       debug("installing signal handler");
+       if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
+               test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
+       }
+
+       debug("signal handler installed");
+
+       server_ready = 0;
+
+       pid = fork();
+       if (pid == -1) {
+               test_fail("fork() failed");
+               return;
+       }
+
+       if (pid == 0) {
+               debug("child");
+               test_xfer_client();
+               test_fail("we should never get here");
+       } else {
+               debug("parent");
+               test_xfer_server(pid);
+               debug("parent done");
+       }
+
+       UNLINK(TEST_SUN_PATH);
+}
+
+void test_simple_client(int type)
+{
+       char buf[BUFSIZE];
+       int sd, rc;
+       struct sockaddr_un addr;
+
+       sd = socket(PF_UNIX, type, 0);
+       if (sd == -1) {
+               test_fail("socket");
+               return;
+       }
+
+       while (server_ready == 0) {
+               debug("[client] waiting for the server");
+               sleep(1);
+       }
+
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       bzero(buf, BUFSIZE);
+       snprintf(buf, BUFSIZE-1, "Hello, My Name is Client.");
+
+
+       if (type == SOCK_DGRAM) {
+
+               rc = sendto(sd, buf, strlen(buf) + 1, 0,
+                       (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+               if (rc == -1) {
+                       test_fail("sendto");
+                       return;
+               }
+
+       } else {
+
+               rc = connect(sd, (struct sockaddr *) &addr,
+                                       sizeof(struct sockaddr_un));
+               if (rc == -1) {
+                       test_fail("connect");
+                       return;
+               }
+
+               rc = write(sd, buf, strlen(buf) + 1);
+               if (rc == -1) {
+                       test_fail("write");
+               }
+
+               rc = read(sd, buf, BUFSIZE);
+               if (rc == -1) {
+                       test_fail("read");
+               }
+
+       }
+
+       rc = close(sd);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       exit(errct);
+}
+
+void test_simple_server(int type, pid_t pid)
+{
+       char buf[BUFSIZE];
+       int sd, rc, client_sd, status;
+       struct sockaddr_un addr;
+       socklen_t addr_len;
+
+       sd = socket(PF_UNIX, type, 0);
+       if (sd == -1) {
+               test_fail("socket");
+       }
+
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+       addr.sun_family = AF_UNIX;
+
+       rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+       if (rc == -1) {
+               test_fail("bind");
+       }
+
+       if (type == SOCK_DGRAM) {
+
+               /* ready for client */
+               kill(pid, SIGUSR1);
+
+               rc = recvfrom(sd, buf, BUFSIZE, 0,
+                               (struct sockaddr *) &addr, &addr_len);
+               if (rc == -1) {
+                       test_fail("recvfrom");
+               }
+
+       } else {
+
+               rc = listen(sd, 5);
+               if (rc == -1) {
+                       test_fail("listen");
+               }
+
+               /* we're ready for connections, time to tell the client 
+                * to start the test
+                */
+               kill(pid, SIGUSR1);
+
+               client_sd = accept(sd, (struct sockaddr *) &addr, &addr_len);
+               if (client_sd == -1) {
+                       test_fail("accept");
+               }
+
+               rc = read(client_sd, buf, BUFSIZE);
+               if (rc == -1) {
+                       test_fail("read");
+               }
+
+               /* added for extra fun to make the client block on read() */
+               sleep(1);
+
+               bzero(buf, BUFSIZE);
+               snprintf(buf, BUFSIZE-1, "Hello, My Name is Server.");
+
+               rc = write(client_sd, buf, strlen(buf) + 1);
+               if (rc == -1) {
+                       test_fail("write");
+               }
+
+               rc = close(client_sd);
+               if (rc == -1) {
+                       test_fail("close");
+               }
+
+       }
+
+       rc = close(sd);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       /* wait for client to exit */
+       do {
+               errno = 0;
+               rc = waitpid(pid, &status, 0);
+       } while (rc == -1 && errno == EINTR);
+
+       /* we use the exit status to get it's error count */
+       errct += WEXITSTATUS(status);
+}
+
+void test_simple_client_server(int type)
+{
+       pid_t pid;
+
+       UNLINK(TEST_SUN_PATH);
+
+       /* the signal handler is only used by the client, but we have to 
+        * install it now. if we don't the server may signal the client 
+        * before the handler is installed.
+        */
+       debug("installing signal handler");
+       if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
+               test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
+       }
+
+       debug("signal handler installed");
+
+       server_ready = 0;
+
+       pid = fork();
+       if (pid == -1) {
+               test_fail("fork() failed");
+               return;
+       }
+
+       if (pid == 0) {
+               debug("child");
+               test_simple_client(type);
+               test_fail("we should never get here");
+       } else {
+               debug("parent");
+               test_simple_server(type, pid);
+               debug("parent done");
+       }
+
+       UNLINK(TEST_SUN_PATH);
+}
+
+void test_vectorio(int type)
+{
+       int sv[2];
+       int rc;
+       struct iovec iov[3];
+       char buf1[BUFSIZE];
+       char buf2[BUFSIZE];
+       char buf3[BUFSIZE];
+       char buf4[BUFSIZE*3];
+       const struct iovec *iovp = iov;
+
+       debug("begin vectorio tests");
+
+       memset(buf1, '\0', BUFSIZE);
+       strncpy(buf1, "HELLO ", BUFSIZE - 1);
+
+       memset(buf2, '\0', BUFSIZE);
+       strncpy(buf2, "WORLD", BUFSIZE - 1);
+
+       memset(buf3, '\0', BUFSIZE);
+
+       rc = socketpair(PF_UNIX, type, 0, sv);
+       if (rc == -1) {
+               test_fail("socketpair");
+       }
+
+       iov[0].iov_base = buf1;
+       iov[0].iov_len  = strlen(buf1);
+       iov[1].iov_base = buf2;
+       iov[1].iov_len  = strlen(buf2);
+       iov[2].iov_base = buf3;
+       iov[2].iov_len  = 1;
+
+       rc = writev(sv[0], iovp, 3);
+       if (rc == -1) {
+               test_fail("writev");
+       }
+
+       memset(buf4, '\0', BUFSIZE*3);
+
+       rc = read(sv[1], buf4, BUFSIZE*3);
+       if (rc == -1) {
+               test_fail("read");
+       }
+
+       if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) {
+               test_fail("the string we read was not 'HELLO WORLD'");
+       }
+
+       memset(buf1, '\0', BUFSIZE);
+       strncpy(buf1, "Unit Test Time", BUFSIZE - 1);
+
+       rc = write(sv[1], buf1, strlen(buf1) + 1);
+       if (rc == -1) {
+               test_fail("write");
+       }
+
+       memset(buf2, '\0', BUFSIZE);
+       memset(buf3, '\0', BUFSIZE);
+       memset(buf4, '\0', BUFSIZE*3);
+
+       iov[0].iov_base = buf2;
+       iov[0].iov_len  = 5;
+       iov[1].iov_base = buf3;
+       iov[1].iov_len  = 5;
+       iov[2].iov_base = buf4;
+       iov[2].iov_len  = 32;
+
+       rc = readv(sv[0], iovp, 3);
+       if (rc == -1) {
+               test_fail("readv");
+       }
+
+       if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || 
+                                       strncmp(buf4, "Time", 4)) {
+               test_fail("readv");
+       }
+
+       rc = close(sv[0]);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       rc = close(sv[1]);
+       if (rc == -1) {
+               test_fail("close");
+       }
+}
+
+void test_msg(int type)
+{
+       int sv[2];
+       int rc;
+       struct msghdr msg1;
+       struct msghdr msg2;
+       struct iovec iov[3];
+       char buf1[BUFSIZE];
+       char buf2[BUFSIZE];
+       char buf3[BUFSIZE];
+       char buf4[BUFSIZE*3];
+
+       debug("begin sendmsg/recvmsg tests");
+
+       memset(buf1, '\0', BUFSIZE);
+       strncpy(buf1, "HELLO ", BUFSIZE - 1);
+
+       memset(buf2, '\0', BUFSIZE);
+       strncpy(buf2, "WORLD", BUFSIZE - 1);
+
+       memset(buf3, '\0', BUFSIZE);
+
+       rc = socketpair(PF_UNIX, type, 0, sv);
+       if (rc == -1) {
+               test_fail("socketpair");
+       }
+
+       iov[0].iov_base = buf1;
+       iov[0].iov_len  = strlen(buf1);
+       iov[1].iov_base = buf2;
+       iov[1].iov_len  = strlen(buf2);
+       iov[2].iov_base = buf3;
+       iov[2].iov_len  = 1;
+
+       memset(&msg1, '\0', sizeof(struct msghdr));
+       msg1.msg_name = NULL;
+       msg1.msg_namelen = 0;
+       msg1.msg_iov = iov;
+       msg1.msg_iovlen = 3;
+       msg1.msg_control = NULL;
+       msg1.msg_controllen = 0;
+       msg1.msg_flags = 0;
+
+       rc = sendmsg(sv[0], &msg1, 0);
+       if (rc == -1) {
+               test_fail("writev");
+       }
+
+       memset(buf4, '\0', BUFSIZE*3);
+
+       rc = read(sv[1], buf4, BUFSIZE*3);
+       if (rc == -1) {
+               test_fail("read");
+       }
+
+       if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) {
+               test_fail("the string we read was not 'HELLO WORLD'");
+       }
+
+       memset(buf1, '\0', BUFSIZE);
+       strncpy(buf1, "Unit Test Time", BUFSIZE - 1);
+
+       rc = write(sv[1], buf1, strlen(buf1) + 1);
+       if (rc == -1) {
+               test_fail("write");
+       }
+
+       memset(buf2, '\0', BUFSIZE);
+       memset(buf3, '\0', BUFSIZE);
+       memset(buf4, '\0', BUFSIZE*3);
+
+       iov[0].iov_base = buf2;
+       iov[0].iov_len  = 5;
+       iov[1].iov_base = buf3;
+       iov[1].iov_len  = 5;
+       iov[2].iov_base = buf4;
+       iov[2].iov_len  = 32;
+
+       memset(&msg2, '\0', sizeof(struct msghdr));
+       msg2.msg_name = NULL;
+       msg2.msg_namelen = 0;
+       msg2.msg_iov = iov;
+       msg2.msg_iovlen = 3;
+       msg2.msg_control = NULL;
+       msg2.msg_controllen = 0;
+       msg2.msg_flags = 0;
+
+       rc = recvmsg(sv[0], &msg2, 0);
+       if (rc == -1) {
+               test_fail("readv");
+       }
+
+       if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || 
+                                       strncmp(buf4, "Time", 4)) {
+               test_fail("readv");
+       }
+
+       rc = close(sv[0]);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       rc = close(sv[1]);
+       if (rc == -1) {
+               test_fail("close");
+       }
+}
+
+void test_msg_dgram(void)
+{
+       int rc;
+       int src;
+       int dst;
+       struct sockaddr_un addr;
+       struct iovec iov[3];
+       struct msghdr msg1;
+       struct msghdr msg2;
+       char buf1[BUFSIZE];
+       char buf2[BUFSIZE];
+       char buf3[BUFSIZE];
+       socklen_t addrlen = sizeof(struct sockaddr_un);
+
+       UNLINK(TEST_SUN_PATH);
+       UNLINK(TEST_SUN_PATHB);
+
+       src = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (src == -1) {
+               test_fail("socket");
+       }
+
+       dst = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (dst == -1) {
+               test_fail("socket");
+       }
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, TEST_SUN_PATHB, sizeof(addr.sun_path) - 1);
+       rc = bind(src, (struct sockaddr *) &addr, addrlen);
+       if (rc == -1) {
+               test_fail("bind");
+       }
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
+
+       rc = bind(dst, (struct sockaddr *) &addr, addrlen);
+       if (rc == -1) {
+               test_fail("bind");
+       }
+
+       memset(&buf1, '\0', BUFSIZE);
+       memset(&buf2, '\0', BUFSIZE);
+       memset(&buf3, '\0', BUFSIZE);
+
+       strncpy(buf1, "Minix ", BUFSIZE-1);
+       strncpy(buf2, "is ", BUFSIZE-1);
+       strncpy(buf3, "great!", BUFSIZE-1);
+
+       iov[0].iov_base = buf1;
+       iov[0].iov_len  = 6;
+       iov[1].iov_base = buf2;
+       iov[1].iov_len  = 3;
+       iov[2].iov_base = buf3;
+       iov[2].iov_len  = 32;
+
+       memset(&msg1, '\0', sizeof(struct msghdr));
+       msg1.msg_name = &addr;
+       msg1.msg_namelen = addrlen;
+       msg1.msg_iov = iov;
+       msg1.msg_iovlen = 3;
+       msg1.msg_control = NULL;
+       msg1.msg_controllen = 0;
+       msg1.msg_flags = 0;
+
+       rc = sendmsg(src, &msg1, 0);
+       if (rc == -1) {
+               test_fail("sendmsg");
+       }
+
+       memset(&buf1, '\0', BUFSIZE);
+       memset(&buf2, '\0', BUFSIZE);
+
+       iov[0].iov_base = buf1;
+       iov[0].iov_len  = 9;
+       iov[1].iov_base = buf2;
+       iov[1].iov_len  = 32;
+
+       memset(&addr, '\0', sizeof(struct sockaddr_un));
+       memset(&msg2, '\0', sizeof(struct msghdr));
+       msg2.msg_name = &addr;
+       msg2.msg_namelen = sizeof(struct sockaddr_un);
+       msg2.msg_iov = iov;
+       msg2.msg_iovlen = 2;
+       msg2.msg_control = NULL;
+       msg2.msg_controllen = 0;
+       msg2.msg_flags = 0;
+
+       rc = recvmsg(dst, &msg2, 0);
+       if (rc == -1) {
+               test_fail("recvmsg");
+       }
+
+       if (strncmp(buf1, "Minix is ", 9) || strncmp(buf2, "great!", 6)) {
+               test_fail("recvmsg");
+       }
+
+       /* we need to use the full path "/usr/src/test/DIR_56/testb.sock"
+        * because that is what is returned by recvmsg().
+        */
+       if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path,
+                                       "/usr/src/test/DIR_56/testb.sock")) {
+               test_fail("recvmsg");
+       }
+
+       rc = close(dst);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       rc = close(src);
+       if (rc == -1) {
+               test_fail("close");
+       }
+
+       UNLINK(TEST_SUN_PATH);
+       UNLINK(TEST_SUN_PATHB);
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+
+       debug("entering main()");
+
+       start(56);
+
+       test_socket();
+       test_listen();
+       test_getsockname();
+       test_header();
+       test_shutdown();
+       test_close();
+       test_dup();
+       test_dup2();
+       test_socketpair();
+       test_shutdown();
+       test_read();
+       test_write();
+       test_sockopts();
+       test_ucred();
+       test_bind();
+       test_xfer();
+
+       for (i = 0; i < 3; i++) {
+               test_simple_client_server(types[i]);
+               if (types[i] != SOCK_DGRAM) test_vectorio(types[i]);
+               if (types[i] != SOCK_DGRAM) test_msg(types[i]);
+       }
+
+       test_msg_dgram();
+
+       cleanup();
+       quit();
+
+       return -1;      /* we should never get here */
+}