From simon@comsys.ntu-kpi.kiev.ua  Tue Feb 10 17:39:13 2009
Return-Path: <simon@comsys.ntu-kpi.kiev.ua>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 31B60106564A
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 10 Feb 2009 17:39:13 +0000 (UTC)
	(envelope-from simon@comsys.ntu-kpi.kiev.ua)
Received: from comsys.ntu-kpi.kiev.ua (comsys.ntu-kpi.kiev.ua [77.47.192.42])
	by mx1.freebsd.org (Postfix) with ESMTP id 728178FC1D
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 10 Feb 2009 17:38:38 +0000 (UTC)
	(envelope-from simon@comsys.ntu-kpi.kiev.ua)
Received: from pm513-1.comsys.ntu-kpi.kiev.ua (pm513-1.comsys.ntu-kpi.kiev.ua [10.18.52.101])
	(authenticated bits=0)
	by comsys.ntu-kpi.kiev.ua (8.13.7/8.13.7) with ESMTP id n1AHhId5010054
	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO)
	for <FreeBSD-gnats-submit@freebsd.org>; Tue, 10 Feb 2009 19:43:18 +0200 (EET)
Received: by pm513-1.comsys.ntu-kpi.kiev.ua (Postfix, from userid 1001)
	id 9951A1CC3F; Tue, 10 Feb 2009 19:38:25 +0200 (EET)
Message-Id: <20090210173825.GA69591@pm513-1.comsys.ntu-kpi.kiev.ua>
Date: Tue, 10 Feb 2009 19:38:25 +0200
From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: FreeBSD-gnats-submit@freebsd.org
Subject: Update for regression/sockets/unix_cmsg

>Number:         131567
>Category:       bin
>Synopsis:       [socket] [patch] Update for regression/sockets/unix_cmsg
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    pluknet
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Tue Feb 10 17:40:01 UTC 2009
>Closed-Date:    Mon Apr 29 21:35:21 UTC 2013
>Last-Modified:  Mon Apr 29 21:40:01 UTC 2013
>Originator:     Andrey Simonenko
>Release:        FreeBSD 7.1-STABLE amd64
>Organization:
>Environment:
>Description:

This is update for unix_cmsg tests:

1. Added test for LOCAL_PEERCRED in unix_cmsg.c and its description to
   README.  As a result some test numbers were changed in unix_cmsg.t.

2. If some incorrect data was found, then finish test, do not check next
   data.

3. unix_cmsg.c code was simplified, some parts were modified to match
   style(9).

4. Now timeout in unix_cmsg.c is 5 seconds (previous value was 60 seconds).
   Now "prove unix_cmsg.t" takes 10 seconds (previous version took 120
   seconds).

This version of unix_cmsg was tested on 7.1 and 8.0 on amd64.

>How-To-Repeat:
>Fix:

diff -ruN unix_cmsg.orig/README unix_cmsg/README
--- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
+++ unix_cmsg/README	2009-02-03 17:41:42.000000000 +0200
@@ -1,7 +1,7 @@
 $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
 
 About unix_cmsg
-================
+===============
 
 This program is a collection of regression tests for ancillary (control)
 data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
@@ -13,8 +13,8 @@
 messages to Server.
 
 It is better to change the owner of unix_cmsg to some safe user
-(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
-can give correct results for wrong implementation.
+(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
+check credentials can give correct results for wrong implementation.
 
 Available options
 =================
@@ -24,7 +24,7 @@
 
 -h	Output help message and exit.
 
--t <socktype>
+-t socktype
 	Run tests only for the given socket type: "stream" or "dgram".
 	With this option it is possible to run only particular test,
 	not all of them.
@@ -90,6 +90,13 @@
     message with data and control message with SCM_TIMESTAMP type
     followed by struct timeval{}.
 
+ 6: Check LOCAL_PEERCRED socket option
+
+    This test does not use control data for PF_LOCAL sockets, but can be
+    implemented here.  Client connects to Server.  Both Client and Server
+    verify that credentials of the peer are correct using LOCAL_PEERCRED
+    socket option.
+
 For SOCK_DGRAM sockets:
 ----------------------
 
@@ -110,7 +117,7 @@
     structure should contain correct information.
 
  3: Sending cmsgcred, receiving sockcred
- 
+
     Server creates datagram socket and set socket option LOCAL_CREDS
     for it.  Client sends one message with data and control message with
     SOCK_CREDS type to Server.  Server should receive one message with
diff -ruN unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
--- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
+++ unix_cmsg/unix_cmsg.c	2009-02-10 19:11:11.000000000 +0200
@@ -27,10 +27,11 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
 
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/resource.h>
 #include <sys/time.h>
 #include <sys/socket.h>
+#include <sys/ucred.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 
@@ -68,7 +69,7 @@
  *
  * Each function which can block, is run under TIMEOUT, if timeout
  * occurs, then test function returns -2 or a client process exits
- * with nonzero return code.
+ * with non-zero return code.
  */
 
 #ifndef LISTENQ
@@ -76,14 +77,14 @@
 #endif
 
 #ifndef TIMEOUT
-# define TIMEOUT	60
+# define TIMEOUT	5
 #endif
 
 #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 
 static int	t_cmsgcred(void), t_sockcred_stream1(void);
 static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
-static int	t_sockcred_dgram(void), t_timestamp(void);
+static int	t_sockcred_dgram(void), t_timestamp(void), t_peercred(void);
 
 struct test_func {
 	int	(*func)(void);	/* Pointer to function.	*/
@@ -91,31 +92,57 @@
 };
 
 static struct test_func test_stream_tbl[] = {
-	{ NULL,			" 0: All tests" },
-	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
-	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
-	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
-	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
-	{ t_timestamp,		" 5: Sending, receiving timestamp" },
-	{ NULL, NULL }
+	{ .func = NULL,
+	  .desc = "All tests"
+	},
+	{ .func = t_cmsgcred,
+	  .desc = "Sending, receiving cmsgcred"
+	},
+	{ .func = t_sockcred_stream1,
+	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
+	},
+	{ .func = t_sockcred_stream2,
+	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
+	},
+	{ .func = t_cmsgcred_sockcred,
+	  .desc = "Sending cmsgcred, receiving sockcred"
+	},
+	{ .func = t_timestamp,
+	  .desc = "Sending, receiving timestamp"
+	},
+	{ .func = t_peercred,
+	  .desc = "Check LOCAL_PEERCRED socket option"
+	}
 };
 
+#define TEST_STREAM_TBL_SIZE \
+	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
+
 static struct test_func test_dgram_tbl[] = {
-	{ NULL,			" 0: All tests" },
-	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
-	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
-	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
-	{ t_timestamp,		" 4: Sending, receiving timestamp" },
-	{ NULL, NULL }
+	{ .func = NULL,
+	  .desc = "All tests"
+	},
+	{ .func = t_cmsgcred,
+	  .desc = "Sending, receiving cmsgcred"
+	},
+	{ .func = t_sockcred_dgram,
+	  .desc = "Receiving sockcred"
+	},
+	{ .func = t_cmsgcred_sockcred,
+	  .desc = "Sending cmsgcred, receiving sockcred"
+	},
+	{ .func = t_timestamp,
+	  .desc = "Sending, receiving timestamp"
+	}
 };
 
-#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
-#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
+#define TEST_DGRAM_TBL_SIZE \
+	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
 
-static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
+static const char *myname;		/* "SERVER" or "CLIENT" */
 
-static int	debug = 0;		/* 1, if -d. */
-static int	no_control_data = 0;	/* 1, if -z. */
+static int	debug = 0;		/* -d */
+static int	no_control_data = 0;	/* -z */
 
 static u_int	nfailed = 0;		/* Number of failed tests. */
 
@@ -164,24 +191,23 @@
 static void
 usage(int quick)
 {
-	const struct test_func *test_func;
+	u_int i;
 
-	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
+	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 	    __progname);
 	if (quick)
 		return;
 	fprintf(stderr, "\n Options are:\n\
   -d\t\t\tOutput debugging information\n\
   -h\t\t\tOutput this help message and exit\n\
-  -t <socktype>\t\tRun test only for the given socket type:\n\
-\t\t\tstream or dgram\n\
+  -t socktype\t\tRun test only for socket type: stream or dgram\n\
   -z\t\t\tDo not send real control data if possible\n\n");
 	fprintf(stderr, " Available tests for stream sockets:\n");
-	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
-		fprintf(stderr, "  %s\n", test_func->desc);
+	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
+		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
 	fprintf(stderr, "\n Available tests for datagram sockets:\n");
-	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
-		fprintf(stderr, "  %s\n", test_func->desc);
+	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
+		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
 }
 
 /*
@@ -210,8 +236,7 @@
 	va_list ap;
 	int errno_save;
 
-	errno_save = errno;		/* Save errno. */
-
+	errno_save = errno;
 	va_start(ap, format);
 	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
@@ -220,8 +245,7 @@
 	else
 		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 	va_end(ap);
-
-	errno = errno_save;		/* Restore errno. */
+	errno = errno_save;
 }
 
 /*
@@ -241,22 +265,35 @@
 }
 
 /*
- * Run tests from testno1 to testno2.
+ * Run tests for the given socket type.
  */
 static int
-run_tests(u_int testno1, u_int testno2)
+run_tests(int type, u_int testno1)
 {
-	const struct test_func *test_func;
-	u_int i, nfailed1;
+	const struct test_func *tf;
+	u_int i, nfailed1, testno2;
 
-	output("Running tests for %s sockets:\n", sock_type_str);
-	test_func = (sock_type == SOCK_STREAM ?
-	    test_stream_tbl : test_dgram_tbl) + testno1;
+	sock_type = type;
+	if (type == SOCK_STREAM) {
+		sock_type_str = "SOCK_STREAM";
+		tf = test_stream_tbl;
+		i = TEST_STREAM_TBL_SIZE - 1;
+	} else {
+		sock_type_str = "SOCK_DGRAM";
+		tf = test_dgram_tbl;
+		i = TEST_DGRAM_TBL_SIZE - 1;
+	}
+	if (testno1 == 0) {
+		testno1 = 1;
+		testno2 = i;
+	} else
+		testno2 = testno1;
 
+	output("Running tests for %s sockets:\n", sock_type_str);
 	nfailed1 = 0;
-	for (i = testno1; i <= testno2; ++test_func, ++i) {
-		output(" %s\n", test_func->desc);
-		switch (test_func->func()) {
+	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
+		output("  %u: %s\n", i, tf->desc);
+		switch (tf->func()) {
 		case -1:
 			++nfailed1;
 			break;
@@ -300,22 +337,35 @@
 	struct sigaction sa;
 
 	sa.sa_handler = SIG_IGN;
-	sigemptyset(&sa.sa_mask);
 	sa.sa_flags = 0;
-	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
+	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0)
 		err(EX_OSERR, "sigaction(SIGPIPE)");
 
 	sa.sa_handler = sig_alrm;
+	sigemptyset(&sa.sa_mask);
 	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 		err(EX_OSERR, "sigaction(SIGALRM)");
 }
 
+static int
+fork_client(void)
+{
+	client_pid = fork();
+	if (client_pid == 0)
+		myname = "CLIENT";
+	else if (client_pid == (pid_t)-1) {
+		logmsg("fork");
+		return (-1);
+	}
+	return (0);
+}
+
 int
 main(int argc, char *argv[])
 {
 	const char *errstr;
-	int opt, dgramflag, streamflag;
-	u_int testno1, testno2;
+	u_int testno;
+	int opt, rv, dgramflag, streamflag;
 
 	dgramflag = streamflag = 0;
 	while ((opt = getopt(argc, argv, "dht:z")) != -1)
@@ -332,7 +382,7 @@
 			else if (strcmp(optarg, "dgram") == 0)
 				dgramflag = 1;
 			else
-				errx(EX_USAGE, "wrong socket type in -t option");
+				errx(EX_USAGE, "option -t: wrong socket type");
 			break;
 		case 'z':
 			no_control_data = 1;
@@ -346,38 +396,40 @@
 	if (optind < argc) {
 		if (optind + 1 != argc)
 			errx(EX_USAGE, "too many arguments");
-		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
+		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 		if (errstr != NULL)
 			errx(EX_USAGE, "wrong test number: %s", errstr);
 	} else
-		testno1 = 0;
+		testno = 0;
 
 	if (dgramflag == 0 && streamflag == 0)
 		dgramflag = streamflag = 1;
 
-	if (dgramflag && streamflag && testno1 != 0)
-		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
+	if (dgramflag && streamflag && testno != 0)
+		errx(EX_USAGE, "you can use particular test, only with "
+		    "datagram or stream sockets");
 
 	if (streamflag) {
-		if (testno1 > TEST_STREAM_NO_MAX)
-			errx(EX_USAGE, "given test %u for stream sockets does not exist",
-			    testno1);
+		if (testno >= TEST_STREAM_TBL_SIZE)
+			errx(EX_USAGE, "given test %u for stream sockets "
+			    "does not exist", testno);
 	} else {
-		if (testno1 > TEST_DGRAM_NO_MAX)
-			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
-			    testno1);
+		if (testno >= TEST_DGRAM_TBL_SIZE)
+			errx(EX_USAGE, "given test %u for datagram sockets "
+			    "does not exist", testno);
 	}
 
 	my_uid = getuid();
 	my_euid = geteuid();
 	my_gid = getgid();
 	my_egid = getegid();
-	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
+	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
+	switch (my_ngids) {
 	case -1:
 		err(EX_SOFTWARE, "getgroups");
 		/* NOTREACHED */
 	case 0:
-		errx(EX_OSERR, "getgroups returned 0 groups");
+		errx(EX_OSERR, "getgroups returned no groups");
 	}
 
 	sig_init();
@@ -385,42 +437,21 @@
 	if (mkdtemp(tempdir) == NULL)
 		err(EX_OSERR, "mkdtemp");
 
-	if (streamflag) {
-		sock_type = SOCK_STREAM;
-		sock_type_str = "SOCK_STREAM";
-		if (testno1 == 0) {
-			testno1 = 1;
-			testno2 = TEST_STREAM_NO_MAX;
-		} else
-			testno2 = testno1;
-		if (run_tests(testno1, testno2) < 0)
-			goto failed;
-		testno1 = 0;
-	}
-
-	if (dgramflag) {
-		sock_type = SOCK_DGRAM;
-		sock_type_str = "SOCK_DGRAM";
-		if (testno1 == 0) {
-			testno1 = 1;
-			testno2 = TEST_DGRAM_NO_MAX;
-		} else
-			testno2 = testno1;
-		if (run_tests(testno1, testno2) < 0)
-			goto failed;
-	}
+	myname = "SERVER";
+	rv = EX_OK;
+	if (streamflag)
+		if (run_tests(SOCK_STREAM, testno) < 0)
+			rv = EX_OSERR;
+	if (dgramflag && rv == EX_OK)
+		if (run_tests(SOCK_DGRAM, testno) < 0)
+			rv = EX_OSERR;
 
 	if (rmdir(tempdir) < 0) {
 		logmsg("rmdir(%s)", tempdir);
-		return (EX_OSERR);
+		rv = EX_OSERR;
 	}
 
-	return (nfailed ? EX_OSERR : EX_OK);
-
-failed:
-	if (rmdir(tempdir) < 0)
-		logmsg("rmdir(%s)", tempdir);
-	return (EX_OSERR);
+	return (nfailed ? EX_OSERR : rv);
 }
 
 /*
@@ -433,27 +464,30 @@
 {
 	int rv, fd;
 
-	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
+	fd = socket(PF_LOCAL, sock_type, 0);
+	if (fd < 0) {
 		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 		return (-1);
 	}
 
 	if (sock_path != NULL) {
-		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
-		    tempdir, myname)) < 0) {
+		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
+		    myname);
+		if (rv < 0) {
 			logmsg("create_socket: snprintf failed");
 			goto failed;
 		}
 		if ((size_t)rv >= sock_path_len) {
-			logmsgx("create_socket: too long path name for given buffer");
+			logmsgx("create_socket: too long path name for given "
+			    "buffer");
 			goto failed;
 		}
 
 		memset(addr, 0, sizeof(addr));
 		addr->sun_family = AF_LOCAL;
 		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
-			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
-			    (u_long)sizeof(addr->sun_path));
+			logmsgx("create_socket: too long path name (>= %zu) "
+			    "for local domain socket", sizeof(addr->sun_path));
 			goto failed;
 		}
 		strcpy(addr->sun_path, sock_path);
@@ -479,7 +513,8 @@
 static int
 create_server_socket(void)
 {
-	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
+	return (create_socket(serv_sock_path, sizeof(serv_sock_path),
+	    &servaddr));
 }
 
 /*
@@ -498,12 +533,13 @@
 static int
 close_socket(const char *sock_path, int fd)
 {
-	int error = 0;
+	int error;
 
 	if (close(fd) < 0) {
 		logmsg("close_socket: close");
 		error = -1;
-	}
+	} else
+		error = 0;
 	if (sock_path != NULL)
 		if (unlink(sock_path) < 0) {
 			logmsg("close_socket: unlink(%s)", sock_path);
@@ -540,17 +576,16 @@
 {
 	ssize_t nsent;
 
-	dbgmsg(("sending %lu bytes", (u_long)n));
+	dbgmsg(("sending %zu bytes", n));
 
 	if (sigsetjmp(env_alrm, 1) != 0) {
-		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
+		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)",
+		    serv_sock_path);
 		return (-1);
 	}
 
 	(void)alarm(TIMEOUT);
-
 	nsent = sendmsg(fd, msg, 0);
-
 	(void)alarm(0);
 
 	if (nsent < 0) {
@@ -559,8 +594,8 @@
 	}
 
 	if ((size_t)nsent != n) {
-		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
-		    (long)nsent, (u_long)n);
+		logmsgx("sendmsg_timeout: sendmsg: short send: %zd of %zu "
+		    "bytes", nsent, n);
 		return (-1);
 	}
 
@@ -583,9 +618,7 @@
 	}
 
 	(void)alarm(TIMEOUT);
-
 	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
-
 	(void)alarm(0);
 
 	if (fd < 0) {
@@ -604,7 +637,7 @@
 {
 	ssize_t nread;
 
-	dbgmsg(("receiving %lu bytes", (u_long)n));
+	dbgmsg(("receiving %zu bytes", n));
 
 	if (sigsetjmp(env_alrm, 1) != 0) {
 		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
@@ -612,9 +645,7 @@
 	}
 
 	(void)alarm(TIMEOUT);
-
 	nread = recvmsg(fd, msg, MSG_WAITALL);
-
 	(void)alarm(0);
 
 	if (nread < 0) {
@@ -623,8 +654,8 @@
 	}
 
 	if ((size_t)nread != n) {
-		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
-		    (long)nread, (u_long)n);
+		logmsgx("recvmsg_timeout: recvmsg: short read: %zd of %zu "
+		    "bytes", nread, n);
 		return (-1);
 	}
 
@@ -648,9 +679,7 @@
 	}
 
 	(void)alarm(TIMEOUT);
-
 	nread = read(fd, &buf, 1);
-
 	(void)alarm(0);
 
 	if (nread < 0) {
@@ -659,8 +688,7 @@
 	}
 
 	if (nread != 1) {
-		logmsgx("sync_recv: read: short read: %ld of 1 byte",
-		    (long)nread);
+		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
 		return (-1);
 	}
 
@@ -683,9 +711,7 @@
 	}
 
 	(void)alarm(TIMEOUT);
-
 	nsent = write(fd, "", 1);
-
 	(void)alarm(0);
 
 	if (nsent < 0) {
@@ -694,8 +720,7 @@
 	}
 
 	if (nsent != 1) {
-		logmsgx("sync_send: write: short write: %ld of 1 byte",
-		    (long)nsent);
+		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
 		return (-1);
 	}
 
@@ -712,15 +737,13 @@
 	pid_t pid;
 
 	if (sigsetjmp(env_alrm, 1) != 0) {
-		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
-		    (long)client_pid);
+		logmsgx("wait_client: cannot get exit status of client "
+		    "PID %ld (timeout)", (long)client_pid);
 		return (-1);
 	}
 
-	(void)alarm(TIMEOUT);
-
+	(void)alarm(TIMEOUT * 2);
 	pid = waitpid(client_pid, &status, 0);
-
 	(void)alarm(0);
 
 	if (pid == (pid_t)-1) {
@@ -730,17 +753,19 @@
 
 	if (WIFEXITED(status)) {
 		if (WEXITSTATUS(status) != 0) {
-			logmsgx("wait_client: exit status of client PID %ld is %d",
-			    (long)client_pid, WEXITSTATUS(status));
+			logmsgx("wait_client: exit status of client PID %ld "
+			    "is %d", (long)client_pid, WEXITSTATUS(status));
 			return (-1);
 		}
 	} else {
 		if (WIFSIGNALED(status))
-			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
-			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
+			logmsgx("wait_client: abnormal termination of client "
+			    "PID %ld, signal %d%s", (long)client_pid,
+			    WTERMSIG(status), WCOREDUMP(status) ?
+			    " (core file generated)" : "");
 		else
-			logmsgx("wait_client: termination of client PID %ld, unknown status",
-			    (long)client_pid);
+			logmsgx("wait_client: termination of client PID %ld, "
+			    "unknown status", (long)client_pid);
 		return (-1);
 	}
 
@@ -754,12 +779,12 @@
 static int
 check_groups(const gid_t *gids, int n)
 {
+	int i, j, error;
 	char match[NGROUPS_MAX] = { 0 };
-	int error, i, j;
 
 	if (n != my_ngids - 1) {
-		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
-		    n, my_ngids - 1);
+		logmsgx("wrong number of groups %d != %d (returned from "
+		    "getgroups() - 1)", n, my_ngids - 1);
 		error = -1;
 	} else
 		error = 0;
@@ -782,7 +807,8 @@
 	}
 	for (j = 1; j < my_ngids; ++j)
 		if (match[j] == 0) {
-			logmsgx("did not receive supplementary GID %u", my_gids[j]);
+			logmsgx("did not receive supplementary GID %lu",
+			    (u_long)my_gids[j]);
 			error = -1;
 		}
 	return (error);
@@ -802,16 +828,19 @@
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct cmsghdr *cmptr;
-	int fd;
 	u_int i;
+	int fd, error;
 
 	assert(n == 1 || n == 2);
 
-	if ((fd = create_unbound_socket()) < 0)
+	error = 1;
+
+	fd = create_unbound_socket();
+	if (fd < 0)
 		goto failed;
 
 	if (connect_server(fd) < 0)
-		goto failed_close;
+		goto done;
 
 	iov[0].iov_base = ipc_message;
 	iov[0].iov_len = IPC_MESSAGE_SIZE;
@@ -835,19 +864,15 @@
 		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
-			goto failed_close;
+			goto done;
 	}
 
-	if (close_socket((const char *)NULL, fd) < 0)
-		goto failed;
-
-	_exit(0);
-
-failed_close:
-	(void)close_socket((const char *)NULL, fd);
-
+	error = 0;
+done:
+	if (close_socket((char *)NULL, fd))
+		error = 1;
 failed:
-	_exit(1);
+	_exit(error);
 }
 
 /*
@@ -858,29 +883,28 @@
 static int
 t_cmsgcred_server(int fd1)
 {
-	char buf[IPC_MESSAGE_SIZE];
 	union {
 		struct cmsghdr	cm;
-		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
+		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
+				EXTRA_CMSG_SPACE];
 	} control_un;
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct cmsghdr *cmptr;
-	const struct cmsgcred *cmcredptr;
-	socklen_t controllen;
-	int error, error2, fd2;
+	const struct cmsgcred *cmcred;
 	u_int i;
+	int fd2, error;
+	char buf[IPC_MESSAGE_SIZE];
 
 	if (sock_type == SOCK_STREAM) {
-		if ((fd2 = accept_timeout(fd1)) < 0)
+		fd2 = accept_timeout(fd1);
+		if (fd2 < 0)
 			return (-2);
 	} else
 		fd2 = fd1;
 
 	error = 0;
 
-	controllen = sizeof(control_un.control);
-
 	for (i = 0; i < 2; ++i) {
 		iov[0].iov_base = buf;
 		iov[0].iov_len = sizeof(buf);
@@ -890,29 +914,31 @@
 		msg.msg_iov = iov;
 		msg.msg_iovlen = 1;
 		msg.msg_control = control_un.control;
-		msg.msg_controllen = controllen;
+		msg.msg_controllen = sizeof(control_un.control);
 		msg.msg_flags = 0;
 
-		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
-
-		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
-			goto failed;
+		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
+			error = -2;
+			break;
+		}
 
 		if (msg.msg_flags & MSG_CTRUNC) {
-			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
-			    i);
-			goto next_error;
+			logmsgx("#%u control data was truncated, MSG_CTRUNC "
+			    "flag is on", i);
+			goto next;
 		}
 
 		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
-			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
-			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
-			goto next_error;
+			logmsgx("#%u msg_controllen %u < %zu "
+			    "(sizeof(struct cmsghdr))", i,
+			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
+			goto next;
 		}
 
-		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
+		cmptr = CMSG_FIRSTHDR(&msg);
+		if (cmptr == NULL) {
 			logmsgx("CMSG_FIRSTHDR is NULL");
-			goto next_error;
+			goto next;
 		}
 
 		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
@@ -921,96 +947,90 @@
 		if (cmptr->cmsg_level != SOL_SOCKET) {
 			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 			    cmptr->cmsg_level);
-			goto next_error;
+			goto next;
 		}
 
 		if (cmptr->cmsg_type != SCM_CREDS) {
 			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 			    cmptr->cmsg_type);
-			goto next_error;
+			goto next;
 		}
 
 		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
-			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
-			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
-			goto next_error;
+			logmsgx("#%u cmsg_len %u != %zu "
+			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
+			    (u_int)cmptr->cmsg_len,
+			    CMSG_LEN(sizeof(struct cmsgcred)));
+			goto next;
 		}
 
-		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
+		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
 
-		error2 = 0;
-		if (cmcredptr->cmcred_pid != client_pid) {
+		if (cmcred->cmcred_pid != client_pid) {
 			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
-			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
-			error2 = 1;
+			    i, (long)cmcred->cmcred_pid, (long)client_pid);
+			goto next;
 		}
-		if (cmcredptr->cmcred_uid != my_uid) {
-			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
-			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
-			error2 = 1;
-		}
-		if (cmcredptr->cmcred_euid != my_euid) {
-			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
-			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
-			error2 = 1;
-		}
-		if (cmcredptr->cmcred_gid != my_gid) {
-			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
-			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
-			error2 = 1;
+		if (cmcred->cmcred_uid != my_uid) {
+			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
+			    "process)", i, (u_long)cmcred->cmcred_uid,
+			    (u_long)my_uid);
+			goto next;
+		}
+		if (cmcred->cmcred_euid != my_euid) {
+			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
+			    "process)", i, (u_long)cmcred->cmcred_euid,
+			    (u_long)my_euid);
+			goto next;
+		}
+		if (cmcred->cmcred_gid != my_gid) {
+			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
+			    "process)", i, (u_long)cmcred->cmcred_gid,
+			    (u_long)my_gid);
+			goto next;
 		}
-		if (cmcredptr->cmcred_ngroups == 0) {
+		if (cmcred->cmcred_ngroups == 0) {
 			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
-			error2 = 1;
-		} else {
-			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
-				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
-				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
-				error2 = 1;
-			} else if (cmcredptr->cmcred_ngroups < 0) {
-				logmsgx("#%u cmcred_ngroups %d < 0",
-				    i, cmcredptr->cmcred_ngroups);
-				error2 = 1;
-			} else {
-				dbgmsg(("#%u cmcred_ngroups = %d", i,
-				    cmcredptr->cmcred_ngroups));
-				if (cmcredptr->cmcred_groups[0] != my_egid) {
-					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
-					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
-					error2 = 1;
-				}
-				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
-					logmsgx("#%u cmcred_groups has wrong GIDs", i);
-					error2 = 1;
-				}
-			}
+			goto next;
 		}
-
-		if (error2)
-			goto next_error;
-
-		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
-			logmsgx("#%u control data has extra header", i);
-			goto next_error;
+		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
+			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
+			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
+			goto next;
+		}
+		if (cmcred->cmcred_ngroups < 0) {
+			logmsgx("#%u cmcred_ngroups %d < 0", i,
+			    cmcred->cmcred_ngroups);
+			goto next;
+		}
+
+		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
+		if (cmcred->cmcred_groups[0] != my_egid) {
+			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
+			    "current process)", i,
+			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
+			goto next;
+		}
+		if (check_groups(cmcred->cmcred_groups + 1,
+		    cmcred->cmcred_ngroups - 1) < 0) {
+			logmsgx("#%u cmcred_groups has wrong GIDs", i);
+			goto next;
+		}
+
+		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
+			logmsgx("#%u control data has extra header, this is "
+			    "wrong", i);
+			goto next;
 		}
-
 		continue;
-next_error:
+next:
 		error = -1;
 	}
 
 	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0) {
-			logmsg("close");
-			return (-2);
-		}
+		if (close_socket((char *)NULL, fd2) < 0)
+			error = -2;
 	return (error);
-
-failed:
-	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0)
-			logmsg("close");
-	return (-2);
 }
 
 static int
@@ -1018,45 +1038,35 @@
 {
 	int error, fd;
 
-	if ((fd = create_server_socket()) < 0)
+	fd = create_server_socket();
+	if (fd < 0)
 		return (-2);
 
+	error = -2;
+
 	if (sock_type == SOCK_STREAM)
 		if (listen(fd, LISTENQ) < 0) {
 			logmsg("listen");
-			goto failed;
+			goto done;
 		}
 
-	if ((client_pid = fork()) == (pid_t)-1) {
-		logmsg("fork");
-		goto failed;
-	}
+	if (fork_client() < 0)
+		goto done;
 
 	if (client_pid == 0) {
-		myname = "CLIENT";
-		if (close_socket((const char *)NULL, fd) < 0)
+		if (close_socket((char *)NULL, fd) < 0)
 			_exit(1);
 		t_cmsgcred_client(2);
 	}
 
-	if ((error = t_cmsgcred_server(fd)) == -2) {
-		(void)wait_client();
-		goto failed;
-	}
+	error = t_cmsgcred_server(fd);
 
 	if (wait_client() < 0)
-		goto failed;
-
-	if (close_socket(serv_sock_path, fd) < 0) {
-		logmsgx("close_socket failed");
-		return (-2);
-	}
-	return (error);
-
-failed:
+		error = -2;
+done:
 	if (close_socket(serv_sock_path, fd) < 0)
-		logmsgx("close_socket failed");
-	return (-2);
+		error = -2;
+	return (error);
 }
 
 /*
@@ -1067,20 +1077,23 @@
 {
 	struct msghdr msg;
 	struct iovec iov[1];
-	int fd;
 	u_int i;
+	int fd, error;
 
 	assert(type == 0 || type == 1);
 
-	if ((fd = create_unbound_socket()) < 0)
+	error = 1;
+
+	fd = create_unbound_socket();
+	if (fd < 0)
 		goto failed;
 
 	if (connect_server(fd) < 0)
-		goto failed_close;
+		goto done;
 
 	if (type == 1)
 		if (sync_recv(fd) < 0)
-			goto failed_close;
+			goto done;
 
 	iov[0].iov_base = ipc_message;
 	iov[0].iov_len = IPC_MESSAGE_SIZE;
@@ -1095,18 +1108,14 @@
 
 	for (i = 0; i < 2; ++i)
 		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
-			goto failed_close;
-
-	if (close_socket((const char *)NULL, fd) < 0)
-		goto failed;
-
-	_exit(0);
-
-failed_close:
-	(void)close_socket((const char *)NULL, fd);
+			goto done;
 
+	error = 0;
+done:
+	if (close_socket((char *)NULL, fd) < 0)
+		error = 1;
 failed:
-	_exit(1);
+	_exit(error);
 }
 
 /*
@@ -1122,33 +1131,37 @@
 	char buf[IPC_MESSAGE_SIZE];
 	union {
 		struct cmsghdr	cm;
-		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
+		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
+				EXTRA_CMSG_SPACE];
 	} control_un;
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct cmsghdr *cmptr;
 	const struct sockcred *sockcred;
-	int error, error2, fd2, optval;
 	u_int i;
+	int fd2, error, optval;
 
 	assert(n == 1 || n == 2);
 	assert(type == 0 || type == 1);
 
+	error = -2;
+
 	if (sock_type == SOCK_STREAM) {
-		if ((fd2 = accept_timeout(fd1)) < 0)
+		fd2 = accept_timeout(fd1);
+		if (fd2 < 0)
 			return (-2);
 		if (type == 1) {
 			optval = 1;
-			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
-				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
-				if (errno == ENOPROTOOPT) {
+			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
+			    sizeof(optval)) < 0) {
+				logmsg("setsockopt(LOCAL_CREDS) for accepted "
+				    "socket");
+				if (errno == ENOPROTOOPT)
 					error = -1;
-					goto done_close;
-				}
-				goto failed;
+				goto done;
 			}
 			if (sync_send(fd2) < 0)
-				goto failed;
+				goto done;
 		}
 	} else
 		fd2 = fd1;
@@ -1157,132 +1170,131 @@
 
 	for (i = 0; i < n; ++i) {
 		iov[0].iov_base = buf;
-		iov[0].iov_len = sizeof buf;
+		iov[0].iov_len = sizeof(buf);
 
 		msg.msg_name = NULL;
 		msg.msg_namelen = 0;
 		msg.msg_iov = iov;
 		msg.msg_iovlen = 1;
 		msg.msg_control = control_un.control;
-		msg.msg_controllen = sizeof control_un.control;
+		msg.msg_controllen = sizeof(control_un.control);
 		msg.msg_flags = 0;
 
-		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
-			goto failed;
+		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
+			error = -2;
+			break;
+		}
 
 		if (msg.msg_flags & MSG_CTRUNC) {
-			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
-			goto next_error;
+			logmsgx("control data was truncated, MSG_CTRUNC flag "
+			    "is on");
+			goto next;
 		}
 
+		dbgmsg(("#%u msg_controllen = %u", i,
+		    (u_int)msg.msg_controllen));
+
 		if (i != 0 && sock_type == SOCK_STREAM) {
 			if (msg.msg_controllen != 0) {
-				logmsgx("second message has control data, this is wrong for stream sockets");
-				goto next_error;
+				logmsgx("second message has control data, "
+				    "this is wrong for stream sockets");
+				goto next;
 			}
-			dbgmsg(("#%u msg_controllen = %u", i,
-			    (u_int)msg.msg_controllen));
 			continue;
 		}
 
 		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
-			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
-			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
-			goto next_error;
+			logmsgx("#%u msg_controllen %u < %zu "
+			    "(sizeof(struct cmsghdr))", i,
+			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
+			goto next;
 		}
 
-		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
+		cmptr = CMSG_FIRSTHDR(&msg);
+		if (cmptr == NULL) {
 			logmsgx("CMSG_FIRSTHDR is NULL");
-			goto next_error;
+			goto next;
 		}
 
-		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
-		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
+		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
 
 		if (cmptr->cmsg_level != SOL_SOCKET) {
 			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 			    cmptr->cmsg_level);
-			goto next_error;
+			goto next;
 		}
 
 		if (cmptr->cmsg_type != SCM_CREDS) {
 			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 			    cmptr->cmsg_type);
-			goto next_error;
+			goto next;
 		}
 
 		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
-			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
-			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
-			goto next_error;
+			logmsgx("#%u cmsg_len %u != %zu "
+			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
+			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
+			goto next;
 		}
 
-		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
+		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
 
-		error2 = 0;
 		if (sockcred->sc_uid != my_uid) {
-			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
-			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
-			error2 = 1;
+			logmsgx("#%u sc_uid %lu != %lu (UID of current "
+			    "process)", i, (u_long)sockcred->sc_uid,
+			    (u_long)my_uid);
+			goto next;
 		}
 		if (sockcred->sc_euid != my_euid) {
-			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
-			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
-			error2 = 1;
+			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
+			    "process)", i, (u_long)sockcred->sc_euid,
+			    (u_long)my_euid);
+			goto next;
 		}
 		if (sockcred->sc_gid != my_gid) {
-			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
-			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
-			error2 = 1;
+			logmsgx("#%u sc_gid %lu != %lu (GID of current "
+			    "process)", i, (u_long)sockcred->sc_gid,
+			    (u_long)my_gid);
+			goto next;
 		}
 		if (sockcred->sc_egid != my_egid) {
-			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
-			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
-			error2 = 1;
+			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
+			    "process)", i, (u_long)sockcred->sc_gid,
+			    (u_long)my_egid);
+			goto next;
 		}
 		if (sockcred->sc_ngroups > NGROUPS_MAX) {
 			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
 			    i, sockcred->sc_ngroups, NGROUPS_MAX);
-			error2 = 1;
-		} else if (sockcred->sc_ngroups < 0) {
-			logmsgx("#%u sc_ngroups %d < 0",
-			    i, sockcred->sc_ngroups);
-			error2 = 1;
-		} else {
-			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
-			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
-				logmsgx("#%u sc_groups has wrong GIDs", i);
-				error2 = 1;
-			}
+			goto next;
+		}
+		if (sockcred->sc_ngroups < 0) {
+			logmsgx("#%u sc_ngroups %d < 0", i,
+			    sockcred->sc_ngroups);
+			goto next;
 		}
 
-		if (error2)
-			goto next_error;
-
-		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
-			logmsgx("#%u control data has extra header, this is wrong",
-			    i);
-			goto next_error;
+		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
+		if (check_groups(sockcred->sc_groups,
+		    sockcred->sc_ngroups) < 0) {
+			logmsgx("#%u sc_groups has wrong GIDs", i);
+			goto next;
 		}
 
+		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
+			logmsgx("#%u control data has extra header, this is "
+			    "wrong", i);
+			goto next;
+		}
 		continue;
-next_error:
+next:
 		error = -1;
 	}
-
-done_close:
+done:
 	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0) {
-			logmsg("close");
-			return (-2);
-		}
+		if (close_socket((char *)NULL, fd2) < 0)
+			error = -2;
 	return (error);
-
-failed:
-	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0)
-			logmsg("close");
-	return (-2);
 }
 
 static int
@@ -1292,59 +1304,47 @@
 
 	assert(type == 0 || type == 1);
 
-	if ((fd = create_server_socket()) < 0)
+	fd = create_server_socket();
+	if (fd < 0)
 		return (-2);
 
+	error = -2;
+
 	if (sock_type == SOCK_STREAM)
 		if (listen(fd, LISTENQ) < 0) {
 			logmsg("listen");
-			goto failed;
+			goto done;
 		}
 
 	if (type == 0) {
 		optval = 1;
-		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
+		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
+		    sizeof(optval)) < 0) {
 			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
-			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
-			if (errno == ENOPROTOOPT) {
+			    sock_type_str);
+			if (errno == ENOPROTOOPT)
 				error = -1;
-				goto done_close;
-			}
-			goto failed;
+			goto done;
 		}
 	}
 
-	if ((client_pid = fork()) == (pid_t)-1) {
-		logmsg("fork");
-		goto failed;
-	}
+	if (fork_client() < 0)
+		goto done;
 
 	if (client_pid == 0) {
-		myname = "CLIENT";
-		if (close_socket((const char *)NULL, fd) < 0)
+		if (close_socket((char *)NULL, fd) < 0)
 			_exit(1);
 		t_sockcred_client(type);
 	}
 
-	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
-		(void)wait_client();
-		goto failed;
-	}
+	error = t_sockcred_server(type, fd, 2);
 
 	if (wait_client() < 0)
-		goto failed;
-
-done_close:
-	if (close_socket(serv_sock_path, fd) < 0) {
-		logmsgx("close_socket failed");
-		return (-2);
-	}
-	return (error);
-
-failed:
+		error = -2;
+done:
 	if (close_socket(serv_sock_path, fd) < 0)
-		logmsgx("close_socket failed");
-	return (-2);
+		error = -2;
+	return (error);
 }
 
 static int
@@ -1368,59 +1368,45 @@
 static int
 t_cmsgcred_sockcred(void)
 {
-	int error, fd, optval;
+	int fd, error, optval;
 
-	if ((fd = create_server_socket()) < 0)
+	fd = create_server_socket();
+	if (fd < 0)
 		return (-2);
 
+	error = -2;
+
 	if (sock_type == SOCK_STREAM)
 		if (listen(fd, LISTENQ) < 0) {
 			logmsg("listen");
-			goto failed;
+			goto done;
 		}
 
 	optval = 1;
-	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
-		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
-		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
-		if (errno == ENOPROTOOPT) {
+	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
+		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
+		if (errno == ENOPROTOOPT)
 			error = -1;
-			goto done_close;
-		}
-		goto failed;
+		goto done;
 	}
 
-	if ((client_pid = fork()) == (pid_t)-1) {
-		logmsg("fork");
-		goto failed;
-	}
+	if (fork_client() < 0)
+		goto done;
 
 	if (client_pid == 0) {
-		myname = "CLIENT";
-		if (close_socket((const char *)NULL, fd) < 0)
+		if (close_socket((char *)NULL, fd) < 0)
 			_exit(1);
 		t_cmsgcred_client(1);
 	}
 
-	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
-		(void)wait_client();
-		goto failed;
-	}
+	error = t_sockcred_server(0, fd, 1);
 
 	if (wait_client() < 0)
-		goto failed;
-
-done_close:
-	if (close_socket(serv_sock_path, fd) < 0) {
-		logmsgx("close_socket failed");
-		return (-2);
-	}
-	return (error);
-
-failed:
+		error = -2;
+done:
 	if (close_socket(serv_sock_path, fd) < 0)
-		logmsgx("close_socket failed");
-	return (-2);
+		error = -2;
+	return (error);
 }
 
 /*
@@ -1437,13 +1423,16 @@
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct cmsghdr *cmptr;
-	int fd;
+	int fd, error;
 
-	if ((fd = create_unbound_socket()) < 0)
+	error = 1;
+
+	fd = create_unbound_socket();
+	if (fd < 0)
 		goto failed;
 
 	if (connect_server(fd) < 0)
-		goto failed_close;
+		goto done;
 
 	iov[0].iov_base = ipc_message;
 	iov[0].iov_len = IPC_MESSAGE_SIZE;
@@ -1454,7 +1443,7 @@
 	msg.msg_iovlen = 1;
 	msg.msg_control = control_un.control;
 	msg.msg_controllen = no_control_data ?
-	    sizeof(struct cmsghdr) :sizeof control_un.control;
+	    sizeof(struct cmsghdr) : sizeof(control_un.control);
 	msg.msg_flags = 0;
 
 	cmptr = CMSG_FIRSTHDR(&msg);
@@ -1467,18 +1456,14 @@
 	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 
 	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
-		goto failed_close;
-
-	if (close_socket((const char *)NULL, fd) < 0)
-		goto failed;
-
-	_exit(0);
-
-failed_close:
-	(void)close_socket((const char *)NULL, fd);
+		goto done;
 
+	error = 0;
+done:
+	if (close_socket((char *)NULL, fd) < 0)
+		error = 1;
 failed:
-	_exit(1);
+	_exit(error);
 }
 
 /*
@@ -1490,34 +1475,38 @@
 {
 	union {
 		struct cmsghdr	cm;
-		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
+		char	control[CMSG_SPACE(sizeof(struct timeval)) +
+				EXTRA_CMSG_SPACE];
 	} control_un;
-	char buf[IPC_MESSAGE_SIZE];
-	int error, fd2;
 	struct msghdr msg;
 	struct iovec iov[1];
 	struct cmsghdr *cmptr;
 	const struct timeval *timeval;
+	int error, fd2;
+	char buf[IPC_MESSAGE_SIZE];
 
 	if (sock_type == SOCK_STREAM) {
-		if ((fd2 = accept_timeout(fd1)) < 0)
+		fd2 = accept_timeout(fd1);
+		if (fd2 < 0)
 			return (-2);
 	} else
 		fd2 = fd1;
 
 	iov[0].iov_base = buf;
-	iov[0].iov_len = sizeof buf;
+	iov[0].iov_len = sizeof(buf);
 
 	msg.msg_name = NULL;
 	msg.msg_namelen = 0;
 	msg.msg_iov = iov;
 	msg.msg_iovlen = 1;
 	msg.msg_control = control_un.control;
-	msg.msg_controllen = sizeof control_un.control;;
+	msg.msg_controllen = sizeof(control_un.control);
 	msg.msg_flags = 0;
 
-	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
-		goto failed;
+	if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
+		error = -2;
+		goto done;
+	}
 
 	error = -1;
 
@@ -1527,12 +1516,13 @@
 	}
 
 	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
-		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
-		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
+		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
+		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 		goto done;
 	}
 
-	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
+	cmptr = CMSG_FIRSTHDR(&msg);
+	if (cmptr == NULL) {
 		logmsgx("CMSG_FIRSTHDR is NULL");
 		goto done;
 	}
@@ -1551,36 +1541,27 @@
 	}
 
 	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
-		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
-		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
+		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
+		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
 		goto done;
 	}
 
-	timeval = (const struct timeval *)CMSG_DATA(cmptr);
+	timeval = (struct timeval *)CMSG_DATA(cmptr);
 
 	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
 
-	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
-		logmsgx("control data has extra header");
+	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
+		logmsgx("control data has extra header, this is wrong");
 		goto done;
 	}
 
 	error = 0;
-
 done:
 	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0) {
-			logmsg("close");
-			return (-2);
-		}
+		if (close_socket((char *)NULL, fd2) < 0)
+			error = -2;
 	return (error);
-
-failed:
-	if (sock_type == SOCK_STREAM)
-		if (close(fd2) < 0)
-			logmsg("close");
-	return (-2);
 }
 
 static int
@@ -1588,43 +1569,174 @@
 {
 	int error, fd;
 
-	if ((fd = create_server_socket()) < 0)
+	fd = create_server_socket();
+	if (fd < 0)
 		return (-2);
 
+	error = -2;
+
 	if (sock_type == SOCK_STREAM)
 		if (listen(fd, LISTENQ) < 0) {
 			logmsg("listen");
-			goto failed;
+			goto done;
 		}
 
-	if ((client_pid = fork()) == (pid_t)-1) {
-		logmsg("fork");
-		goto failed;
-	}
+	if (fork_client() < 0)
+		goto done;
 
 	if (client_pid == 0) {
-		myname = "CLIENT";
-		if (close_socket((const char *)NULL, fd) < 0)
+		if (close_socket((char *)NULL, fd) < 0)
 			_exit(1);
 		t_timestamp_client();
 	}
 
-	if ((error = t_timestamp_server(fd)) == -2) {
-		(void)wait_client();
-		goto failed;
-	}
+	error = t_timestamp_server(fd);
 
 	if (wait_client() < 0)
+		error = -2;
+done:
+	if (close_socket(serv_sock_path, fd) < 0)
+		error = -2;
+	return (error);
+}
+
+/*
+ * Verify that xucred structure has correct credentials.
+ */
+static int
+check_xucred(const struct xucred *xuc)
+{
+	if (xuc->cr_version != XUCRED_VERSION) {
+		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
+		    xuc->cr_version, XUCRED_VERSION);
+		return (-1);
+	}
+	if (xuc->cr_uid != my_euid) {
+		logmsgx("cr_uid %lu != %lu (EUID of current process)",
+		   (u_long)xuc->cr_uid, (u_long)my_euid);
+		return (-1);
+	}
+	if (xuc->cr_ngroups == 0) {
+		logmsgx("cr_ngroups = 0, this is wrong");
+		return (-1);
+	}
+	if (xuc->cr_ngroups > NGROUPS) {
+		logmsgx("cr_ngroups %hu > %d (NGROUPS)",
+		    xuc->cr_ngroups, NGROUPS);
+		return (-1);
+	}
+	if (xuc->cr_groups[0] != my_egid) {
+		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
+		    (u_long)xuc->cr_groups[0], (u_long)my_egid);
+		return (-1);
+	}
+	if (check_groups(xuc->cr_groups + 1, xuc->cr_ngroups - 1) < 0) {
+		logmsgx("cr_groups has wrong GIDs");
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Connect to server and set LOCAL_PEERCRED socket option for connected
+ * socket.  Verify that credentials of the peer are correct.
+ */
+static void
+t_peercred_client(void)
+{
+	struct xucred xuc;
+	socklen_t len;
+	int fd, error;
+
+	error = 1;
+
+	fd = create_unbound_socket();
+	if (fd < 0)
 		goto failed;
 
-	if (close_socket(serv_sock_path, fd) < 0) {
-		logmsgx("close_socket failed");
+	if (connect_server(fd) < 0)
+		goto done;
+
+	len = sizeof(xuc);
+	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xuc, &len) < 0) {
+		logmsg("getsockopt(LOCAL_PEERCRED)");
+		goto done;
+	}
+
+	if (check_xucred(&xuc) < 0)
+		goto done;
+done:
+	error = close_socket((char *)NULL, fd) < 0 ? 1 : 0;
+failed:
+	_exit(error);
+}
+
+/*
+ * Accept connection from client and set LOCAL_PEERCRED socket option for
+ * connected socket.  Verify that credentials of the peer are correct.
+ */
+static int
+t_peercred_server(int fd1)
+{
+	struct xucred xuc;
+	socklen_t len;
+	int fd2, error;
+
+	fd2 = accept_timeout(fd1);
+	if (fd2 < 0)
 		return (-2);
+
+	len = sizeof(xuc);
+	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xuc, &len) < 0) {
+		logmsg("getsockopt(LOCAL_PEERCRED)");
+		error = -2;
+		goto done;
 	}
+
+	if (check_xucred(&xuc) < 0) {
+		error = -1;
+		goto done;
+	}
+
+	error = 0;
+done:
+	if (close_socket((char *)NULL, fd2) < 0)
+		error = -2;
 	return (error);
+}
 
-failed:
+static int
+t_peercred(void)
+{
+	int fd, error;
+
+	fd = create_server_socket();
+	if (fd < 0)
+		return (-2);
+
+	error = -2;
+
+	if (sock_type == SOCK_STREAM)
+		if (listen(fd, LISTENQ) < 0) {
+			logmsg("listen");
+			goto done;
+		}
+
+	if (fork_client() < 0)
+		goto done;
+
+	if (client_pid == 0) {
+		if (close_socket((char *)NULL, fd) < 0)
+			_exit(1);
+		t_peercred_client();
+	}
+
+	error = t_peercred_server(fd);
+
+	if (wait_client() < 0)
+		error = -2;
+done:
 	if (close_socket(serv_sock_path, fd) < 0)
-		logmsgx("close_socket failed");
-	return (-2);
+		error = -2;
+	return (error);
 }
diff -ruN unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
--- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
+++ unix_cmsg/unix_cmsg.t	2009-01-30 15:44:49.000000000 +0200
@@ -23,14 +23,15 @@
 	done
 }
 
-echo "1..15"
+echo "1..16"
 
 for desc in \
 	"Sending, receiving cmsgcred" \
 	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 	"Sending cmsgcred, receiving sockcred # TODO" \
-	"Sending, receiving timestamp"
+	"Sending, receiving timestamp" \
+	"Check LOCAL_PEERCRED socket option"
 do
 	n=`expr ${n} + 1`
 	run ${n} stream "" ${n} "STREAM ${desc}"
@@ -48,10 +49,10 @@
 	run ${n} dgram "" ${i} "DGRAM ${desc}"
 done
 
-run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
-run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
-run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
+run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
+run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
+run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 
-run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
-run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
-run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
+run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
+run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
+run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
>Release-Note:
>Audit-Trail:

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Fri, 13 Feb 2009 12:57:23 +0200

 Updated patch: use getprogname(3) instead of __progname.
 
 diff -ruN unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/README	2009-02-03 17:41:42.000000000 +0200
 @@ -1,7 +1,7 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
  
  About unix_cmsg
 -================
 +===============
  
  This program is a collection of regression tests for ancillary (control)
  data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 @@ -13,8 +13,8 @@
  messages to Server.
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
  
  Available options
  =================
 @@ -24,7 +24,7 @@
  
  -h	Output help message and exit.
  
 --t <socktype>
 +-t socktype
  	Run tests only for the given socket type: "stream" or "dgram".
  	With this option it is possible to run only particular test,
  	not all of them.
 @@ -90,6 +90,13 @@
      message with data and control message with SCM_TIMESTAMP type
      followed by struct timeval{}.
  
 + 6: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use control data for PF_LOCAL sockets, but can be
 +    implemented here.  Client connects to Server.  Both Client and Server
 +    verify that credentials of the peer are correct using LOCAL_PEERCRED
 +    socket option.
 +
  For SOCK_DGRAM sockets:
  ----------------------
  
 @@ -110,7 +117,7 @@
      structure should contain correct information.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 +
      Server creates datagram socket and set socket option LOCAL_CREDS
      for it.  Client sends one message with data and control message with
      SOCK_CREDS type to Server.  Server should receive one message with
 diff -ruN unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
 +++ unix_cmsg/unix_cmsg.c	2009-02-13 12:47:39.000000000 +0200
 @@ -27,10 +27,11 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 @@ -68,7 +69,7 @@
   *
   * Each function which can block, is run under TIMEOUT, if timeout
   * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * with non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,14 +77,14 @@
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	5
  #endif
  
  #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
  
  static int	t_cmsgcred(void), t_sockcred_stream1(void);
  static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_sockcred_dgram(void), t_timestamp(void), t_peercred(void);
  
  struct test_func {
  	int	(*func)(void);	/* Pointer to function.	*/
 @@ -91,31 +92,57 @@
  };
  
  static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_stream1,
 +	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_sockcred_stream2,
 +	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	},
 +	{ .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
 +
  static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_dgram,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 +static const char *myname;		/* "SERVER" or "CLIENT" */
  
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 +static int	debug = 0;		/* -d */
 +static int	no_control_data = 0;	/* -z */
  
  static u_int	nfailed = 0;		/* Number of failed tests. */
  
 @@ -156,32 +183,29 @@
  static void	logmsgx(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
  /*
   * Output the help message (-h switch).
   */
  static void
  usage(int quick)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 +	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 +	    getprogname());
  	if (quick)
  		return;
  	fprintf(stderr, "\n Options are:\n\
    -d\t\t\tOutput debugging information\n\
    -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 +  -t socktype\t\tRun test only for socket type: stream or dgram\n\
    -z\t\t\tDo not send real control data if possible\n\n");
  	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
  	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
  /*
 @@ -210,8 +234,7 @@
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
  		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 @@ -220,8 +243,7 @@
  	else
  		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
  	va_end(ap);
 -
 -	errno = errno_save;		/* Restore errno. */
 +	errno = errno_save;
  }
  
  /*
 @@ -241,22 +263,35 @@
  }
  
  /*
 - * Run tests from testno1 to testno2.
 + * Run tests for the given socket type.
   */
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, nfailed1, testno2;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 +	output("Running tests for %s sockets:\n", sock_type_str);
  	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
  			++nfailed1;
  			break;
 @@ -300,22 +335,35 @@
  	struct sigaction sa;
  
  	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
  	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 +	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0)
  		err(EX_OSERR, "sigaction(SIGPIPE)");
  
  	sa.sa_handler = sig_alrm;
 +	sigemptyset(&sa.sa_mask);
  	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
  		err(EX_OSERR, "sigaction(SIGALRM)");
  }
  
 +static int
 +fork_client(void)
 +{
 +	client_pid = fork();
 +	if (client_pid == 0)
 +		myname = "CLIENT";
 +	else if (client_pid == (pid_t)-1) {
 +		logmsg("fork");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 +	u_int testno;
 +	int opt, rv, dgramflag, streamflag;
  
  	dgramflag = streamflag = 0;
  	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 @@ -332,7 +380,7 @@
  			else if (strcmp(optarg, "dgram") == 0)
  				dgramflag = 1;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EX_USAGE, "option -t: wrong socket type");
  			break;
  		case 'z':
  			no_control_data = 1;
 @@ -346,38 +394,40 @@
  	if (optind < argc) {
  		if (optind + 1 != argc)
  			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
  			errx(EX_USAGE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 +		testno = 0;
  
  	if (dgramflag == 0 && streamflag == 0)
  		dgramflag = streamflag = 1;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (dgramflag && streamflag && testno != 0)
 +		errx(EX_USAGE, "you can use particular test, only with "
 +		    "datagram or stream sockets");
  
  	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EX_USAGE, "given test %u for stream sockets "
 +			    "does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EX_USAGE, "given test %u for datagram sockets "
 +			    "does not exist", testno);
  	}
  
  	my_uid = getuid();
  	my_euid = geteuid();
  	my_gid = getgid();
  	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 +	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
 +	switch (my_ngids) {
  	case -1:
  		err(EX_SOFTWARE, "getgroups");
  		/* NOTREACHED */
  	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		errx(EX_OSERR, "getgroups returned no groups");
  	}
  
  	sig_init();
 @@ -385,42 +435,21 @@
  	if (mkdtemp(tempdir) == NULL)
  		err(EX_OSERR, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 -
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	myname = "SERVER";
 +	rv = EX_OK;
 +	if (streamflag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EX_OSERR;
 +	if (dgramflag && rv == EX_OK)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EX_OSERR;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EX_OSERR;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 -
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +	return (nfailed ? EX_OSERR : rv);
  }
  
  /*
 @@ -433,27 +462,30 @@
  {
  	int rv, fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
  		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
  		return (-1);
  	}
  
  	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 +		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
 +		    myname);
 +		if (rv < 0) {
  			logmsg("create_socket: snprintf failed");
  			goto failed;
  		}
  		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 +			logmsgx("create_socket: too long path name for given "
 +			    "buffer");
  			goto failed;
  		}
  
  		memset(addr, 0, sizeof(addr));
  		addr->sun_family = AF_LOCAL;
  		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +			logmsgx("create_socket: too long path name (>= %zu) "
 +			    "for local domain socket", sizeof(addr->sun_path));
  			goto failed;
  		}
  		strcpy(addr->sun_path, sock_path);
 @@ -479,7 +511,8 @@
  static int
  create_server_socket(void)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 +	return (create_socket(serv_sock_path, sizeof(serv_sock_path),
 +	    &servaddr));
  }
  
  /*
 @@ -498,12 +531,13 @@
  static int
  close_socket(const char *sock_path, int fd)
  {
 -	int error = 0;
 +	int error;
  
  	if (close(fd) < 0) {
  		logmsg("close_socket: close");
  		error = -1;
 -	}
 +	} else
 +		error = 0;
  	if (sock_path != NULL)
  		if (unlink(sock_path) < 0) {
  			logmsg("close_socket: unlink(%s)", sock_path);
 @@ -540,17 +574,16 @@
  {
  	ssize_t nsent;
  
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 +	dbgmsg(("sending %zu bytes", n));
  
  	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 +		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)",
 +		    serv_sock_path);
  		return (-1);
  	}
  
  	(void)alarm(TIMEOUT);
 -
  	nsent = sendmsg(fd, msg, 0);
 -
  	(void)alarm(0);
  
  	if (nsent < 0) {
 @@ -559,8 +592,8 @@
  	}
  
  	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +		logmsgx("sendmsg_timeout: sendmsg: short send: %zd of %zu "
 +		    "bytes", nsent, n);
  		return (-1);
  	}
  
 @@ -583,9 +616,7 @@
  	}
  
  	(void)alarm(TIMEOUT);
 -
  	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 -
  	(void)alarm(0);
  
  	if (fd < 0) {
 @@ -604,7 +635,7 @@
  {
  	ssize_t nread;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 +	dbgmsg(("receiving %zu bytes", n));
  
  	if (sigsetjmp(env_alrm, 1) != 0) {
  		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 @@ -612,9 +643,7 @@
  	}
  
  	(void)alarm(TIMEOUT);
 -
  	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
  	(void)alarm(0);
  
  	if (nread < 0) {
 @@ -623,8 +652,8 @@
  	}
  
  	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +		logmsgx("recvmsg_timeout: recvmsg: short read: %zd of %zu "
 +		    "bytes", nread, n);
  		return (-1);
  	}
  
 @@ -648,9 +677,7 @@
  	}
  
  	(void)alarm(TIMEOUT);
 -
  	nread = read(fd, &buf, 1);
 -
  	(void)alarm(0);
  
  	if (nread < 0) {
 @@ -659,8 +686,7 @@
  	}
  
  	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
  		return (-1);
  	}
  
 @@ -683,9 +709,7 @@
  	}
  
  	(void)alarm(TIMEOUT);
 -
  	nsent = write(fd, "", 1);
 -
  	(void)alarm(0);
  
  	if (nsent < 0) {
 @@ -694,8 +718,7 @@
  	}
  
  	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
  		return (-1);
  	}
  
 @@ -712,15 +735,13 @@
  	pid_t pid;
  
  	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 +		logmsgx("wait_client: cannot get exit status of client "
 +		    "PID %ld (timeout)", (long)client_pid);
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 -
 +	(void)alarm(TIMEOUT * 2);
  	pid = waitpid(client_pid, &status, 0);
 -
  	(void)alarm(0);
  
  	if (pid == (pid_t)-1) {
 @@ -730,17 +751,19 @@
  
  	if (WIFEXITED(status)) {
  		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 +			logmsgx("wait_client: exit status of client PID %ld "
 +			    "is %d", (long)client_pid, WEXITSTATUS(status));
  			return (-1);
  		}
  	} else {
  		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 +			logmsgx("wait_client: abnormal termination of client "
 +			    "PID %ld, signal %d%s", (long)client_pid,
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
  		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +			logmsgx("wait_client: termination of client PID %ld, "
 +			    "unknown status", (long)client_pid);
  		return (-1);
  	}
  
 @@ -754,12 +777,12 @@
  static int
  check_groups(const gid_t *gids, int n)
  {
 +	int i, j, error;
  	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
  
  	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 +		logmsgx("wrong number of groups %d != %d (returned from "
 +		    "getgroups() - 1)", n, my_ngids - 1);
  		error = -1;
  	} else
  		error = 0;
 @@ -782,7 +805,8 @@
  	}
  	for (j = 1; j < my_ngids; ++j)
  		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 +			logmsgx("did not receive supplementary GID %lu",
 +			    (u_long)my_gids[j]);
  			error = -1;
  		}
  	return (error);
 @@ -802,16 +826,19 @@
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
  	u_int i;
 +	int fd, error;
  
  	assert(n == 1 || n == 2);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	error = 1;
 +
 +	fd = create_unbound_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -835,19 +862,15 @@
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
  		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +			goto done;
  	}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 -
 +	error = 0;
 +done:
 +	if (close_socket((char *)NULL, fd))
 +		error = 1;
  failed:
 -	_exit(1);
 +	_exit(error);
  }
  
  /*
 @@ -858,29 +881,28 @@
  static int
  t_cmsgcred_server(int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 +	const struct cmsgcred *cmcred;
  	u_int i;
 +	int fd2, error;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_timeout(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	error = 0;
  
 -	controllen = sizeof(control_un.control);
 -
  	for (i = 0; i < 2; ++i) {
  		iov[0].iov_base = buf;
  		iov[0].iov_len = sizeof(buf);
 @@ -890,29 +912,31 @@
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
 +			error = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 +			logmsgx("#%u control data was truncated, MSG_CTRUNC "
 +			    "flag is on", i);
 +			goto next;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 @@ -921,96 +945,90 @@
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
 +			    (u_int)cmptr->cmsg_len,
 +			    CMSG_LEN(sizeof(struct cmsgcred)));
 +			goto next;
  		}
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 +		if (cmcred->cmcred_pid != client_pid) {
  			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 +			    i, (long)cmcred->cmcred_pid, (long)client_pid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 +		if (cmcred->cmcred_uid != my_uid) {
 +			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_uid,
 +			    (u_long)my_uid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_euid != my_euid) {
 +			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_euid,
 +			    (u_long)my_euid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_gid != my_gid) {
 +			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 +		if (cmcred->cmcred_ngroups == 0) {
  			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 +			goto next;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 +		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
 +			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 +			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_ngroups < 0) {
 +			logmsgx("#%u cmcred_ngroups %d < 0", i,
 +			    cmcred->cmcred_ngroups);
 +			goto next;
 +		}
 +
 +		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
 +		if (cmcred->cmcred_groups[0] != my_egid) {
 +			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
 +			    "current process)", i,
 +			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
 +			goto next;
 +		}
 +		if (check_groups(cmcred->cmcred_groups + 1,
 +		    cmcred->cmcred_ngroups - 1) < 0) {
 +			logmsgx("#%u cmcred_groups has wrong GIDs", i);
 +			goto next;
 +		}
 +
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, this is "
 +			    "wrong", i);
 +			goto next;
  		}
 -
  		continue;
 -next_error:
 +next:
  		error = -1;
  	}
  
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			error = -2;
  	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
  }
  
  static int
 @@ -1018,45 +1036,35 @@
  {
  	int error, fd;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 +	error = -2;
 +
  	if (sock_type == SOCK_STREAM)
  		if (listen(fd, LISTENQ) < 0) {
  			logmsg("listen");
 -			goto failed;
 +			goto done;
  		}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(2);
  	}
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	error = t_cmsgcred_server(fd);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		error = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		error = -2;
 +	return (error);
  }
  
  /*
 @@ -1067,20 +1075,23 @@
  {
  	struct msghdr msg;
  	struct iovec iov[1];
 -	int fd;
  	u_int i;
 +	int fd, error;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	error = 1;
 +
 +	fd = create_unbound_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	if (type == 1)
  		if (sync_recv(fd) < 0)
 -			goto failed_close;
 +			goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1095,18 +1106,14 @@
  
  	for (i = 0; i < 2; ++i)
  		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +			goto done;
  
 +	error = 0;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		error = 1;
  failed:
 -	_exit(1);
 +	_exit(error);
  }
  
  /*
 @@ -1122,33 +1129,37 @@
  	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
  	u_int i;
 +	int fd2, error, optval;
  
  	assert(n == 1 || n == 2);
  	assert(type == 0 || type == 1);
  
 +	error = -2;
 +
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_timeout(fd1);
 +		if (fd2 < 0)
  			return (-2);
  		if (type == 1) {
  			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 +			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
 +			    sizeof(optval)) < 0) {
 +				logmsg("setsockopt(LOCAL_CREDS) for accepted "
 +				    "socket");
 +				if (errno == ENOPROTOOPT)
  					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 +				goto done;
  			}
  			if (sync_send(fd2) < 0)
 -				goto failed;
 +				goto done;
  		}
  	} else
  		fd2 = fd1;
 @@ -1157,132 +1168,131 @@
  
  	for (i = 0; i < n; ++i) {
  		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 +		iov[0].iov_len = sizeof(buf);
  
  		msg.msg_name = NULL;
  		msg.msg_namelen = 0;
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
 +			error = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +			logmsgx("control data was truncated, MSG_CTRUNC flag "
 +			    "is on");
 +			goto next;
  		}
  
 +		dbgmsg(("#%u msg_controllen = %u", i,
 +		    (u_int)msg.msg_controllen));
 +
  		if (i != 0 && sock_type == SOCK_STREAM) {
  			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 +				logmsgx("second message has control data, "
 +				    "this is wrong for stream sockets");
 +				goto next;
  			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
  			continue;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
  
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
 +			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
 +			goto next;
  		}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
  		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 +			logmsgx("#%u sc_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)sockcred->sc_uid,
 +			    (u_long)my_uid);
 +			goto next;
  		}
  		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 +			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)sockcred->sc_euid,
 +			    (u_long)my_euid);
 +			goto next;
  		}
  		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 +			logmsgx("#%u sc_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
  		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 +			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_egid);
 +			goto next;
  		}
  		if (sockcred->sc_ngroups > NGROUPS_MAX) {
  			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
  			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 -			}
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups < 0) {
 +			logmsgx("#%u sc_ngroups %d < 0", i,
 +			    sockcred->sc_ngroups);
 +			goto next;
  		}
  
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 +		if (check_groups(sockcred->sc_groups,
 +		    sockcred->sc_ngroups) < 0) {
 +			logmsgx("#%u sc_groups has wrong GIDs", i);
 +			goto next;
  		}
  
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, this is "
 +			    "wrong", i);
 +			goto next;
 +		}
  		continue;
 -next_error:
 +next:
  		error = -1;
  	}
 -
 -done_close:
 +done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			error = -2;
  	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
  }
  
  static int
 @@ -1292,59 +1302,47 @@
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 +	error = -2;
 +
  	if (sock_type == SOCK_STREAM)
  		if (listen(fd, LISTENQ) < 0) {
  			logmsg("listen");
 -			goto failed;
 +			goto done;
  		}
  
  	if (type == 0) {
  		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 +		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
 +		    sizeof(optval)) < 0) {
  			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 +			    sock_type_str);
 +			if (errno == ENOPROTOOPT)
  				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 +			goto done;
  		}
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_sockcred_client(type);
  	}
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	error = t_sockcred_server(type, fd, 2);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		error = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		error = -2;
 +	return (error);
  }
  
  static int
 @@ -1368,59 +1366,45 @@
  static int
  t_cmsgcred_sockcred(void)
  {
 -	int error, fd, optval;
 +	int fd, error, optval;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 +	error = -2;
 +
  	if (sock_type == SOCK_STREAM)
  		if (listen(fd, LISTENQ) < 0) {
  			logmsg("listen");
 -			goto failed;
 +			goto done;
  		}
  
  	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 +	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
 +		if (errno == ENOPROTOOPT)
  			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(1);
  	}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	error = t_sockcred_server(0, fd, 1);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		error = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		error = -2;
 +	return (error);
  }
  
  /*
 @@ -1437,13 +1421,16 @@
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
 +	int fd, error;
 +
 +	error = 1;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_unbound_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1454,7 +1441,7 @@
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
  	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 +	    sizeof(struct cmsghdr) : sizeof(control_un.control);
  	msg.msg_flags = 0;
  
  	cmptr = CMSG_FIRSTHDR(&msg);
 @@ -1467,18 +1454,14 @@
  	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  
  	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +		goto done;
  
 +	error = 0;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		error = 1;
  failed:
 -	_exit(1);
 +	_exit(error);
  }
  
  /*
 @@ -1490,34 +1473,38 @@
  {
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct timeval)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct timeval *timeval;
 +	int error, fd2;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_timeout(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 +	iov[0].iov_len = sizeof(buf);
  
  	msg.msg_name = NULL;
  	msg.msg_namelen = 0;
  	msg.msg_iov = iov;
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;;
 +	msg.msg_controllen = sizeof(control_un.control);
  	msg.msg_flags = 0;
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0) {
 +		error = -2;
 +		goto done;
 +	}
  
  	error = -1;
  
 @@ -1527,12 +1514,13 @@
  	}
  
  	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 +		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
 +		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
  		goto done;
  	}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +	cmptr = CMSG_FIRSTHDR(&msg);
 +	if (cmptr == NULL) {
  		logmsgx("CMSG_FIRSTHDR is NULL");
  		goto done;
  	}
 @@ -1551,36 +1539,27 @@
  	}
  
  	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
 +		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
  		goto done;
  	}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +	timeval = (struct timeval *)CMSG_DATA(cmptr);
  
  	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
  	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 +	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +		logmsgx("control data has extra header, this is wrong");
  		goto done;
  	}
  
  	error = 0;
 -
  done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			error = -2;
  	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
  }
  
  static int
 @@ -1588,43 +1567,174 @@
  {
  	int error, fd;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 +	error = -2;
 +
  	if (sock_type == SOCK_STREAM)
  		if (listen(fd, LISTENQ) < 0) {
  			logmsg("listen");
 -			goto failed;
 +			goto done;
  		}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_timestamp_client();
  	}
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	error = t_timestamp_server(fd);
  
  	if (wait_client() < 0)
 +		error = -2;
 +done:
 +	if (close_socket(serv_sock_path, fd) < 0)
 +		error = -2;
 +	return (error);
 +}
 +
 +/*
 + * Verify that xucred structure has correct credentials.
 + */
 +static int
 +check_xucred(const struct xucred *xuc)
 +{
 +	if (xuc->cr_version != XUCRED_VERSION) {
 +		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
 +		    xuc->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xuc->cr_uid != my_euid) {
 +		logmsgx("cr_uid %lu != %lu (EUID of current process)",
 +		   (u_long)xuc->cr_uid, (u_long)my_euid);
 +		return (-1);
 +	}
 +	if (xuc->cr_ngroups == 0) {
 +		logmsgx("cr_ngroups = 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xuc->cr_ngroups > NGROUPS) {
 +		logmsgx("cr_ngroups %hu > %d (NGROUPS)",
 +		    xuc->cr_ngroups, NGROUPS);
 +		return (-1);
 +	}
 +	if (xuc->cr_groups[0] != my_egid) {
 +		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
 +		    (u_long)xuc->cr_groups[0], (u_long)my_egid);
 +		return (-1);
 +	}
 +	if (check_groups(xuc->cr_groups + 1, xuc->cr_ngroups - 1) < 0) {
 +		logmsgx("cr_groups has wrong GIDs");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Connect to server and set LOCAL_PEERCRED socket option for connected
 + * socket.  Verify that credentials of the peer are correct.
 + */
 +static void
 +t_peercred_client(void)
 +{
 +	struct xucred xuc;
 +	socklen_t len;
 +	int fd, error;
 +
 +	error = 1;
 +
 +	fd = create_unbound_socket();
 +	if (fd < 0)
  		goto failed;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 +	if (connect_server(fd) < 0)
 +		goto done;
 +
 +	len = sizeof(xuc);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xuc, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		goto done;
 +	}
 +
 +	if (check_xucred(&xuc) < 0)
 +		goto done;
 +done:
 +	error = close_socket((char *)NULL, fd) < 0 ? 1 : 0;
 +failed:
 +	_exit(error);
 +}
 +
 +/*
 + * Accept connection from client and set LOCAL_PEERCRED socket option for
 + * connected socket.  Verify that credentials of the peer are correct.
 + */
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xuc;
 +	socklen_t len;
 +	int fd2, error;
 +
 +	fd2 = accept_timeout(fd1);
 +	if (fd2 < 0)
  		return (-2);
 +
 +	len = sizeof(xuc);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xuc, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		error = -2;
 +		goto done;
  	}
 +
 +	if (check_xucred(&xuc) < 0) {
 +		error = -1;
 +		goto done;
 +	}
 +
 +	error = 0;
 +done:
 +	if (close_socket((char *)NULL, fd2) < 0)
 +		error = -2;
  	return (error);
 +}
  
 -failed:
 +static int
 +t_peercred(void)
 +{
 +	int fd, error;
 +
 +	fd = create_server_socket();
 +	if (fd < 0)
 +		return (-2);
 +
 +	error = -2;
 +
 +	if (sock_type == SOCK_STREAM)
 +		if (listen(fd, LISTENQ) < 0) {
 +			logmsg("listen");
 +			goto done;
 +		}
 +
 +	if (fork_client() < 0)
 +		goto done;
 +
 +	if (client_pid == 0) {
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(1);
 +		t_peercred_client();
 +	}
 +
 +	error = t_peercred_server(fd);
 +
 +	if (wait_client() < 0)
 +		error = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		error = -2;
 +	return (error);
  }
 diff -ruN unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/unix_cmsg.t	2009-01-30 15:44:49.000000000 +0200
 @@ -23,14 +23,15 @@
  	done
  }
  
 -echo "1..15"
 +echo "1..16"
  
  for desc in \
  	"Sending, receiving cmsgcred" \
  	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
  	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
  	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +	"Sending, receiving timestamp" \
 +	"Check LOCAL_PEERCRED socket option"
  do
  	n=`expr ${n} + 1`
  	run ${n} stream "" ${n} "STREAM ${desc}"
 @@ -48,10 +49,10 @@
  	run ${n} dgram "" ${i} "DGRAM ${desc}"
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
  
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
Responsible-Changed-From-To: freebsd-bugs->freebsd-net 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Fri Feb 13 14:27:57 UTC 2009 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=131567 

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Wed, 14 Oct 2009 14:07:13 +0300

 Updated version of the patch for regression/sockets/unix_cmsg has
 some optimizations and does not use SIGALRM.
 
 Can somebody verify whether it is possible to increase WARNS to higher
 level (I tested unix_cmsg on i386 and amd64 on 8.0-RC1 release)?
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/README	2009-10-14 13:31:06.000000000 +0300
 @@ -1,7 +1,7 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
  
  About unix_cmsg
 -================
 +===============
  
  This program is a collection of regression tests for ancillary (control)
  data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 @@ -13,8 +13,8 @@ is correct in received message.  Sometim
  messages to Server.
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
  
  Available options
  =================
 @@ -24,13 +24,13 @@ Available options
  
  -h	Output help message and exit.
  
 --t <socktype>
 +-t socktype
  	Run tests only for the given socket type: "stream" or "dgram".
  	With this option it is possible to run only particular test,
  	not all of them.
  
  -z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 +	should be followed by real control data.  It is not clear whether
  	a sender should give control data in all cases (this is not
  	documented and an arbitrary application can choose anything).
  
 @@ -90,6 +90,13 @@ For SOCK_STREAM sockets:
      message with data and control message with SCM_TIMESTAMP type
      followed by struct timeval{}.
  
 + 6: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use control data for PF_LOCAL sockets, but can be
 +    implemented here.  Client connects to Server.  Both Client and Server
 +    verify that credentials of the peer are correct using LOCAL_PEERCRED
 +    socket option.
 +
  For SOCK_DGRAM sockets:
  ----------------------
  
 @@ -110,7 +117,7 @@ For SOCK_DGRAM sockets:
      structure should contain correct information.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 +
      Server creates datagram socket and set socket option LOCAL_CREDS
      for it.  Client sends one message with data and control message with
      SOCK_CREDS type to Server.  Server should receive one message with
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
 +++ unix_cmsg/unix_cmsg.c	2009-10-14 13:47:51.000000000 +0300
 @@ -27,10 +27,12 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 @@ -38,16 +40,15 @@ __FBSDID("$FreeBSD: src/tools/regression
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdarg.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
 @@ -68,7 +69,7 @@ __FBSDID("$FreeBSD: src/tools/regression
   *
   * Each function which can block, is run under TIMEOUT, if timeout
   * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * with non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,46 +77,76 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	5
  #endif
  
  #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
  
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_stream1(void);
 +static int	t_sockcred_stream2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_sockcred_dgram(void);
 +static int	t_timestamp(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 +	int		(*func)(void);	/* Pointer to function.	*/
 +	const char	*desc;		/* Test description.	*/
  };
  
  static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_stream1,
 +	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_sockcred_stream2,
 +	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	},
 +	{ .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
 +
  static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_dgram,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 +static const char *myname;		/* "SERVER" or "CLIENT" */
  
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 +static int	debug = 0;		/* -d */
 +static int	no_control_data = 0;	/* -z */
  
  static u_int	nfailed = 0;		/* Number of failed tests. */
  
 @@ -131,8 +162,6 @@ static char	ipc_message[] = "hello";
  
  static struct sockaddr_un servaddr;	/* Server address. */
  
 -static sigjmp_buf env_alrm;
 -
  static uid_t	my_uid;
  static uid_t	my_euid;
  static gid_t	my_gid;
 @@ -156,32 +185,29 @@ static void	logmsg(const char *, ...) __
  static void	logmsgx(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
  /*
   * Output the help message (-h switch).
   */
  static void
 -usage(int quick)
 +usage(const int verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 +	    getprogname());
 +	if (!verbose)
  		return;
  	fprintf(stderr, "\n Options are:\n\
    -d\t\t\tOutput debugging information\n\
    -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 +  -t socktype\t\tRun test only for socket type: stream or dgram\n\
    -z\t\t\tDo not send real control data if possible\n\n");
  	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
  	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
  /*
 @@ -195,7 +221,7 @@ output(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
 @@ -210,18 +236,16 @@ logmsg(const char *format, ...)
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
  		output("%s: %s\n", myname, buf);
  	else
  		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
  	va_end(ap);
 -
 -	errno = errno_save;		/* Restore errno. */
 +	errno = errno_save;
  }
  
  /*
 @@ -235,33 +259,47 @@ logmsgx(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
  	output("%s: %s\n", myname, buf);
  	va_end(ap);
  }
  
  /*
 - * Run tests from testno1 to testno2.
 + * Run tests for the given socket type.
   */
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(const int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, nfailed1, testno2;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 +	output("Running tests for %s sockets:\n", sock_type_str);
  	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
  			++nfailed1;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred, "
 +			    "exiting");
  			return (-1);
  		}
  	}
 @@ -284,38 +322,40 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 -{
 -	siglongjmp(env_alrm, 1);
 -}
 -
  /*
   * Initialize signals handlers.
   */
  static void
  sig_init(void)
  {
 -	struct sigaction sa;
 +	struct sigaction sigact;
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0)
 +		err(EXIT_FAILURE, "sigaction(SIGPIPE)");
 +}
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +static int
 +fork_client(void)
 +{
 +	client_pid = fork();
 +	if (client_pid == 0)
 +		myname = "CLIENT";
 +	else if (client_pid == (pid_t)-1) {
 +		logmsg("fork");
 +		return (-1);
 +	}
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 +	u_int testno;
 +	int opt, rv, dgramflag, streamflag;
  
  	dgramflag = streamflag = 0;
  	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 @@ -324,141 +364,162 @@ main(int argc, char *argv[])
  			debug = 1;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(1);
 +			return (EXIT_SUCCESS);
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
  				streamflag = 1;
  			else if (strcmp(optarg, "dgram") == 0)
  				dgramflag = 1;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: wrong "
 +				    "socket type");
  			break;
  		case 'z':
  			no_control_data = 1;
  			break;
  		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(0);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 +		testno = 0;
  
  	if (dgramflag == 0 && streamflag == 0)
  		dgramflag = streamflag = 1;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (dgramflag && streamflag && testno != 0)
 +		errx(EXIT_FAILURE, "you can use particular test, only "
 +		    "with datagram or stream sockets");
  
  	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
  	my_uid = getuid();
  	my_euid = geteuid();
  	my_gid = getgid();
  	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 +	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
 +	switch (my_ngids) {
  	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 +		err(EXIT_FAILURE, "getgroups");
  		/* NOTREACHED */
  	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		errx(EXIT_FAILURE, "getgroups returned no groups");
  	}
  
  	sig_init();
  
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 -
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	myname = "SERVER";
 +	rv = EXIT_SUCCESS;
 +	if (streamflag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgramflag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (nfailed > 0 ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +/*
 + * Close socket descriptor, if sock_path is not equal to NULL,
 + * then unlink the given path.
 + */
 +static int
 +close_socket(const char *sock_path, const int fd)
 +{
 +	int rv;
 +
 +	if (close(fd) < 0) {
 +		logmsg("close_socket: close");
 +		rv = -1;
 +	} else
 +		rv = 0;
 +	if (sock_path != NULL)
 +		if (unlink(sock_path) < 0) {
 +			logmsg("close_socket: unlink(%s)", sock_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
  /*
   * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 + * bind() it.  Return socket address in *un.  Return file descriptor
   * or -1 if some error occurred.
   */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +create_socket(char *sock_path, const size_t sock_path_len,
 +    struct sockaddr_un *un)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("create_socket: socket(PF_LOCAL, %s, 0)",
 +		    sock_type_str);
  		return (-1);
  	}
  
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("create_socket: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
 +
  	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 +		int rv;
 +
 +		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
 +		    myname);
 +		if (rv < 0) {
  			logmsg("create_socket: snprintf failed");
  			goto failed;
  		}
  		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 +			logmsgx("create_socket: too long path name for "
 +			    "given buffer");
  			goto failed;
  		}
 -
 -		memset(addr, 0, sizeof(addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +		if (strlen(sock_path) >= sizeof(un->sun_path)) {
 +			logmsgx("create_socket: too long path name (>= %zu) "
 +			    "for local domain socket", sizeof(un->sun_path));
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 +		memset(un, 0, sizeof(un));
 +		un->sun_family = PF_LOCAL;
 +		strcpy(un->sun_path, sock_path);
 +		un->sun_len = SUN_LEN(un);
 +
 +		if (bind(fd, (struct sockaddr *)un, un->sun_len) < 0) {
  			logmsg("create_socket: bind(%s)", sock_path);
  			goto failed;
  		}
 @@ -473,43 +534,49 @@ failed:
  }
  
  /*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 + * Create server socket.
   */
  static int
  create_server_socket(void)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	int fd;
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	fd = create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr);
 +	if (fd < 0)
 +		return (-1);
 +
 +	if (sock_type == SOCK_STREAM) {
 +		int val;
 +
 +		if (listen(fd, LISTENQ) < 0) {
 +			logmsg("create_server_socket: listen");
 +			goto failed;
 +		}
 +		val = fcntl(fd, F_GETFL, 0);
 +		if (val < 0) {
 +			logmsg("create_server_socket: fcntl(F_GETFL)");
 +			goto failed;
 +		}
 +		if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +			logmsg("create_server_socket: fcntl(F_SETFL)");
 +			goto failed;
 +		}
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	(void)close_socket(serv_sock_path, fd);
 +	return (-1);
  }
  
  /*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 + * Create client socket.
   */
  static int
 -close_socket(const char *sock_path, int fd)
 +create_client_socket(void)
  {
 -	int error = 0;
 -
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 +	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
  }
  
  /*
 @@ -524,7 +591,7 @@ connect_server(int fd)
  	 * If PF_LOCAL listening socket's queue is full, then connect()
  	 * returns ECONNREFUSED immediately, do not need timeout.
  	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 +	if (connect(fd, (struct sockaddr *)&servaddr, servaddr.sun_len) < 0) {
  		logmsg("connect_server: connect(%s)", serv_sock_path);
  		return (-1);
  	}
 @@ -533,34 +600,23 @@ connect_server(int fd)
  }
  
  /*
 - * sendmsg() with timeout.
 + * sendmsg() wrapper.
   */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +send_message(const int fd, const struct msghdr *msg, const size_t n)
  {
  	ssize_t nsent;
  
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("sending %zu bytes", n));
  
  	nsent = sendmsg(fd, msg, 0);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +		logmsg("send_message: sendmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +		logmsgx("send_message: sendmsg: short send: "
 +		    "%zd of %zu bytes", nsent, n);
  		return (-1);
  	}
  
 @@ -568,63 +624,73 @@ sendmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * accept() with timeout.
 + * accept() wrapper.
   */
  static int
 -accept_timeout(int listenfd)
 +accept_connection(const int listenfd)
  {
 -	int fd;
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
  
  	dbgmsg(("accepting connection"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("accept_connection: select");
 +		return (-1);
 +	}
 +	if (rv == 0) {
 +		logmsgx("accept_connection: select timeout");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
  
  	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 -
 -	(void)alarm(0);
 -
  	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 +		logmsg("accept_connection: accept");
  		return (-1);
  	}
  
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("accept_connection: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("accept_connection: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
  	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("accept_connection: close");
 +	return (-1);
  }
  
  /*
 - * recvmsg() with timeout.
 + * recvmsg() wrapper.
   */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +recv_message(const int fd, struct msghdr *msg, const size_t n)
  {
  	ssize_t nread;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("receiving %zu bytes", n));
  
  	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +		logmsg("recv_message: recvmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +		logmsgx("recv_message: recvmsg: short read: "
 +		    "%zd of %zu bytes", nread, n);
  		return (-1);
  	}
  
 @@ -632,35 +698,23 @@ recvmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * Wait for synchronization message (1 byte) with timeout.
 + * Wait for synchronization message (1 byte).
   */
  static int
 -sync_recv(int fd)
 +sync_recv(const int fd)
  {
  	ssize_t nread;
  	char buf;
  
  	dbgmsg(("waiting for sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
  		logmsg("sync_recv: read");
  		return (-1);
  	}
 -
  	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
  		return (-1);
  	}
  
 @@ -668,34 +722,22 @@ sync_recv(int fd)
  }
  
  /*
 - * Send synchronization message (1 byte) with timeout.
 + * Send synchronization message (1 byte).
   */
  static int
 -sync_send(int fd)
 +sync_send(const int fd)
  {
  	ssize_t nsent;
  
  	dbgmsg(("sending sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
  		logmsg("sync_send: write");
  		return (-1);
  	}
 -
  	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
  		return (-1);
  	}
  
 @@ -711,36 +753,29 @@ wait_client(void)
  	int status;
  	pid_t pid;
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("waiting for a client"));
  
  	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
  	if (pid == (pid_t)-1) {
  		logmsg("wait_client: waitpid");
  		return (-1);
  	}
  
  	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("wait_client: exit status of client PID %ld "
 +			    "is %d", (long)client_pid, WEXITSTATUS(status));
  			return (-1);
  		}
  	} else {
  		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 +			logmsgx("wait_client: abnormal termination of client "
 +			    "PID %ld, signal %d%s", (long)client_pid,
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
  		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +			logmsgx("wait_client: termination of client PID %ld, "
 +			    "unknown status", (long)client_pid);
  		return (-1);
  	}
  
 @@ -752,24 +787,24 @@ wait_client(void)
   * has (my_ngids - 1) supplementary GIDs of current process.
   */
  static int
 -check_groups(const gid_t *gids, int n)
 +check_groups(const gid_t *gids, const int n)
  {
 +	int i, j, rv;
  	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
  
  	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 +		logmsgx("wrong number of groups %d != %d (returned from "
 +		    "getgroups() - 1)", n, my_ngids - 1);
 +		rv = -1;
  	} else
 -		error = 0;
 +		rv = 0;
  	for (i = 0; i < n; ++i) {
  		for (j = 1; j < my_ngids; ++j) {
  			if (gids[i] == my_gids[j]) {
  				if (match[j]) {
  					logmsgx("duplicated GID %lu",
  					    (u_long)gids[i]);
 -					error = -1;
 +					rv = -1;
  				} else
  					match[j] = 1;
  				break;
 @@ -777,15 +812,16 @@ check_groups(const gid_t *gids, int n)
  		}
  		if (j == my_ngids) {
  			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +			rv = -1;
  		}
  	}
  	for (j = 1; j < my_ngids; ++j)
  		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 +			logmsgx("did not receive supplementary GID %lu",
 +			    (u_long)my_gids[j]);
 +			rv = -1;
  		}
 -	return (error);
 +	return (rv);
  }
  
  /*
 @@ -793,7 +829,7 @@ check_groups(const gid_t *gids, int n)
   * to server and exit.
   */
  static void
 -t_cmsgcred_client(u_int n)
 +t_cmsgcred_client(const u_int n)
  {
  	union {
  		struct cmsghdr	cm;
 @@ -802,16 +838,19 @@ t_cmsgcred_client(u_int n)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(n == 1 || n == 2);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -834,20 +873,16 @@ t_cmsgcred_client(u_int n)
  	for (i = 0; i < n; ++i) {
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
  		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  	}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 -
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd))
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -856,30 +891,29 @@ failed:
   * socket for stream sockets or simply socket for datagram sockets.
   */
  static int
 -t_cmsgcred_server(int fd1)
 +t_cmsgcred_server(const int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 +	const struct cmsgcred *cmcred;
  	u_int i;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 -
 -	controllen = sizeof(control_un.control);
 +	rv = 0;
  
  	for (i = 0; i < 2; ++i) {
  		iov[0].iov_base = buf;
 @@ -890,29 +924,31 @@ t_cmsgcred_server(int fd1)
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 +			logmsgx("#%u control data was truncated, MSG_CTRUNC "
 +			    "flag is on", i);
 +			goto next;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 @@ -921,166 +957,147 @@ t_cmsgcred_server(int fd1)
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
 +			    (u_int)cmptr->cmsg_len,
 +			    CMSG_LEN(sizeof(struct cmsgcred)));
 +			goto next;
  		}
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 +		if (cmcred->cmcred_pid != client_pid) {
  			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 +			    i, (long)cmcred->cmcred_pid, (long)client_pid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 +		if (cmcred->cmcred_uid != my_uid) {
 +			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_uid,
 +			    (u_long)my_uid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_euid != my_euid) {
 +			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_euid,
 +			    (u_long)my_euid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_gid != my_gid) {
 +			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 +		if (cmcred->cmcred_ngroups == 0) {
  			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 +			goto next;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 +		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
 +			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 +			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_ngroups < 0) {
 +			logmsgx("#%u cmcred_ngroups %d < 0", i,
 +			    cmcred->cmcred_ngroups);
 +			goto next;
 +		}
 +
 +		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
 +		if (cmcred->cmcred_groups[0] != my_egid) {
 +			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
 +			    "current process)", i,
 +			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
 +			goto next;
 +		}
 +		if (check_groups(cmcred->cmcred_groups + 1,
 +		    cmcred->cmcred_ngroups - 1) < 0) {
 +			logmsgx("#%u cmcred_groups has wrong GIDs", i);
 +			goto next;
 +		}
 +
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, this is "
 +			    "wrong", i);
 +			goto next;
  		}
 -
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
  
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_cmsgcred(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(2);
  	}
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_cmsgcred_server(fd);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
   * Send two messages with data to server and exit.
   */
  static void
 -t_sockcred_client(int type)
 +t_sockcred_client(const int type)
  {
  	struct msghdr msg;
  	struct iovec iov[1];
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	if (type == 1)
  		if (sync_recv(fd) < 0)
 -			goto failed_close;
 +			goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1094,19 +1111,15 @@ t_sockcred_client(int type)
  	msg.msg_flags = 0;
  
  	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1117,234 +1130,219 @@ failed:
   * 1, then set LOCAL_CREDS option for accepted stream socket.
   */
  static int
 -t_sockcred_server(int type, int fd1, u_int n)
 +t_sockcred_server(const int type, const int fd1, const u_int n)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
  	u_int i;
 +	int fd2, rv, optval;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	assert(n == 1 || n == 2);
  	assert(type == 0 || type == 1);
  
 +	rv = -2;
 +
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  		if (type == 1) {
  			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 +			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
 +			    sizeof(optval)) < 0) {
 +				logmsg("setsockopt(LOCAL_CREDS) for accepted "
 +				    "socket");
 +				if (errno == ENOPROTOOPT)
 +					rv = -1;
 +				goto done;
  			}
  			if (sync_send(fd2) < 0)
 -				goto failed;
 +				goto done;
  		}
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 +	rv = 0;
  
  	for (i = 0; i < n; ++i) {
  		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 +		iov[0].iov_len = sizeof(buf);
  
  		msg.msg_name = NULL;
  		msg.msg_namelen = 0;
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +			logmsgx("control data was truncated, MSG_CTRUNC flag "
 +			    "is on");
 +			goto next;
  		}
  
 +		dbgmsg(("#%u msg_controllen = %u", i,
 +		    (u_int)msg.msg_controllen));
 +
  		if (i != 0 && sock_type == SOCK_STREAM) {
  			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 +				logmsgx("second message has control data, "
 +				    "this is wrong for stream sockets");
 +				goto next;
  			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
  			continue;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
  
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
 +			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
 +			goto next;
  		}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
  		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 +			logmsgx("#%u sc_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)sockcred->sc_uid,
 +			    (u_long)my_uid);
 +			goto next;
  		}
  		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 +			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)sockcred->sc_euid,
 +			    (u_long)my_euid);
 +			goto next;
  		}
  		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 +			logmsgx("#%u sc_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
  		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 +			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_egid);
 +			goto next;
  		}
  		if (sockcred->sc_ngroups > NGROUPS_MAX) {
  			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
  			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 -			}
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups < 0) {
 +			logmsgx("#%u sc_ngroups %d < 0", i,
 +			    sockcred->sc_ngroups);
 +			goto next;
  		}
  
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 +		if (check_groups(sockcred->sc_groups,
 +		    sockcred->sc_ngroups) < 0) {
 +			logmsgx("#%u sc_groups has wrong GIDs", i);
 +			goto next;
  		}
  
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, this is "
 +			    "wrong", i);
 +			goto next;
 +		}
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
 -
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 +done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
 -t_sockcred(int type)
 +t_sockcred(const int type)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	if (type == 0) {
  		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 +		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
 +		    sizeof(optval)) < 0) {
  			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 +			    sock_type_str);
 +			if (errno == ENOPROTOOPT)
 +				rv = -1;
 +			goto done;
  		}
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_sockcred_client(type);
  	}
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(type, fd, 2);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  static int
 @@ -1368,59 +1366,39 @@ t_sockcred_dgram(void)
  static int
  t_cmsgcred_sockcred(void)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
 +		if (errno == ENOPROTOOPT)
 +			rv = -1;
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(1);
  	}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(0, fd, 1);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1437,13 +1415,16 @@ t_timestamp_client(void)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1454,7 +1435,7 @@ t_timestamp_client(void)
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
  	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 +	    sizeof(struct cmsghdr) : sizeof(control_un.control);
  	msg.msg_flags = 0;
  
  	cmptr = CMSG_FIRSTHDR(&msg);
 @@ -1466,19 +1447,15 @@ t_timestamp_client(void)
  	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
  	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +		goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1486,40 +1463,44 @@ failed:
   * type followed by struct timeval{} from client.
   */
  static int
 -t_timestamp_server(int fd1)
 +t_timestamp_server(const int fd1)
  {
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct timeval)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct timeval *timeval;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 +	iov[0].iov_len = sizeof(buf);
  
  	msg.msg_name = NULL;
  	msg.msg_namelen = 0;
  	msg.msg_iov = iov;
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;;
 +	msg.msg_controllen = sizeof(control_un.control);
  	msg.msg_flags = 0;
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +		rv = -2;
 +		goto done;
 +	}
  
 -	error = -1;
 +	rv = -1;
  
  	if (msg.msg_flags & MSG_CTRUNC) {
  		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 @@ -1527,12 +1508,13 @@ t_timestamp_server(int fd1)
  	}
  
  	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 +		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
 +		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
  		goto done;
  	}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +	cmptr = CMSG_FIRSTHDR(&msg);
 +	if (cmptr == NULL) {
  		logmsgx("CMSG_FIRSTHDR is NULL");
  		goto done;
  	}
 @@ -1551,80 +1533,195 @@ t_timestamp_server(int fd1)
  	}
  
  	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
 +		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
  		goto done;
  	}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +	timeval = (struct timeval *)CMSG_DATA(cmptr);
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 +	dbgmsg(("timeval tv_sec %"PRIdMAX", tv_usec %"PRIdMAX,
  	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 +	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +		logmsgx("control data has extra header, this is wrong");
  		goto done;
  	}
  
 -	error = 0;
 -
 +	rv = 0;
  done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_timestamp(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_timestamp_client();
  	}
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_timestamp_server(fd);
  
  	if (wait_client() < 0)
 +		rv = -2;
 +done:
 +	if (close_socket(serv_sock_path, fd) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +/*
 + * Verify that xucred structure has correct credentials.
 + */
 +static int
 +check_xucred(const struct xucred *xucred, const socklen_t len)
 +{
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu (sizeof(struct xucred))",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != my_euid) {
 +		logmsgx("cr_uid %lu != %lu (EUID of current process)",
 +		   (u_long)xucred->cr_uid, (u_long)my_euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("cr_ngroups = 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > NGROUPS) {
 +		logmsgx("cr_ngroups %hu > %d (NGROUPS)",
 +		    xucred->cr_ngroups, NGROUPS);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != my_egid) {
 +		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
 +		    (u_long)xucred->cr_groups[0], (u_long)my_egid);
 +		return (-1);
 +	}
 +	if (check_groups(xucred->cr_groups + 1, xucred->cr_ngroups - 1) < 0) {
 +		logmsgx("cr_groups has wrong GIDs");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Connect to server and set LOCAL_PEERCRED socket option for connected
 + * socket.  Verify that credentials of the peer are correct.
 + */
 +static void
 +t_peercred_client(void)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +	if (connect_server(fd) < 0)
 +		goto done;
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		goto done;
  	}
 -	return (error);
  
 +	if (check_xucred(&xucred, len) < 0)
 +		goto done;
 +done:
 +	rv = close_socket((char *)NULL, fd) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
  failed:
 +	_exit(rv);
 +}
 +
 +/*
 + * Accept connection from client and set LOCAL_PEERCRED socket option for
 + * connected socket.  Verify that credentials of the peer are correct.
 + */
 +static int
 +t_peercred_server(const int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
 +
 +	fd2 = accept_connection(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
 +	}
 +
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (close_socket((char *)NULL, fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	int fd, rv;
 +
 +	fd = create_server_socket();
 +	if (fd < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	if (fork_client() < 0)
 +		goto done;
 +
 +	if (client_pid == 0) {
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(1);
 +		t_peercred_client();
 +	}
 +
 +	rv = t_peercred_server(fd);
 +
 +	if (wait_client() < 0)
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/unix_cmsg.t	2009-10-14 02:56:49.000000000 +0300
 @@ -23,14 +23,15 @@ run()
  	done
  }
  
 -echo "1..15"
 +echo "1..16"
  
  for desc in \
  	"Sending, receiving cmsgcred" \
  	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
  	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
  	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +	"Sending, receiving timestamp" \
 +	"Check LOCAL_PEERCRED socket option"
  do
  	n=`expr ${n} + 1`
  	run ${n} stream "" ${n} "STREAM ${desc}"
 @@ -48,10 +49,10 @@ do
  	run ${n} dgram "" ${i} "DGRAM ${desc}"
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
  
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Wed, 21 Apr 2010 16:08:13 +0300

 Updated version of unix_cmsg now works correctly on FreeBSD 8.0-STABLE
 and 9.0-CURRENT, where data returned in groups array was changed.
 
 The "TODO" was removed from several tests, since they are passed correctly
 on 8.0-STABLE and 9.0-CURRENT.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/README	2010-04-21 12:28:59.000000000 +0300
 @@ -1,7 +1,7 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
  
  About unix_cmsg
 -================
 +===============
  
  This program is a collection of regression tests for ancillary (control)
  data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 @@ -13,8 +13,8 @@ is correct in received message.  Sometim
  messages to Server.
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
  
  Available options
  =================
 @@ -24,13 +24,13 @@ Available options
  
  -h	Output help message and exit.
  
 --t <socktype>
 +-t socktype
  	Run tests only for the given socket type: "stream" or "dgram".
  	With this option it is possible to run only particular test,
  	not all of them.
  
  -z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 +	should be followed by real control data.  It is not clear whether
  	a sender should give control data in all cases (this is not
  	documented and an arbitrary application can choose anything).
  
 @@ -90,6 +90,13 @@ For SOCK_STREAM sockets:
      message with data and control message with SCM_TIMESTAMP type
      followed by struct timeval{}.
  
 + 6: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use control data for PF_LOCAL sockets, but can be
 +    implemented here.  Client connects to Server.  Both Client and Server
 +    verify that credentials of the peer are correct using LOCAL_PEERCRED
 +    socket option.
 +
  For SOCK_DGRAM sockets:
  ----------------------
  
 @@ -110,7 +117,7 @@ For SOCK_DGRAM sockets:
      structure should contain correct information.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 +
      Server creates datagram socket and set socket option LOCAL_CREDS
      for it.  Client sends one message with data and control message with
      SOCK_CREDS type to Server.  Server should receive one message with
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
 +++ unix_cmsg/unix_cmsg.c	2010-04-21 15:23:45.000000000 +0300
 @@ -27,10 +27,12 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 @@ -38,16 +40,15 @@ __FBSDID("$FreeBSD: src/tools/regression
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdarg.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
 @@ -68,7 +69,7 @@ __FBSDID("$FreeBSD: src/tools/regression
   *
   * Each function which can block, is run under TIMEOUT, if timeout
   * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * with non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,46 +77,76 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	5
  #endif
  
  #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
  
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_stream1(void);
 +static int	t_sockcred_stream2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_sockcred_dgram(void);
 +static int	t_timestamp(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 +	int		(*func)(void);	/* Pointer to function.	*/
 +	const char	*desc;		/* Test description.	*/
  };
  
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_stream1,
 +	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_sockcred_stream2,
 +	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	},
 +	{ .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
 +
 +static const struct test_func test_dgram_tbl[] = {
 +	{ .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{ .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{ .func = t_sockcred_dgram,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{ .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{ .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 +static const char *myname;		/* "SERVER" or "CLIENT" */
  
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 +static int	debug = 0;		/* -d */
 +static int	no_control_data = 0;	/* -z */
  
  static u_int	nfailed = 0;		/* Number of failed tests. */
  
 @@ -131,17 +162,11 @@ static char	ipc_message[] = "hello";
  
  static struct sockaddr_un servaddr;	/* Server address. */
  
 -static sigjmp_buf env_alrm;
 -
  static uid_t	my_uid;
  static uid_t	my_euid;
  static gid_t	my_gid;
  static gid_t	my_egid;
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
  static gid_t	my_gids[NGROUPS_MAX];
  static int	my_ngids;
  
 @@ -156,32 +181,29 @@ static void	logmsg(const char *, ...) __
  static void	logmsgx(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
  /*
   * Output the help message (-h switch).
   */
  static void
 -usage(int quick)
 +usage(int verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 +	    getprogname());
 +	if (!verbose)
  		return;
  	fprintf(stderr, "\n Options are:\n\
    -d\t\t\tOutput debugging information\n\
    -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 +  -t socktype\t\tRun test only for socket type: stream or dgram\n\
    -z\t\t\tDo not send real control data if possible\n\n");
  	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
  	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
  /*
 @@ -195,7 +217,7 @@ output(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
 @@ -210,18 +232,16 @@ logmsg(const char *format, ...)
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
  		output("%s: %s\n", myname, buf);
  	else
  		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
  	va_end(ap);
 -
 -	errno = errno_save;		/* Restore errno. */
 +	errno = errno_save;
  }
  
  /*
 @@ -235,33 +255,47 @@ logmsgx(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
  	output("%s: %s\n", myname, buf);
  	va_end(ap);
  }
  
  /*
 - * Run tests from testno1 to testno2.
 + * Run tests for the given socket type.
   */
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, nfailed1, testno2;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 +	output("Running tests for %s sockets:\n", sock_type_str);
  	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
  			++nfailed1;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred, "
 +			    "exiting");
  			return (-1);
  		}
  	}
 @@ -284,38 +318,57 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 +/*
 + * Initialize signals handlers.
 + */
  static void
 -sig_alrm(int signo __unused)
 +sig_init(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	struct sigaction sigact;
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0)
 +		err(EXIT_FAILURE, "sigaction(SIGPIPE)");
  }
  
  /*
 - * Initialize signals handlers.
 + * Output this process UID, GID, groups from getgroups(), EUID and EGID.
   */
  static void
 -sig_init(void)
 +show_my_id(void)
  {
 -	struct sigaction sa;
 +	int i;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	if (!debug)
 +		return;
 +	logmsgx("UID %lu, GID %lu, groups from getgroups():",
 +	    (u_long)my_uid, (u_long)my_gid);
 +	for (i = 0; i < my_ngids; ++i)
 +		logmsgx("  GID %lu", (u_long)my_gids[i]);
 +	logmsgx("EUID %lu, EGID %lu", (u_long)my_euid, (u_long)my_egid);
 +}
 +
 +static int
 +fork_client(void)
 +{
 +	client_pid = fork();
 +	if (client_pid == 0)
 +		myname = "CLIENT";
 +	else if (client_pid == (pid_t)-1) {
 +		logmsg("fork");
 +		return (-1);
 +	}
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 +	u_int testno;
 +	int opt, rv, dgramflag, streamflag;
  
  	dgramflag = streamflag = 0;
  	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 @@ -324,141 +377,162 @@ main(int argc, char *argv[])
  			debug = 1;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(1);
 +			return (EXIT_SUCCESS);
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
  				streamflag = 1;
  			else if (strcmp(optarg, "dgram") == 0)
  				dgramflag = 1;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
  			no_control_data = 1;
  			break;
  		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(0);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 +		testno = 0;
  
  	if (dgramflag == 0 && streamflag == 0)
  		dgramflag = streamflag = 1;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (dgramflag && streamflag && testno != 0)
 +		errx(EXIT_FAILURE, "you can use particular test, only "
 +		    "with datagram or stream sockets");
  
  	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
  	my_uid = getuid();
  	my_euid = geteuid();
  	my_gid = getgid();
  	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 +	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
 +	switch (my_ngids) {
  	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 +		err(EXIT_FAILURE, "getgroups");
  		/* NOTREACHED */
  	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		errx(EXIT_FAILURE, "getgroups returned no groups");
  	}
  
  	sig_init();
  
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 -
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	myname = "SERVER";
 +	rv = EXIT_SUCCESS;
 +	show_my_id();
 +	if (streamflag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgramflag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (nfailed > 0 ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +/*
 + * Close socket descriptor, if sock_path is not equal to NULL,
 + * then unlink the given path.
 + */
 +static int
 +close_socket(const char *sock_path, int fd)
 +{
 +	int rv;
 +
 +	if (close(fd) < 0) {
 +		logmsg("close_socket: close");
 +		rv = -1;
 +	} else
 +		rv = 0;
 +	if (sock_path != NULL)
 +		if (unlink(sock_path) < 0) {
 +			logmsg("close_socket: unlink(%s)", sock_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
  /*
   * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 + * bind() it.  Return socket address in *un.  Return file descriptor
   * or -1 if some error occurred.
   */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *un)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("create_socket: socket(PF_LOCAL, %s, 0)",
 +		    sock_type_str);
  		return (-1);
  	}
  
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("create_socket: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
 +
  	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 +		int rv;
 +
 +		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
 +		    myname);
 +		if (rv < 0) {
  			logmsg("create_socket: snprintf failed");
  			goto failed;
  		}
  		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 +			logmsgx("create_socket: too long path name for "
 +			    "given buffer");
  			goto failed;
  		}
 -
 -		memset(addr, 0, sizeof(addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +		if (strlen(sock_path) >= sizeof(un->sun_path)) {
 +			logmsgx("create_socket: too long path name (>= %zu) "
 +			    "for local domain socket", sizeof(un->sun_path));
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 +		memset(un, 0, sizeof(un));
 +		un->sun_family = PF_LOCAL;
 +		strcpy(un->sun_path, sock_path);
 +		un->sun_len = SUN_LEN(un);
 +
 +		if (bind(fd, (struct sockaddr *)un, un->sun_len) < 0) {
  			logmsg("create_socket: bind(%s)", sock_path);
  			goto failed;
  		}
 @@ -473,43 +547,49 @@ failed:
  }
  
  /*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 + * Create server socket.
   */
  static int
  create_server_socket(void)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	int fd;
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	fd = create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr);
 +	if (fd < 0)
 +		return (-1);
 +
 +	if (sock_type == SOCK_STREAM) {
 +		int val;
 +
 +		if (listen(fd, LISTENQ) < 0) {
 +			logmsg("create_server_socket: listen");
 +			goto failed;
 +		}
 +		val = fcntl(fd, F_GETFL, 0);
 +		if (val < 0) {
 +			logmsg("create_server_socket: fcntl(F_GETFL)");
 +			goto failed;
 +		}
 +		if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +			logmsg("create_server_socket: fcntl(F_SETFL)");
 +			goto failed;
 +		}
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	(void)close_socket(serv_sock_path, fd);
 +	return (-1);
  }
  
  /*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 + * Create client socket.
   */
  static int
 -close_socket(const char *sock_path, int fd)
 +create_client_socket(void)
  {
 -	int error = 0;
 -
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 +	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
  }
  
  /*
 @@ -524,7 +604,7 @@ connect_server(int fd)
  	 * If PF_LOCAL listening socket's queue is full, then connect()
  	 * returns ECONNREFUSED immediately, do not need timeout.
  	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 +	if (connect(fd, (struct sockaddr *)&servaddr, servaddr.sun_len) < 0) {
  		logmsg("connect_server: connect(%s)", serv_sock_path);
  		return (-1);
  	}
 @@ -533,34 +613,23 @@ connect_server(int fd)
  }
  
  /*
 - * sendmsg() with timeout.
 + * sendmsg() wrapper.
   */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +send_message(int fd, const struct msghdr *msg, size_t n)
  {
  	ssize_t nsent;
  
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("sending %zu bytes", n));
  
  	nsent = sendmsg(fd, msg, 0);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +		logmsg("send_message: sendmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +		logmsgx("send_message: sendmsg: short send: "
 +		    "%zd of %zu bytes", nsent, n);
  		return (-1);
  	}
  
 @@ -568,63 +637,73 @@ sendmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * accept() with timeout.
 + * accept() wrapper.
   */
  static int
 -accept_timeout(int listenfd)
 +accept_connection(int listenfd)
  {
 -	int fd;
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
  
  	dbgmsg(("accepting connection"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("accept_connection: select");
 +		return (-1);
 +	}
 +	if (rv == 0) {
 +		logmsgx("accept_connection: select timeout");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
  
  	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 -
 -	(void)alarm(0);
 -
  	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 +		logmsg("accept_connection: accept");
  		return (-1);
  	}
  
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("accept_connection: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("accept_connection: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
  	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("accept_connection: close");
 +	return (-1);
  }
  
  /*
 - * recvmsg() with timeout.
 + * recvmsg() wrapper.
   */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +recv_message(int fd, struct msghdr *msg, size_t n)
  {
  	ssize_t nread;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("receiving %zu bytes", n));
  
  	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +		logmsg("recv_message: recvmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +		logmsgx("recv_message: recvmsg: short read: "
 +		    "%zd of %zu bytes", nread, n);
  		return (-1);
  	}
  
 @@ -632,7 +711,7 @@ recvmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * Wait for synchronization message (1 byte) with timeout.
 + * Wait for synchronization message (1 byte).
   */
  static int
  sync_recv(int fd)
 @@ -642,25 +721,13 @@ sync_recv(int fd)
  
  	dbgmsg(("waiting for sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
  		logmsg("sync_recv: read");
  		return (-1);
  	}
 -
  	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
  		return (-1);
  	}
  
 @@ -668,7 +735,7 @@ sync_recv(int fd)
  }
  
  /*
 - * Send synchronization message (1 byte) with timeout.
 + * Send synchronization message (1 byte).
   */
  static int
  sync_send(int fd)
 @@ -677,25 +744,13 @@ sync_send(int fd)
  
  	dbgmsg(("sending sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
  		logmsg("sync_send: write");
  		return (-1);
  	}
 -
  	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
  		return (-1);
  	}
  
 @@ -711,36 +766,29 @@ wait_client(void)
  	int status;
  	pid_t pid;
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("waiting for a client"));
  
  	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
  	if (pid == (pid_t)-1) {
  		logmsg("wait_client: waitpid");
  		return (-1);
  	}
  
  	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("wait_client: exit status of client PID %ld "
 +			    "is %d", (long)client_pid, WEXITSTATUS(status));
  			return (-1);
  		}
  	} else {
  		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 +			logmsgx("wait_client: abnormal termination of client "
 +			    "PID %ld, signal %d%s", (long)client_pid,
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
  		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +			logmsgx("wait_client: termination of client PID %ld, "
 +			    "unknown status", (long)client_pid);
  		return (-1);
  	}
  
 @@ -748,28 +796,27 @@ wait_client(void)
  }
  
  /*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 + * Check whether n GIDs in gids are correct.
   */
  static int
  check_groups(const gid_t *gids, int n)
  {
 +	int i, j, rv;
  	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 +	if (n != my_ngids) {
 +		logmsgx("wrong number of groups %d != %d (returned from "
 +		    "getgroups())", n, my_ngids);
 +		rv = -1;
  	} else
 -		error = 0;
 +		rv = 0;
  	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 +		for (j = 0; j < my_ngids; ++j) {
  			if (gids[i] == my_gids[j]) {
  				if (match[j]) {
  					logmsgx("duplicated GID %lu",
  					    (u_long)gids[i]);
 -					error = -1;
 +					rv = -1;
  				} else
  					match[j] = 1;
  				break;
 @@ -777,15 +824,15 @@ check_groups(const gid_t *gids, int n)
  		}
  		if (j == my_ngids) {
  			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +			rv = -1;
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 +	for (j = 0; j < my_ngids; ++j)
  		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 +			logmsgx("did not receive GID %lu", (u_long)my_gids[j]);
 +			rv = -1;
  		}
 -	return (error);
 +	return (rv);
  }
  
  /*
 @@ -802,16 +849,19 @@ t_cmsgcred_client(u_int n)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(n == 1 || n == 2);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -834,20 +884,16 @@ t_cmsgcred_client(u_int n)
  	for (i = 0; i < n; ++i) {
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
  		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  	}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 -
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd))
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -858,28 +904,27 @@ failed:
  static int
  t_cmsgcred_server(int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 +	const struct cmsgcred *cmcred;
  	u_int i;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 -
 -	controllen = sizeof(control_un.control);
 +	rv = 0;
  
  	for (i = 0; i < 2; ++i) {
  		iov[0].iov_base = buf;
 @@ -890,29 +935,31 @@ t_cmsgcred_server(int fd1)
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 +			logmsgx("#%u control data was truncated, MSG_CTRUNC "
 +			    "flag is on", i);
 +			goto next;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 @@ -921,142 +968,120 @@ t_cmsgcred_server(int fd1)
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
 +			    (u_int)cmptr->cmsg_len,
 +			    CMSG_LEN(sizeof(struct cmsgcred)));
 +			goto next;
  		}
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 +		if (cmcred->cmcred_pid != client_pid) {
  			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 +			    i, (long)cmcred->cmcred_pid, (long)client_pid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 +		if (cmcred->cmcred_uid != my_uid) {
 +			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_uid,
 +			    (u_long)my_uid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_euid != my_euid) {
 +			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_euid,
 +			    (u_long)my_euid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_gid != my_gid) {
 +			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 +		if (cmcred->cmcred_ngroups == 0) {
  			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 +			goto next;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 +		if (cmcred->cmcred_ngroups < 0) {
 +			logmsgx("#%u cmcred_ngroups %d < 0",
 +			    i, cmcred->cmcred_ngroups);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
 +			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 +			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
 +			goto next;
 +		}
 +
 +		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
 +		if (cmcred->cmcred_groups[0] != my_egid) {
 +			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
 +			    "current process)", i,
 +			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
 +			goto next;
 +		}
 +		if (check_groups(cmcred->cmcred_groups,
 +		    cmcred->cmcred_ngroups) < 0) {
 +			logmsgx("#%u cmcred_groups has wrong GIDs", i);
 +			goto next;
 +		}
 +
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
  		}
 -
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
  
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_cmsgcred(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(2);
  	}
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_cmsgcred_server(fd);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1067,20 +1092,23 @@ t_sockcred_client(int type)
  {
  	struct msghdr msg;
  	struct iovec iov[1];
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	if (type == 1)
  		if (sync_recv(fd) < 0)
 -			goto failed_close;
 +			goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1094,19 +1122,15 @@ t_sockcred_client(int type)
  	msg.msg_flags = 0;
  
  	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1119,232 +1143,222 @@ failed:
  static int
  t_sockcred_server(int type, int fd1, u_int n)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
  	u_int i;
 +	int fd2, rv, optval;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	assert(n == 1 || n == 2);
  	assert(type == 0 || type == 1);
  
 +	rv = -2;
 +
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  		if (type == 1) {
  			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 +			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
 +			    sizeof(optval)) < 0) {
 +				logmsg("setsockopt(LOCAL_CREDS) for accepted "
 +				    "socket");
 +				if (errno == ENOPROTOOPT)
 +					rv = -1;
 +				goto done;
  			}
  			if (sync_send(fd2) < 0)
 -				goto failed;
 +				goto done;
  		}
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 +	rv = 0;
  
  	for (i = 0; i < n; ++i) {
  		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 +		iov[0].iov_len = sizeof(buf);
  
  		msg.msg_name = NULL;
  		msg.msg_namelen = 0;
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +			logmsgx("control data was truncated, MSG_CTRUNC flag "
 +			    "is on");
 +			goto next;
  		}
  
 +		dbgmsg(("#%u msg_controllen = %u", i,
 +		    (u_int)msg.msg_controllen));
 +
  		if (i != 0 && sock_type == SOCK_STREAM) {
  			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 +				logmsgx("second message has control data, "
 +				    "this is wrong for stream sockets");
 +				goto next;
  			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
  			continue;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
  
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
 +			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
 +			goto next;
  		}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
  		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 +			logmsgx("#%u sc_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)sockcred->sc_uid,
 +			    (u_long)my_uid);
 +			goto next;
  		}
  		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 +			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)sockcred->sc_euid,
 +			    (u_long)my_euid);
 +			goto next;
  		}
  		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 +			logmsgx("#%u sc_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
  		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 +			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_egid);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups == 0) {
 +			logmsgx("#%u sc_ngroups %d = 0, this is wrong",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups < 0) {
 +			logmsgx("#%u sc_ngroups %d < 0",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
  		}
  		if (sockcred->sc_ngroups > NGROUPS_MAX) {
  			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
  			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 -			}
 +			goto next;
  		}
  
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 +		if (check_groups(sockcred->sc_groups,
 +		    sockcred->sc_ngroups) < 0) {
 +			logmsgx("#%u sc_groups has wrong GIDs", i);
 +			goto next;
  		}
  
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
 +		}
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
 -
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 +done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_sockcred(int type)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	if (type == 0) {
  		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 +		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
 +		    sizeof(optval)) < 0) {
  			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 +			    sock_type_str);
 +			if (errno == ENOPROTOOPT)
 +				rv = -1;
 +			goto done;
  		}
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_sockcred_client(type);
  	}
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(type, fd, 2);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  static int
 @@ -1368,59 +1382,39 @@ t_sockcred_dgram(void)
  static int
  t_cmsgcred_sockcred(void)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
 +		if (errno == ENOPROTOOPT)
 +			rv = -1;
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_cmsgcred_client(1);
  	}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(0, fd, 1);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1437,13 +1431,16 @@ t_timestamp_client(void)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1454,7 +1451,7 @@ t_timestamp_client(void)
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
  	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 +	    sizeof(struct cmsghdr) : sizeof(control_un.control);
  	msg.msg_flags = 0;
  
  	cmptr = CMSG_FIRSTHDR(&msg);
 @@ -1466,19 +1463,15 @@ t_timestamp_client(void)
  	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
  	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +		goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1490,36 +1483,40 @@ t_timestamp_server(int fd1)
  {
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct timeval)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct timeval *timeval;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 +	iov[0].iov_len = sizeof(buf);
  
  	msg.msg_name = NULL;
  	msg.msg_namelen = 0;
  	msg.msg_iov = iov;
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;;
 +	msg.msg_controllen = sizeof(control_un.control);
  	msg.msg_flags = 0;
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +		rv = -2;
 +		goto done;
 +	}
  
 -	error = -1;
 +	rv = -1;
  
  	if (msg.msg_flags & MSG_CTRUNC) {
  		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 @@ -1527,12 +1524,13 @@ t_timestamp_server(int fd1)
  	}
  
  	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 +		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
 +		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
  		goto done;
  	}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +	cmptr = CMSG_FIRSTHDR(&msg);
 +	if (cmptr == NULL) {
  		logmsgx("CMSG_FIRSTHDR is NULL");
  		goto done;
  	}
 @@ -1551,80 +1549,199 @@ t_timestamp_server(int fd1)
  	}
  
  	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
 +		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
  		goto done;
  	}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +	timeval = (struct timeval *)CMSG_DATA(cmptr);
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 +	dbgmsg(("timeval tv_sec %"PRIdMAX", tv_usec %"PRIdMAX,
  	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 +	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +		logmsgx("control data has extra header, this is wrong");
  		goto done;
  	}
  
 -	error = 0;
 -
 +	rv = 0;
  done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_timestamp(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 +		if (close_socket((char *)NULL, fd) < 0)
  			_exit(1);
  		t_timestamp_client();
  	}
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_timestamp_server(fd);
  
  	if (wait_client() < 0)
 +		rv = -2;
 +done:
 +	if (close_socket(serv_sock_path, fd) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +/*
 + * Verify that xucred structure has correct credentials.
 + */
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
 +{
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu (sizeof(struct xucred))",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != my_euid) {
 +		logmsgx("cr_uid %lu != %lu (EUID of current process)",
 +		   (u_long)xucred->cr_uid, (u_long)my_euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("cr_ngroups = 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("cr_ngroups < 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > NGROUPS_MAX) {
 +		logmsgx("cr_ngroups %hu > %u (NGROUPS_MAX)",
 +		    xucred->cr_ngroups, NGROUPS_MAX);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != my_egid) {
 +		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
 +		    (u_long)xucred->cr_groups[0], (u_long)my_egid);
 +		return (-1);
 +	}
 +	if (check_groups(xucred->cr_groups, xucred->cr_ngroups) < 0) {
 +		logmsgx("cr_groups has wrong GIDs");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Connect to server and set LOCAL_PEERCRED socket option for connected
 + * socket.  Verify that credentials of the peer are correct.
 + */
 +static void
 +t_peercred_client(void)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +	if (connect_server(fd) < 0)
 +		goto done;
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		goto done;
  	}
 -	return (error);
  
 +	if (check_xucred(&xucred, len) < 0)
 +		goto done;
 +done:
 +	rv = close_socket((char *)NULL, fd) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
  failed:
 +	_exit(rv);
 +}
 +
 +/*
 + * Accept connection from client and set LOCAL_PEERCRED socket option for
 + * connected socket.  Verify that credentials of the peer are correct.
 + */
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
 +
 +	fd2 = accept_connection(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
 +	}
 +
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (close_socket((char *)NULL, fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	int fd, rv;
 +
 +	fd = create_server_socket();
 +	if (fd < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	if (fork_client() < 0)
 +		goto done;
 +
 +	if (client_pid == 0) {
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(1);
 +		t_peercred_client();
 +	}
 +
 +	rv = t_peercred_server(fd);
 +
 +	if (wait_client() < 0)
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/unix_cmsg.t	2010-04-21 14:14:46.000000000 +0300
 @@ -23,14 +23,15 @@ run()
  	done
  }
  
 -echo "1..15"
 +echo "1..16"
  
  for desc in \
  	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +	"Receiving sockcred (listening socket has LOCAL_CREDS)" \
 +	"Receiving sockcred (accepted socket has LOCAL_CREDS)" \
 +	"Sending cmsgcred, receiving sockcred" \
 +	"Sending, receiving timestamp" \
 +	"Check LOCAL_PEERCRED socket option"
  do
  	n=`expr ${n} + 1`
  	run ${n} stream "" ${n} "STREAM ${desc}"
 @@ -48,10 +49,10 @@ do
  	run ${n} dgram "" ${i} "DGRAM ${desc}"
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
  
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Mon, 19 Mar 2012 14:37:05 +0200

 Updated unix_cmsg: all flags were converted to 'bool' datatype from
 <stdbool.h>, use macro variables values from <stdlib.h> for return values
 in all exit() calls.
 
 Verified correctness of unix_cmsg on 9.0-STABLE and 10-CURRENT.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/README	2012-03-19 13:42:52.000000000 +0200
 @@ -1,7 +1,7 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
  
  About unix_cmsg
 -================
 +===============
  
  This program is a collection of regression tests for ancillary (control)
  data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 @@ -13,8 +13,8 @@ is correct in received message.  Sometim
  messages to Server.
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
  
  Available options
  =================
 @@ -24,13 +24,13 @@ Available options
  
  -h	Output help message and exit.
  
 --t <socktype>
 +-t socktype
  	Run tests only for the given socket type: "stream" or "dgram".
  	With this option it is possible to run only particular test,
  	not all of them.
  
  -z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 +	should be followed by real control data.  It is not clear whether
  	a sender should give control data in all cases (this is not
  	documented and an arbitrary application can choose anything).
  
 @@ -90,6 +90,13 @@ For SOCK_STREAM sockets:
      message with data and control message with SCM_TIMESTAMP type
      followed by struct timeval{}.
  
 + 6: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use control data for PF_LOCAL sockets, but can be
 +    implemented here.  Client connects to Server.  Both Client and Server
 +    verify that credentials of the peer are correct using LOCAL_PEERCRED
 +    socket option.
 +
  For SOCK_DGRAM sockets:
  ----------------------
  
 @@ -110,7 +117,7 @@ For SOCK_DGRAM sockets:
      structure should contain correct information.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 +
      Server creates datagram socket and set socket option LOCAL_CREDS
      for it.  Client sends one message with data and control message with
      SOCK_CREDS type to Server.  Server should receive one message with
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
 +++ unix_cmsg/unix_cmsg.c	2012-03-19 14:26:40.000000000 +0200
 @@ -27,10 +27,12 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 @@ -38,16 +40,16 @@ __FBSDID("$FreeBSD: src/tools/regression
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
 @@ -68,7 +70,7 @@ __FBSDID("$FreeBSD: src/tools/regression
   *
   * Each function which can block, is run under TIMEOUT, if timeout
   * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * with non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,46 +78,88 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	5
  #endif
  
  #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
  
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_stream1(void);
 +static int	t_sockcred_stream2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_sockcred_dgram(void);
 +static int	t_timestamp(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 +	int		(*func)(void);	/* Pointer to function.	*/
 +	const char	*desc;		/* Test description.	*/
  };
  
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_stream1,
 +	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
 +	},
 +	{
 +	  .func = t_sockcred_stream2,
 +	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
 +
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_dgram,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 +static const char *myname;		/* "SERVER" or "CLIENT" */
  
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 +static bool	debug = false;		/* -d */
 +static bool	no_control_data = false;/* -z */
  
  static u_int	nfailed = 0;		/* Number of failed tests. */
  
 @@ -131,17 +175,11 @@ static char	ipc_message[] = "hello";
  
  static struct sockaddr_un servaddr;	/* Server address. */
  
 -static sigjmp_buf env_alrm;
 -
  static uid_t	my_uid;
  static uid_t	my_euid;
  static gid_t	my_gid;
  static gid_t	my_egid;
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
  static gid_t	my_gids[NGROUPS_MAX];
  static int	my_ngids;
  
 @@ -150,38 +188,35 @@ static pid_t	client_pid;		/* PID of fork
  #define dbgmsg(x)	do {			\
  	if (debug)				\
  	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +} while (0)
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
  /*
   * Output the help message (-h switch).
   */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 +	    getprogname());
 +	if (!verbose)
  		return;
  	fprintf(stderr, "\n Options are:\n\
    -d\t\t\tOutput debugging information\n\
    -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 +  -t socktype\t\tRun test only for socket type: stream or dgram\n\
    -z\t\t\tDo not send real control data if possible\n\n");
  	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
  	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
  /*
 @@ -195,7 +230,7 @@ output(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
 @@ -210,18 +245,16 @@ logmsg(const char *format, ...)
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
  		output("%s: %s\n", myname, buf);
  	else
  		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
  	va_end(ap);
 -
 -	errno = errno_save;		/* Restore errno. */
 +	errno = errno_save;
  }
  
  /*
 @@ -235,33 +268,47 @@ logmsgx(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
  	output("%s: %s\n", myname, buf);
  	va_end(ap);
  }
  
  /*
 - * Run tests from testno1 to testno2.
 + * Run tests for the given socket type.
   */
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, nfailed1, testno2;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 +	output("Running tests for %s sockets:\n", sock_type_str);
  	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
  			++nfailed1;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred, "
 +			    "exiting");
  			return (-1);
  		}
  	}
 @@ -284,181 +331,222 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 +/*
 + * Initialize signals handlers.
 + */
  static void
 -sig_alrm(int signo __unused)
 +sig_init(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	struct sigaction sigact;
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0)
 +		err(EXIT_FAILURE, "sigaction(SIGPIPE)");
  }
  
  /*
 - * Initialize signals handlers.
 + * Output this process UID, GID, groups from getgroups(), EUID and EGID.
   */
  static void
 -sig_init(void)
 +show_my_id(void)
  {
 -	struct sigaction sa;
 +	int i;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	if (!debug)
 +		return;
 +	logmsgx("UID %lu, GID %lu, groups from getgroups():",
 +	    (u_long)my_uid, (u_long)my_gid);
 +	for (i = 0; i < my_ngids; ++i)
 +		logmsgx("  GID %lu", (u_long)my_gids[i]);
 +	logmsgx("EUID %lu, EGID %lu", (u_long)my_euid, (u_long)my_egid);
 +}
 +
 +static int
 +fork_client(void)
 +{
 +	client_pid = fork();
 +	if (client_pid == 0)
 +		myname = "CLIENT";
 +	else if (client_pid == (pid_t)-1) {
 +		logmsg("fork");
 +		return (-1);
 +	}
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 +	u_int testno;
 +	int opt, rv;
 +	bool dgramflag, streamflag;
  
 -	dgramflag = streamflag = 0;
 +	dgramflag = streamflag = false;
  	while ((opt = getopt(argc, argv, "dht:z")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				streamflag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgramflag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			no_control_data = true;
  			break;
  		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 +		testno = 0;
  
 -	if (dgramflag == 0 && streamflag == 0)
 -		dgramflag = streamflag = 1;
 +	if (!dgramflag && !streamflag)
 +		dgramflag = streamflag = true;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (dgramflag && streamflag && testno != 0)
 +		errx(EXIT_FAILURE, "you can use particular test, only "
 +		    "with datagram or stream sockets");
  
  	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
  	my_uid = getuid();
  	my_euid = geteuid();
  	my_gid = getgid();
  	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 +	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
 +	switch (my_ngids) {
  	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 +		err(EXIT_FAILURE, "getgroups");
  		/* NOTREACHED */
  	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		errx(EXIT_FAILURE, "getgroups returned no groups");
  	}
  
  	sig_init();
  
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 -
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	myname = "SERVER";
 +	rv = EXIT_SUCCESS;
 +	show_my_id();
 +	if (streamflag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgramflag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (nfailed > 0 ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +/*
 + * Close socket descriptor, if sock_path is not equal to NULL,
 + * then unlink the given path.
 + */
 +static int
 +close_socket(const char *sock_path, int fd)
 +{
 +	int rv;
 +
 +	if (close(fd) < 0) {
 +		logmsg("close_socket: close");
 +		rv = -1;
 +	} else
 +		rv = 0;
 +	if (sock_path != NULL)
 +		if (unlink(sock_path) < 0) {
 +			logmsg("close_socket: unlink(%s)", sock_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
  /*
   * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 + * bind() it.  Return socket address in *un.  Return file descriptor
   * or -1 if some error occurred.
   */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *un)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("create_socket: socket(PF_LOCAL, %s, 0)",
 +		    sock_type_str);
  		return (-1);
  	}
  
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("create_socket: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
 +
  	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 +		int rv;
 +
 +		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
 +		    myname);
 +		if (rv < 0) {
  			logmsg("create_socket: snprintf failed");
  			goto failed;
  		}
  		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 +			logmsgx("create_socket: too long path name for "
 +			    "given buffer");
  			goto failed;
  		}
 -
 -		memset(addr, 0, sizeof(addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +		if (strlen(sock_path) >= sizeof(un->sun_path)) {
 +			logmsgx("create_socket: too long path name (>= %zu) "
 +			    "for local domain socket", sizeof(un->sun_path));
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 +		memset(un, 0, sizeof(un));
 +		un->sun_family = PF_LOCAL;
 +		strcpy(un->sun_path, sock_path);
 +		un->sun_len = SUN_LEN(un);
 +
 +		if (bind(fd, (struct sockaddr *)un, un->sun_len) < 0) {
  			logmsg("create_socket: bind(%s)", sock_path);
  			goto failed;
  		}
 @@ -473,43 +561,49 @@ failed:
  }
  
  /*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 + * Create server socket.
   */
  static int
  create_server_socket(void)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	int fd;
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	fd = create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr);
 +	if (fd < 0)
 +		return (-1);
 +
 +	if (sock_type == SOCK_STREAM) {
 +		int val;
 +
 +		if (listen(fd, LISTENQ) < 0) {
 +			logmsg("create_server_socket: listen");
 +			goto failed;
 +		}
 +		val = fcntl(fd, F_GETFL, 0);
 +		if (val < 0) {
 +			logmsg("create_server_socket: fcntl(F_GETFL)");
 +			goto failed;
 +		}
 +		if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +			logmsg("create_server_socket: fcntl(F_SETFL)");
 +			goto failed;
 +		}
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	(void)close_socket(serv_sock_path, fd);
 +	return (-1);
  }
  
  /*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 + * Create client socket.
   */
  static int
 -close_socket(const char *sock_path, int fd)
 +create_client_socket(void)
  {
 -	int error = 0;
 -
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 +	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
  }
  
  /*
 @@ -524,7 +618,7 @@ connect_server(int fd)
  	 * If PF_LOCAL listening socket's queue is full, then connect()
  	 * returns ECONNREFUSED immediately, do not need timeout.
  	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 +	if (connect(fd, (struct sockaddr *)&servaddr, servaddr.sun_len) < 0) {
  		logmsg("connect_server: connect(%s)", serv_sock_path);
  		return (-1);
  	}
 @@ -533,34 +627,23 @@ connect_server(int fd)
  }
  
  /*
 - * sendmsg() with timeout.
 + * sendmsg() wrapper.
   */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +send_message(int fd, const struct msghdr *msg, size_t n)
  {
  	ssize_t nsent;
  
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("sending %zu bytes", n));
  
  	nsent = sendmsg(fd, msg, 0);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +		logmsg("send_message: sendmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +		logmsgx("send_message: sendmsg: short send: "
 +		    "%zd of %zu bytes", nsent, n);
  		return (-1);
  	}
  
 @@ -568,63 +651,73 @@ sendmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * accept() with timeout.
 + * accept() wrapper.
   */
  static int
 -accept_timeout(int listenfd)
 +accept_connection(int listenfd)
  {
 -	int fd;
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
  
  	dbgmsg(("accepting connection"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("accept_connection: select");
 +		return (-1);
 +	}
 +	if (rv == 0) {
 +		logmsgx("accept_connection: select timeout");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
  
  	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 -
 -	(void)alarm(0);
 -
  	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 +		logmsg("accept_connection: accept");
  		return (-1);
  	}
  
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("accept_connection: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("accept_connection: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
  	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("accept_connection: close");
 +	return (-1);
  }
  
  /*
 - * recvmsg() with timeout.
 + * recvmsg() wrapper.
   */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +recv_message(int fd, struct msghdr *msg, size_t n)
  {
  	ssize_t nread;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("receiving %zu bytes", n));
  
  	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +		logmsg("recv_message: recvmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +		logmsgx("recv_message: recvmsg: short read: "
 +		    "%zd of %zu bytes", nread, n);
  		return (-1);
  	}
  
 @@ -632,7 +725,7 @@ recvmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * Wait for synchronization message (1 byte) with timeout.
 + * Wait for synchronization message (1 byte).
   */
  static int
  sync_recv(int fd)
 @@ -642,25 +735,13 @@ sync_recv(int fd)
  
  	dbgmsg(("waiting for sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
  		logmsg("sync_recv: read");
  		return (-1);
  	}
 -
  	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
  		return (-1);
  	}
  
 @@ -668,7 +749,7 @@ sync_recv(int fd)
  }
  
  /*
 - * Send synchronization message (1 byte) with timeout.
 + * Send synchronization message (1 byte).
   */
  static int
  sync_send(int fd)
 @@ -677,25 +758,13 @@ sync_send(int fd)
  
  	dbgmsg(("sending sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
  		logmsg("sync_send: write");
  		return (-1);
  	}
 -
  	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
  		return (-1);
  	}
  
 @@ -711,36 +780,29 @@ wait_client(void)
  	int status;
  	pid_t pid;
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("waiting for a client"));
  
  	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
  	if (pid == (pid_t)-1) {
  		logmsg("wait_client: waitpid");
  		return (-1);
  	}
  
  	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("wait_client: exit status of client PID %ld "
 +			    "is %d", (long)client_pid, WEXITSTATUS(status));
  			return (-1);
  		}
  	} else {
  		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 +			logmsgx("wait_client: abnormal termination of client "
 +			    "PID %ld, signal %d%s", (long)client_pid,
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
  		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +			logmsgx("wait_client: termination of client PID %ld, "
 +			    "unknown status", (long)client_pid);
  		return (-1);
  	}
  
 @@ -748,28 +810,27 @@ wait_client(void)
  }
  
  /*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 + * Check whether n GIDs in gids are correct.
   */
  static int
  check_groups(const gid_t *gids, int n)
  {
 +	int i, j, rv;
  	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 +	if (n != my_ngids) {
 +		logmsgx("wrong number of groups %d != %d (returned from "
 +		    "getgroups())", n, my_ngids);
 +		rv = -1;
  	} else
 -		error = 0;
 +		rv = 0;
  	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 +		for (j = 0; j < my_ngids; ++j) {
  			if (gids[i] == my_gids[j]) {
  				if (match[j]) {
  					logmsgx("duplicated GID %lu",
  					    (u_long)gids[i]);
 -					error = -1;
 +					rv = -1;
  				} else
  					match[j] = 1;
  				break;
 @@ -777,15 +838,15 @@ check_groups(const gid_t *gids, int n)
  		}
  		if (j == my_ngids) {
  			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +			rv = -1;
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 +	for (j = 0; j < my_ngids; ++j)
  		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 +			logmsgx("did not receive GID %lu", (u_long)my_gids[j]);
 +			rv = -1;
  		}
 -	return (error);
 +	return (rv);
  }
  
  /*
 @@ -802,16 +863,19 @@ t_cmsgcred_client(u_int n)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(n == 1 || n == 2);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -834,20 +898,16 @@ t_cmsgcred_client(u_int n)
  	for (i = 0; i < n; ++i) {
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
  		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  	}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 -
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd))
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -858,28 +918,27 @@ failed:
  static int
  t_cmsgcred_server(int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 +	const struct cmsgcred *cmcred;
  	u_int i;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 -
 -	controllen = sizeof(control_un.control);
 +	rv = 0;
  
  	for (i = 0; i < 2; ++i) {
  		iov[0].iov_base = buf;
 @@ -890,29 +949,31 @@ t_cmsgcred_server(int fd1)
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 +			logmsgx("#%u control data was truncated, MSG_CTRUNC "
 +			    "flag is on", i);
 +			goto next;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 @@ -921,142 +982,120 @@ t_cmsgcred_server(int fd1)
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
 +			    (u_int)cmptr->cmsg_len,
 +			    CMSG_LEN(sizeof(struct cmsgcred)));
 +			goto next;
  		}
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 +		if (cmcred->cmcred_pid != client_pid) {
  			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 +			    i, (long)cmcred->cmcred_pid, (long)client_pid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 +		if (cmcred->cmcred_uid != my_uid) {
 +			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_uid,
 +			    (u_long)my_uid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_euid != my_euid) {
 +			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_euid,
 +			    (u_long)my_euid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_gid != my_gid) {
 +			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 +		if (cmcred->cmcred_ngroups == 0) {
  			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 +			goto next;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 +		if (cmcred->cmcred_ngroups < 0) {
 +			logmsgx("#%u cmcred_ngroups %d < 0",
 +			    i, cmcred->cmcred_ngroups);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
 +			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 +			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
 +			goto next;
 +		}
 +
 +		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
 +		if (cmcred->cmcred_groups[0] != my_egid) {
 +			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
 +			    "current process)", i,
 +			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
 +			goto next;
 +		}
 +		if (check_groups(cmcred->cmcred_groups,
 +		    cmcred->cmcred_ngroups) < 0) {
 +			logmsgx("#%u cmcred_groups has wrong GIDs", i);
 +			goto next;
 +		}
 +
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
  		}
 -
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
  
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_cmsgcred(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_cmsgcred_client(2);
  	}
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_cmsgcred_server(fd);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1067,20 +1106,23 @@ t_sockcred_client(int type)
  {
  	struct msghdr msg;
  	struct iovec iov[1];
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	if (type == 1)
  		if (sync_recv(fd) < 0)
 -			goto failed_close;
 +			goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1094,19 +1136,15 @@ t_sockcred_client(int type)
  	msg.msg_flags = 0;
  
  	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1119,232 +1157,222 @@ failed:
  static int
  t_sockcred_server(int type, int fd1, u_int n)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
  	u_int i;
 +	int fd2, rv, optval;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	assert(n == 1 || n == 2);
  	assert(type == 0 || type == 1);
  
 +	rv = -2;
 +
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  		if (type == 1) {
  			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 +			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
 +			    sizeof(optval)) < 0) {
 +				logmsg("setsockopt(LOCAL_CREDS) for accepted "
 +				    "socket");
 +				if (errno == ENOPROTOOPT)
 +					rv = -1;
 +				goto done;
  			}
  			if (sync_send(fd2) < 0)
 -				goto failed;
 +				goto done;
  		}
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 +	rv = 0;
  
  	for (i = 0; i < n; ++i) {
  		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 +		iov[0].iov_len = sizeof(buf);
  
  		msg.msg_name = NULL;
  		msg.msg_namelen = 0;
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +			logmsgx("control data was truncated, MSG_CTRUNC flag "
 +			    "is on");
 +			goto next;
  		}
  
 +		dbgmsg(("#%u msg_controllen = %u", i,
 +		    (u_int)msg.msg_controllen));
 +
  		if (i != 0 && sock_type == SOCK_STREAM) {
  			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 +				logmsgx("second message has control data, "
 +				    "this is wrong for stream sockets");
 +				goto next;
  			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
  			continue;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
  
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
 +			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
 +			goto next;
  		}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
  		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 +			logmsgx("#%u sc_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)sockcred->sc_uid,
 +			    (u_long)my_uid);
 +			goto next;
  		}
  		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 +			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)sockcred->sc_euid,
 +			    (u_long)my_euid);
 +			goto next;
  		}
  		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 +			logmsgx("#%u sc_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
  		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 +			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_egid);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups == 0) {
 +			logmsgx("#%u sc_ngroups %d = 0, this is wrong",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups < 0) {
 +			logmsgx("#%u sc_ngroups %d < 0",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
  		}
  		if (sockcred->sc_ngroups > NGROUPS_MAX) {
  			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
  			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 -			}
 +			goto next;
  		}
  
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 +		if (check_groups(sockcred->sc_groups,
 +		    sockcred->sc_ngroups) < 0) {
 +			logmsgx("#%u sc_groups has wrong GIDs", i);
 +			goto next;
  		}
  
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
 +		}
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
 -
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 +done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_sockcred(int type)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
  	assert(type == 0 || type == 1);
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	if (type == 0) {
  		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 +		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
 +		    sizeof(optval)) < 0) {
  			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 +			    sock_type_str);
 +			if (errno == ENOPROTOOPT)
 +				rv = -1;
 +			goto done;
  		}
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_sockcred_client(type);
  	}
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(type, fd, 2);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  static int
 @@ -1368,59 +1396,39 @@ t_sockcred_dgram(void)
  static int
  t_cmsgcred_sockcred(void)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
 +		if (errno == ENOPROTOOPT)
 +			rv = -1;
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_cmsgcred_client(1);
  	}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(0, fd, 1);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1437,13 +1445,16 @@ t_timestamp_client(void)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
 +	int fd, rv;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1454,7 +1465,7 @@ t_timestamp_client(void)
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
  	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 +	    sizeof(struct cmsghdr) : sizeof(control_un.control);
  	msg.msg_flags = 0;
  
  	cmptr = CMSG_FIRSTHDR(&msg);
 @@ -1466,19 +1477,15 @@ t_timestamp_client(void)
  	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
  	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +		goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1490,36 +1497,40 @@ t_timestamp_server(int fd1)
  {
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct timeval)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct timeval *timeval;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 +	iov[0].iov_len = sizeof(buf);
  
  	msg.msg_name = NULL;
  	msg.msg_namelen = 0;
  	msg.msg_iov = iov;
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;;
 +	msg.msg_controllen = sizeof(control_un.control);
  	msg.msg_flags = 0;
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +		rv = -2;
 +		goto done;
 +	}
  
 -	error = -1;
 +	rv = -1;
  
  	if (msg.msg_flags & MSG_CTRUNC) {
  		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 @@ -1527,12 +1538,13 @@ t_timestamp_server(int fd1)
  	}
  
  	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 +		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
 +		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
  		goto done;
  	}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +	cmptr = CMSG_FIRSTHDR(&msg);
 +	if (cmptr == NULL) {
  		logmsgx("CMSG_FIRSTHDR is NULL");
  		goto done;
  	}
 @@ -1551,80 +1563,202 @@ t_timestamp_server(int fd1)
  	}
  
  	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
 +		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
  		goto done;
  	}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +	timeval = (struct timeval *)CMSG_DATA(cmptr);
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 +	dbgmsg(("timeval tv_sec %"PRIdMAX", tv_usec %"PRIdMAX,
  	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 +	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +		logmsgx("control data has extra header, this is wrong");
  		goto done;
  	}
  
 -	error = 0;
 -
 +	rv = 0;
  done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_timestamp(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_timestamp_client();
  	}
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_timestamp_server(fd);
  
  	if (wait_client() < 0)
 +		rv = -2;
 +done:
 +	if (close_socket(serv_sock_path, fd) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +/*
 + * Verify that xucred structure has correct credentials.
 + */
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
 +{
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu (sizeof(struct xucred))",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != my_euid) {
 +		logmsgx("cr_uid %lu != %lu (EUID of current process)",
 +		   (u_long)xucred->cr_uid, (u_long)my_euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("cr_ngroups = 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("cr_ngroups < 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > NGROUPS_MAX) {
 +		logmsgx("cr_ngroups %hu > %u (NGROUPS_MAX)",
 +		    xucred->cr_ngroups, NGROUPS_MAX);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != my_egid) {
 +		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
 +		    (u_long)xucred->cr_groups[0], (u_long)my_egid);
 +		return (-1);
 +	}
 +	if (check_groups(xucred->cr_groups, xucred->cr_ngroups) < 0) {
 +		logmsgx("cr_groups has wrong GIDs");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Connect to server and set LOCAL_PEERCRED socket option for connected
 + * socket.  Verify that credentials of the peer are correct.
 + */
 +static void
 +t_peercred_client(void)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +	if (connect_server(fd) < 0)
 +		goto done;
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		goto done;
  	}
 -	return (error);
  
 +	if (check_xucred(&xucred, len) < 0)
 +		goto done;
 +
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 +	_exit(rv);
 +}
 +
 +/*
 + * Accept connection from client and set LOCAL_PEERCRED socket option for
 + * connected socket.  Verify that credentials of the peer are correct.
 + */
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
 +
 +	fd2 = accept_connection(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
 +	}
 +
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (close_socket((char *)NULL, fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	int fd, rv;
 +
 +	fd = create_server_socket();
 +	if (fd < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	if (fork_client() < 0)
 +		goto done;
 +
 +	if (client_pid == 0) {
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
 +		t_peercred_client();
 +	}
 +
 +	rv = t_peercred_server(fd);
 +
 +	if (wait_client() < 0)
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/unix_cmsg.t	2012-03-19 13:42:52.000000000 +0200
 @@ -23,14 +23,15 @@ run()
  	done
  }
  
 -echo "1..15"
 +echo "1..16"
  
  for desc in \
  	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +	"Receiving sockcred (listening socket has LOCAL_CREDS)" \
 +	"Receiving sockcred (accepted socket has LOCAL_CREDS)" \
 +	"Sending cmsgcred, receiving sockcred" \
 +	"Sending, receiving timestamp" \
 +	"Check LOCAL_PEERCRED socket option"
  do
  	n=`expr ${n} + 1`
  	run ${n} stream "" ${n} "STREAM ${desc}"
 @@ -48,10 +49,10 @@ do
  	run ${n} dgram "" ${i} "DGRAM ${desc}"
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
  
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Mon, 1 Oct 2012 14:20:11 +0300

 Updated unix_cmsg: all assert() were removed.
 
 Verified correctness of unix_cmsg on 9.1-PRERELEASE and 10-CURRENT.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/README	2012-10-01 13:47:51.000000000 +0300
 @@ -1,7 +1,7 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.1 2006/05/29 18:40:55 maxim Exp $
  
  About unix_cmsg
 -================
 +===============
  
  This program is a collection of regression tests for ancillary (control)
  data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 @@ -13,8 +13,8 @@ is correct in received message.  Sometim
  messages to Server.
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
  
  Available options
  =================
 @@ -24,13 +24,13 @@ Available options
  
  -h	Output help message and exit.
  
 --t <socktype>
 +-t socktype
  	Run tests only for the given socket type: "stream" or "dgram".
  	With this option it is possible to run only particular test,
  	not all of them.
  
  -z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 +	should be followed by real control data.  It is not clear whether
  	a sender should give control data in all cases (this is not
  	documented and an arbitrary application can choose anything).
  
 @@ -90,6 +90,13 @@ For SOCK_STREAM sockets:
      message with data and control message with SCM_TIMESTAMP type
      followed by struct timeval{}.
  
 + 6: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use control data for PF_LOCAL sockets, but can be
 +    implemented here.  Client connects to Server.  Both Client and Server
 +    verify that credentials of the peer are correct using LOCAL_PEERCRED
 +    socket option.
 +
  For SOCK_DGRAM sockets:
  ----------------------
  
 @@ -110,7 +117,7 @@ For SOCK_DGRAM sockets:
      structure should contain correct information.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 +
      Server creates datagram socket and set socket option LOCAL_CREDS
      for it.  Client sends one message with data and control message with
      SOCK_CREDS type to Server.  Server should receive one message with
 @@ -124,4 +131,4 @@ For SOCK_DGRAM sockets:
      message with SCM_TIMESTAMP type followed by struct timeval{}.
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2006-05-31 11:10:34.000000000 +0300
 +++ unix_cmsg/unix_cmsg.c	2012-10-01 13:49:59.000000000 +0300
 @@ -27,27 +27,28 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.2 2006/05/31 08:10:34 maxim Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
 @@ -68,7 +69,7 @@ __FBSDID("$FreeBSD: src/tools/regression
   *
   * Each function which can block, is run under TIMEOUT, if timeout
   * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * with non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,46 +77,88 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	5
  #endif
  
  #define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
  
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_stream1(void);
 +static int	t_sockcred_stream2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_sockcred_dgram(void);
 +static int	t_timestamp(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 +	int		(*func)(void);	/* Pointer to function.	*/
 +	const char	*desc;		/* Test description.	*/
  };
  
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_stream1,
 +	  .desc = "Receiving sockcred (listening socket has LOCAL_CREDS)"
 +	},
 +	{
 +	  .func = t_sockcred_stream2,
 +	  .desc = "Receiving sockcred (accepted socket has LOCAL_CREDS)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
 +
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_dgram,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timestamp,
 +	  .desc = "Sending, receiving timestamp"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 +static const char *myname;		/* "SERVER" or "CLIENT" */
  
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 +static bool	debug = false;		/* -d */
 +static bool	no_control_data = false;/* -z */
  
  static u_int	nfailed = 0;		/* Number of failed tests. */
  
 @@ -131,17 +174,11 @@ static char	ipc_message[] = "hello";
  
  static struct sockaddr_un servaddr;	/* Server address. */
  
 -static sigjmp_buf env_alrm;
 -
  static uid_t	my_uid;
  static uid_t	my_euid;
  static gid_t	my_gid;
  static gid_t	my_egid;
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
  static gid_t	my_gids[NGROUPS_MAX];
  static int	my_ngids;
  
 @@ -150,38 +187,35 @@ static pid_t	client_pid;		/* PID of fork
  #define dbgmsg(x)	do {			\
  	if (debug)				\
  	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +} while (0)
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
  /*
   * Output the help message (-h switch).
   */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	fprintf(stderr, "usage: %s [-dhz] [-t socktype] [testno]\n",
 +	    getprogname());
 +	if (!verbose)
  		return;
  	fprintf(stderr, "\n Options are:\n\
    -d\t\t\tOutput debugging information\n\
    -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 +  -t socktype\t\tRun test only for socket type: stream or dgram\n\
    -z\t\t\tDo not send real control data if possible\n\n");
  	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_stream_tbl[i].desc);
  	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		fprintf(stderr, "   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
  /*
 @@ -195,7 +229,7 @@ output(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
 @@ -210,18 +244,16 @@ logmsg(const char *format, ...)
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
  		output("%s: %s\n", myname, buf);
  	else
  		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
  	va_end(ap);
 -
 -	errno = errno_save;		/* Restore errno. */
 +	errno = errno_save;
  }
  
  /*
 @@ -235,33 +267,47 @@ logmsgx(const char *format, ...)
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
  	output("%s: %s\n", myname, buf);
  	va_end(ap);
  }
  
  /*
 - * Run tests from testno1 to testno2.
 + * Run tests for the given socket type.
   */
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, nfailed1, testno2;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 +	output("Running tests for %s sockets:\n", sock_type_str);
  	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
  			++nfailed1;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred, "
 +			    "exiting");
  			return (-1);
  		}
  	}
 @@ -284,181 +330,222 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 +/*
 + * Initialize signals handlers.
 + */
  static void
 -sig_alrm(int signo __unused)
 +sig_init(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	struct sigaction sigact;
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0)
 +		err(EXIT_FAILURE, "sigaction(SIGPIPE)");
  }
  
  /*
 - * Initialize signals handlers.
 + * Output this process UID, GID, groups from getgroups(), EUID and EGID.
   */
  static void
 -sig_init(void)
 +show_my_id(void)
  {
 -	struct sigaction sa;
 +	int i;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	if (!debug)
 +		return;
 +	logmsgx("UID %lu, GID %lu, groups from getgroups():",
 +	    (u_long)my_uid, (u_long)my_gid);
 +	for (i = 0; i < my_ngids; ++i)
 +		logmsgx("  GID %lu", (u_long)my_gids[i]);
 +	logmsgx("EUID %lu, EGID %lu", (u_long)my_euid, (u_long)my_egid);
 +}
 +
 +static int
 +fork_client(void)
 +{
 +	client_pid = fork();
 +	if (client_pid == 0)
 +		myname = "CLIENT";
 +	else if (client_pid == (pid_t)-1) {
 +		logmsg("fork");
 +		return (-1);
 +	}
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 +	u_int testno;
 +	int opt, rv;
 +	bool dgramflag, streamflag;
  
 -	dgramflag = streamflag = 0;
 +	dgramflag = streamflag = false;
  	while ((opt = getopt(argc, argv, "dht:z")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				streamflag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgramflag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			no_control_data = true;
  			break;
  		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 +		testno = 0;
  
 -	if (dgramflag == 0 && streamflag == 0)
 -		dgramflag = streamflag = 1;
 +	if (!dgramflag && !streamflag)
 +		dgramflag = streamflag = true;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (dgramflag && streamflag && testno != 0)
 +		errx(EXIT_FAILURE, "you can use particular test, only "
 +		    "with datagram or stream sockets");
  
  	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
  	my_uid = getuid();
  	my_euid = geteuid();
  	my_gid = getgid();
  	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 +	my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids);
 +	switch (my_ngids) {
  	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 +		err(EXIT_FAILURE, "getgroups");
  		/* NOTREACHED */
  	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		errx(EXIT_FAILURE, "getgroups returned no groups");
  	}
  
  	sig_init();
  
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 -
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	myname = "SERVER";
 +	rv = EXIT_SUCCESS;
 +	show_my_id();
 +	if (streamflag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgramflag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (nfailed > 0 ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +/*
 + * Close socket descriptor, if sock_path is not equal to NULL,
 + * then unlink the given path.
 + */
 +static int
 +close_socket(const char *sock_path, int fd)
 +{
 +	int rv;
 +
 +	if (close(fd) < 0) {
 +		logmsg("close_socket: close");
 +		rv = -1;
 +	} else
 +		rv = 0;
 +	if (sock_path != NULL)
 +		if (unlink(sock_path) < 0) {
 +			logmsg("close_socket: unlink(%s)", sock_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
  /*
   * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 + * bind() it.  Return socket address in *un.  Return file descriptor
   * or -1 if some error occurred.
   */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *un)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("create_socket: socket(PF_LOCAL, %s, 0)",
 +		    sock_type_str);
  		return (-1);
  	}
  
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("create_socket: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
 +
  	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 +		int rv;
 +
 +		rv = snprintf(sock_path, sock_path_len, "%s/%s", tempdir,
 +		    myname);
 +		if (rv < 0) {
  			logmsg("create_socket: snprintf failed");
  			goto failed;
  		}
  		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 +			logmsgx("create_socket: too long path name for "
 +			    "given buffer");
  			goto failed;
  		}
 -
 -		memset(addr, 0, sizeof(addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +		if (strlen(sock_path) >= sizeof(un->sun_path)) {
 +			logmsgx("create_socket: too long path name (>= %zu) "
 +			    "for local domain socket", sizeof(un->sun_path));
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 +		memset(un, 0, sizeof(un));
 +		un->sun_family = PF_LOCAL;
 +		strcpy(un->sun_path, sock_path);
 +		un->sun_len = SUN_LEN(un);
 +
 +		if (bind(fd, (struct sockaddr *)un, un->sun_len) < 0) {
  			logmsg("create_socket: bind(%s)", sock_path);
  			goto failed;
  		}
 @@ -473,43 +560,49 @@ failed:
  }
  
  /*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 + * Create server socket.
   */
  static int
  create_server_socket(void)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	int fd;
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	fd = create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr);
 +	if (fd < 0)
 +		return (-1);
 +
 +	if (sock_type == SOCK_STREAM) {
 +		int val;
 +
 +		if (listen(fd, LISTENQ) < 0) {
 +			logmsg("create_server_socket: listen");
 +			goto failed;
 +		}
 +		val = fcntl(fd, F_GETFL, 0);
 +		if (val < 0) {
 +			logmsg("create_server_socket: fcntl(F_GETFL)");
 +			goto failed;
 +		}
 +		if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +			logmsg("create_server_socket: fcntl(F_SETFL)");
 +			goto failed;
 +		}
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	(void)close_socket(serv_sock_path, fd);
 +	return (-1);
  }
  
  /*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 + * Create client socket.
   */
  static int
 -close_socket(const char *sock_path, int fd)
 +create_client_socket(void)
  {
 -	int error = 0;
 -
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 +	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
  }
  
  /*
 @@ -524,7 +617,7 @@ connect_server(int fd)
  	 * If PF_LOCAL listening socket's queue is full, then connect()
  	 * returns ECONNREFUSED immediately, do not need timeout.
  	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 +	if (connect(fd, (struct sockaddr *)&servaddr, servaddr.sun_len) < 0) {
  		logmsg("connect_server: connect(%s)", serv_sock_path);
  		return (-1);
  	}
 @@ -533,34 +626,23 @@ connect_server(int fd)
  }
  
  /*
 - * sendmsg() with timeout.
 + * sendmsg() wrapper.
   */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +send_message(int fd, const struct msghdr *msg, size_t n)
  {
  	ssize_t nsent;
  
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("sending %zu bytes", n));
  
  	nsent = sendmsg(fd, msg, 0);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +		logmsg("send_message: sendmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +		logmsgx("send_message: sendmsg: short send: "
 +		    "%zd of %zu bytes", nsent, n);
  		return (-1);
  	}
  
 @@ -568,63 +650,73 @@ sendmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * accept() with timeout.
 + * accept() wrapper.
   */
  static int
 -accept_timeout(int listenfd)
 +accept_connection(int listenfd)
  {
 -	int fd;
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
  
  	dbgmsg(("accepting connection"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("accept_connection: select");
 +		return (-1);
 +	}
 +	if (rv == 0) {
 +		logmsgx("accept_connection: select timeout");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
  
  	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 -
 -	(void)alarm(0);
 -
  	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 +		logmsg("accept_connection: accept");
  		return (-1);
  	}
  
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("accept_connection: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("accept_connection: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
  	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("accept_connection: close");
 +	return (-1);
  }
  
  /*
 - * recvmsg() with timeout.
 + * recvmsg() wrapper.
   */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +recv_message(int fd, struct msghdr *msg, size_t n)
  {
  	ssize_t nread;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("receiving %zu bytes", n));
  
  	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +		logmsg("recv_message: recvmsg");
  		return (-1);
  	}
 -
  	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +		logmsgx("recv_message: recvmsg: short read: "
 +		    "%zd of %zu bytes", nread, n);
  		return (-1);
  	}
  
 @@ -632,7 +724,7 @@ recvmsg_timeout(int fd, struct msghdr *m
  }
  
  /*
 - * Wait for synchronization message (1 byte) with timeout.
 + * Wait for synchronization message (1 byte).
   */
  static int
  sync_recv(int fd)
 @@ -642,25 +734,13 @@ sync_recv(int fd)
  
  	dbgmsg(("waiting for sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
  	if (nread < 0) {
  		logmsg("sync_recv: read");
  		return (-1);
  	}
 -
  	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +		logmsgx("sync_recv: read: short read: %zd of 1 byte", nread);
  		return (-1);
  	}
  
 @@ -668,7 +748,7 @@ sync_recv(int fd)
  }
  
  /*
 - * Send synchronization message (1 byte) with timeout.
 + * Send synchronization message (1 byte).
   */
  static int
  sync_send(int fd)
 @@ -677,25 +757,13 @@ sync_send(int fd)
  
  	dbgmsg(("sending sync message"));
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 -
  	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
  	if (nsent < 0) {
  		logmsg("sync_send: write");
  		return (-1);
  	}
 -
  	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +		logmsgx("sync_send: write: short write: %zd of 1 byte", nsent);
  		return (-1);
  	}
  
 @@ -711,36 +779,29 @@ wait_client(void)
  	int status;
  	pid_t pid;
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	dbgmsg(("waiting for a client"));
  
  	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
  	if (pid == (pid_t)-1) {
  		logmsg("wait_client: waitpid");
  		return (-1);
  	}
  
  	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("wait_client: exit status of client PID %ld "
 +			    "is %d", (long)client_pid, WEXITSTATUS(status));
  			return (-1);
  		}
  	} else {
  		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 +			logmsgx("wait_client: abnormal termination of client "
 +			    "PID %ld, signal %d%s", (long)client_pid,
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
  		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +			logmsgx("wait_client: termination of client PID %ld, "
 +			    "unknown status", (long)client_pid);
  		return (-1);
  	}
  
 @@ -748,28 +809,27 @@ wait_client(void)
  }
  
  /*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 + * Check whether n GIDs in gids are correct.
   */
  static int
  check_groups(const gid_t *gids, int n)
  {
 +	int i, j, rv;
  	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 +	if (n != my_ngids) {
 +		logmsgx("wrong number of groups %d != %d (returned from "
 +		    "getgroups())", n, my_ngids);
 +		rv = -1;
  	} else
 -		error = 0;
 +		rv = 0;
  	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 +		for (j = 0; j < my_ngids; ++j) {
  			if (gids[i] == my_gids[j]) {
  				if (match[j]) {
  					logmsgx("duplicated GID %lu",
  					    (u_long)gids[i]);
 -					error = -1;
 +					rv = -1;
  				} else
  					match[j] = 1;
  				break;
 @@ -777,15 +837,15 @@ check_groups(const gid_t *gids, int n)
  		}
  		if (j == my_ngids) {
  			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +			rv = -1;
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 +	for (j = 0; j < my_ngids; ++j)
  		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 +			logmsgx("did not receive GID %lu", (u_long)my_gids[j]);
 +			rv = -1;
  		}
 -	return (error);
 +	return (rv);
  }
  
  /*
 @@ -802,16 +862,17 @@ t_cmsgcred_client(u_int n)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
 -	assert(n == 1 || n == 2);
 +	rv = EXIT_FAILURE;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -834,20 +895,16 @@ t_cmsgcred_client(u_int n)
  	for (i = 0; i < n; ++i) {
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
  		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  	}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 -
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd))
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -858,28 +915,27 @@ failed:
  static int
  t_cmsgcred_server(int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 +	const struct cmsgcred *cmcred;
  	u_int i;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 -
 -	controllen = sizeof(control_un.control);
 +	rv = 0;
  
  	for (i = 0; i < 2; ++i) {
  		iov[0].iov_base = buf;
 @@ -890,29 +946,31 @@ t_cmsgcred_server(int fd1)
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 +			logmsgx("#%u control data was truncated, MSG_CTRUNC "
 +			    "flag is on", i);
 +			goto next;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
  		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 @@ -921,142 +979,120 @@ t_cmsgcred_server(int fd1)
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(sizeof(struct cmsgcred))", i,
 +			    (u_int)cmptr->cmsg_len,
 +			    CMSG_LEN(sizeof(struct cmsgcred)));
 +			goto next;
  		}
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +		cmcred = (struct cmsgcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 +		if (cmcred->cmcred_pid != client_pid) {
  			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 +			    i, (long)cmcred->cmcred_pid, (long)client_pid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 +		if (cmcred->cmcred_uid != my_uid) {
 +			logmsgx("#%u cmcred_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_uid,
 +			    (u_long)my_uid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_euid != my_euid) {
 +			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_euid,
 +			    (u_long)my_euid);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_gid != my_gid) {
 +			logmsgx("#%u cmcred_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)cmcred->cmcred_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 +		if (cmcred->cmcred_ngroups == 0) {
  			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 +			goto next;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 +		if (cmcred->cmcred_ngroups < 0) {
 +			logmsgx("#%u cmcred_ngroups %d < 0",
 +			    i, cmcred->cmcred_ngroups);
 +			goto next;
 +		}
 +		if (cmcred->cmcred_ngroups > NGROUPS_MAX) {
 +			logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 +			    i, cmcred->cmcred_ngroups, NGROUPS_MAX);
 +			goto next;
 +		}
 +
 +		dbgmsg(("#%u cmcred_ngroups = %d", i, cmcred->cmcred_ngroups));
 +		if (cmcred->cmcred_groups[0] != my_egid) {
 +			logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of "
 +			    "current process)", i,
 +			    (u_long)cmcred->cmcred_groups[0], (u_long)my_egid);
 +			goto next;
 +		}
 +		if (check_groups(cmcred->cmcred_groups,
 +		    cmcred->cmcred_ngroups) < 0) {
 +			logmsgx("#%u cmcred_groups has wrong GIDs", i);
 +			goto next;
 +		}
 +
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
  		}
 -
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
  
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_cmsgcred(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_cmsgcred_client(2);
  	}
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_cmsgcred_server(fd);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1067,20 +1103,21 @@ t_sockcred_client(int type)
  {
  	struct msghdr msg;
  	struct iovec iov[1];
 -	int fd;
  	u_int i;
 +	int fd, rv;
  
 -	assert(type == 0 || type == 1);
 +	rv = EXIT_FAILURE;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	if (type == 1)
  		if (sync_recv(fd) < 0)
 -			goto failed_close;
 +			goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1094,19 +1131,15 @@ t_sockcred_client(int type)
  	msg.msg_flags = 0;
  
  	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +		if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +			goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1119,232 +1152,217 @@ failed:
  static int
  t_sockcred_server(int type, int fd1, u_int n)
  {
 -	char buf[IPC_MESSAGE_SIZE];
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
  	u_int i;
 +	int fd2, rv, optval;
 +	char buf[IPC_MESSAGE_SIZE];
  
 -	assert(n == 1 || n == 2);
 -	assert(type == 0 || type == 1);
 +	rv = -2;
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  		if (type == 1) {
  			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 +			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval,
 +			    sizeof(optval)) < 0) {
 +				logmsg("setsockopt(LOCAL_CREDS) for accepted "
 +				    "socket");
 +				if (errno == ENOPROTOOPT)
 +					rv = -1;
 +				goto done;
  			}
  			if (sync_send(fd2) < 0)
 -				goto failed;
 +				goto done;
  		}
  	} else
  		fd2 = fd1;
  
 -	error = 0;
 +	rv = 0;
  
  	for (i = 0; i < n; ++i) {
  		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 +		iov[0].iov_len = sizeof(buf);
  
  		msg.msg_name = NULL;
  		msg.msg_namelen = 0;
  		msg.msg_iov = iov;
  		msg.msg_iovlen = 1;
  		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 +		msg.msg_controllen = sizeof(control_un.control);
  		msg.msg_flags = 0;
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +		if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
  		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +			logmsgx("control data was truncated, MSG_CTRUNC flag "
 +			    "is on");
 +			goto next;
  		}
  
 +		dbgmsg(("#%u msg_controllen = %u", i,
 +		    (u_int)msg.msg_controllen));
 +
  		if (i != 0 && sock_type == SOCK_STREAM) {
  			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 +				logmsgx("second message has control data, "
 +				    "this is wrong for stream sockets");
 +				goto next;
  			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
  			continue;
  		}
  
  		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +			logmsgx("#%u msg_controllen %u < %zu "
 +			    "(sizeof(struct cmsghdr))", i,
 +			    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
 +			goto next;
  		}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +		cmptr = CMSG_FIRSTHDR(&msg);
 +		if (cmptr == NULL) {
  			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +			goto next;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +		dbgmsg(("#%u cmsg_len = %u", i, (u_int)cmptr->cmsg_len));
  
  		if (cmptr->cmsg_level != SOL_SOCKET) {
  			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
  			    cmptr->cmsg_level);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_type != SCM_CREDS) {
  			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
  			    cmptr->cmsg_type);
 -			goto next_error;
 +			goto next;
  		}
  
  		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			logmsgx("#%u cmsg_len %u != %zu "
 +			    "(CMSG_LEN(SOCKCREDSIZE(1)))", i,
 +			    (u_int)cmptr->cmsg_len, CMSG_LEN(SOCKCREDSIZE(1)));
 +			goto next;
  		}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +		sockcred = (struct sockcred *)CMSG_DATA(cmptr);
  
 -		error2 = 0;
  		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 +			logmsgx("#%u sc_uid %lu != %lu (UID of current "
 +			    "process)", i, (u_long)sockcred->sc_uid,
 +			    (u_long)my_uid);
 +			goto next;
  		}
  		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 +			logmsgx("#%u sc_euid %lu != %lu (EUID of current "
 +			    "process)", i, (u_long)sockcred->sc_euid,
 +			    (u_long)my_euid);
 +			goto next;
  		}
  		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 +			logmsgx("#%u sc_gid %lu != %lu (GID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_gid);
 +			goto next;
  		}
  		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 +			logmsgx("#%u sc_egid %lu != %lu (EGID of current "
 +			    "process)", i, (u_long)sockcred->sc_gid,
 +			    (u_long)my_egid);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups == 0) {
 +			logmsgx("#%u sc_ngroups %d = 0, this is wrong",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
 +		}
 +		if (sockcred->sc_ngroups < 0) {
 +			logmsgx("#%u sc_ngroups %d < 0",
 +			    i, sockcred->sc_ngroups);
 +			goto next;
  		}
  		if (sockcred->sc_ngroups > NGROUPS_MAX) {
  			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
  			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 -			}
 +			goto next;
  		}
  
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 +		if (check_groups(sockcred->sc_groups,
 +		    sockcred->sc_ngroups) < 0) {
 +			logmsgx("#%u sc_groups has wrong GIDs", i);
 +			goto next;
  		}
  
 +		if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +			logmsgx("#%u control data has extra header, "
 +			    "this is wrong", i);
 +			goto next;
 +		}
  		continue;
 -next_error:
 -		error = -1;
 +next:
 +		rv = -1;
  	}
 -
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 +done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_sockcred(int type)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
 -	assert(type == 0 || type == 1);
 -
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	if (type == 0) {
  		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 +		if (setsockopt(fd, 0, LOCAL_CREDS, &optval,
 +		    sizeof(optval)) < 0) {
  			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 +			    sock_type_str);
 +			if (errno == ENOPROTOOPT)
 +				rv = -1;
 +			goto done;
  		}
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_sockcred_client(type);
  	}
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(type, fd, 2);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  static int
 @@ -1368,59 +1386,39 @@ t_sockcred_dgram(void)
  static int
  t_cmsgcred_sockcred(void)
  {
 -	int error, fd, optval;
 +	int fd, rv, optval;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
  	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof(optval)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS) for %s socket", sock_type_str);
 +		if (errno == ENOPROTOOPT)
 +			rv = -1;
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_cmsgcred_client(1);
  	}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_sockcred_server(0, fd, 1);
  
  	if (wait_client() < 0)
 -		goto failed;
 -
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 -
 -failed:
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
  
  /*
 @@ -1437,13 +1435,16 @@ t_timestamp_client(void)
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
 -	int fd;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
  
 -	if ((fd = create_unbound_socket()) < 0)
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
  	if (connect_server(fd) < 0)
 -		goto failed_close;
 +		goto done;
  
  	iov[0].iov_base = ipc_message;
  	iov[0].iov_len = IPC_MESSAGE_SIZE;
 @@ -1454,7 +1455,7 @@ t_timestamp_client(void)
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
  	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 +	    sizeof(struct cmsghdr) : sizeof(control_un.control);
  	msg.msg_flags = 0;
  
  	cmptr = CMSG_FIRSTHDR(&msg);
 @@ -1466,19 +1467,15 @@ t_timestamp_client(void)
  	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
  	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 -
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 -
 -	_exit(0);
 -
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (send_message(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 +		goto done;
  
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 -	_exit(1);
 +	_exit(rv);
  }
  
  /*
 @@ -1490,36 +1487,40 @@ t_timestamp_server(int fd1)
  {
  	union {
  		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 +		char	control[CMSG_SPACE(sizeof(struct timeval)) +
 +				EXTRA_CMSG_SPACE];
  	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
  	struct msghdr msg;
  	struct iovec iov[1];
  	struct cmsghdr *cmptr;
  	const struct timeval *timeval;
 +	int fd2, rv;
 +	char buf[IPC_MESSAGE_SIZE];
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 +		fd2 = accept_connection(fd1);
 +		if (fd2 < 0)
  			return (-2);
  	} else
  		fd2 = fd1;
  
  	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 +	iov[0].iov_len = sizeof(buf);
  
  	msg.msg_name = NULL;
  	msg.msg_namelen = 0;
  	msg.msg_iov = iov;
  	msg.msg_iovlen = 1;
  	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;;
 +	msg.msg_controllen = sizeof(control_un.control);
  	msg.msg_flags = 0;
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	if (recv_message(fd2, &msg, sizeof(buf)) < 0) {
 +		rv = -2;
 +		goto done;
 +	}
  
 -	error = -1;
 +	rv = -1;
  
  	if (msg.msg_flags & MSG_CTRUNC) {
  		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 @@ -1527,12 +1528,13 @@ t_timestamp_server(int fd1)
  	}
  
  	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 +		logmsgx("msg_controllen %u < %zu (sizeof(struct cmsghdr))",
 +		    (u_int)msg.msg_controllen, sizeof(struct cmsghdr));
  		goto done;
  	}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 +	cmptr = CMSG_FIRSTHDR(&msg);
 +	if (cmptr == NULL) {
  		logmsgx("CMSG_FIRSTHDR is NULL");
  		goto done;
  	}
 @@ -1551,80 +1553,202 @@ t_timestamp_server(int fd1)
  	}
  
  	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +		logmsgx("cmsg_len %u != %zu (CMSG_LEN(sizeof(struct timeval))",
 +		    (u_int)cmptr->cmsg_len, CMSG_LEN(sizeof(struct timeval)));
  		goto done;
  	}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +	timeval = (struct timeval *)CMSG_DATA(cmptr);
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 +	dbgmsg(("timeval tv_sec %"PRIdMAX", tv_usec %"PRIdMAX,
  	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 +	if (CMSG_NXTHDR(&msg, cmptr) != NULL) {
 +		logmsgx("control data has extra header, this is wrong");
  		goto done;
  	}
  
 -	error = 0;
 -
 +	rv = 0;
  done:
  	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 -
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +		if (close_socket((char *)NULL, fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
  t_timestamp(void)
  {
 -	int error, fd;
 +	int fd, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	fd = create_server_socket();
 +	if (fd < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (fork_client() < 0)
 +		goto done;
  
  	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
  		t_timestamp_client();
  	}
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = t_timestamp_server(fd);
  
  	if (wait_client() < 0)
 +		rv = -2;
 +done:
 +	if (close_socket(serv_sock_path, fd) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +/*
 + * Verify that xucred structure has correct credentials.
 + */
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
 +{
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu (sizeof(struct xucred))",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("cr_version %u != %d (XUCRED_VERSION)",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != my_euid) {
 +		logmsgx("cr_uid %lu != %lu (EUID of current process)",
 +		   (u_long)xucred->cr_uid, (u_long)my_euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("cr_ngroups = 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("cr_ngroups < 0, this is wrong");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > NGROUPS_MAX) {
 +		logmsgx("cr_ngroups %hu > %u (NGROUPS_MAX)",
 +		    xucred->cr_ngroups, NGROUPS_MAX);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != my_egid) {
 +		logmsgx("cr_groups[0] %lu != %lu (EGID of current process)",
 +		    (u_long)xucred->cr_groups[0], (u_long)my_egid);
 +		return (-1);
 +	}
 +	if (check_groups(xucred->cr_groups, xucred->cr_ngroups) < 0) {
 +		logmsgx("cr_groups has wrong GIDs");
 +		return (-1);
 +	}
 +	return (0);
 +}
 +
 +/*
 + * Connect to server and set LOCAL_PEERCRED socket option for connected
 + * socket.  Verify that credentials of the peer are correct.
 + */
 +static void
 +t_peercred_client(void)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd, rv;
 +
 +	rv = EXIT_FAILURE;
 +
 +	fd = create_client_socket();
 +	if (fd < 0)
  		goto failed;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +	if (connect_server(fd) < 0)
 +		goto done;
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		goto done;
  	}
 -	return (error);
  
 +	if (check_xucred(&xucred, len) < 0)
 +		goto done;
 +
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (close_socket((char *)NULL, fd) < 0)
 +		rv = EXIT_FAILURE;
  failed:
 +	_exit(rv);
 +}
 +
 +/*
 + * Accept connection from client and set LOCAL_PEERCRED socket option for
 + * connected socket.  Verify that credentials of the peer are correct.
 + */
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
 +
 +	fd2 = accept_connection(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
 +	}
 +
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (close_socket((char *)NULL, fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	int fd, rv;
 +
 +	fd = create_server_socket();
 +	if (fd < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	if (fork_client() < 0)
 +		goto done;
 +
 +	if (client_pid == 0) {
 +		if (close_socket((char *)NULL, fd) < 0)
 +			_exit(EXIT_FAILURE);
 +		t_peercred_client();
 +	}
 +
 +	rv = t_peercred_server(fd);
 +
 +	if (wait_client() < 0)
 +		rv = -2;
 +done:
  	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +		rv = -2;
 +	return (rv);
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2006-05-29 21:40:55.000000000 +0300
 +++ unix_cmsg/unix_cmsg.t	2012-10-01 13:47:17.000000000 +0300
 @@ -23,14 +23,15 @@ run()
  	done
  }
  
 -echo "1..15"
 +echo "1..16"
  
  for desc in \
  	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +	"Receiving sockcred (listening socket has LOCAL_CREDS)" \
 +	"Receiving sockcred (accepted socket has LOCAL_CREDS)" \
 +	"Sending cmsgcred, receiving sockcred" \
 +	"Sending, receiving timestamp" \
 +	"Check LOCAL_PEERCRED socket option"
  do
  	n=`expr ${n} + 1`
  	run ${n} stream "" ${n} "STREAM ${desc}"
 @@ -48,10 +49,10 @@ do
  	run ${n} dgram "" ${i} "DGRAM ${desc}"
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 +run 11 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 +run 12 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 13 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
  
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +run 14 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 +run 15 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 +run 16 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Thu, 7 Feb 2013 16:16:31 +0200

 Completely redesigned unix_cmsg with improved logic.
 
 Details in README.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/README	2013-02-07 15:56:03.000000000 +0200
 @@ -1,127 +1,160 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.2 2012/11/17 01:53:57 svnexp Exp $
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
  
  Description of tests
  ====================
  
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
 +
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.  All
 +    these attempts should fail, since cmsg_len in all requests is less than
 +    CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2012-11-20 11:26:18.000000000 +0200
 +++ unix_cmsg/unix_cmsg.c	2013-02-07 16:09:02.000000000 +0200
 @@ -27,48 +27,45 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.5 2012/11/19 22:59:17 svnexp Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of client.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +73,292 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	3
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	tempdir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_orig;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +#define CMSG_SPACE_EXTRA	64
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +366,322 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_orig = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_orig = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_orig == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_orig[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", tempdir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: %s", errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: %s", errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: %s", errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "wrong test number: %s", errstr);
  	} else
 -		testno1 = 0;
 -
 -	if (dgramflag == 0 && streamflag == 0)
 -		dgramflag = streamflag = 1;
 +		testno = 0;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (!dgram_flag && !stream_flag)
 +		dgram_flag = stream_flag = true;
  
 -	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +	if (dgram_flag && stream_flag && testno != 0)
 +		errx(EXIT_FAILURE, "particular test can be used "
 +		    "with the -t option only");
 +
 +	if (stream_flag) {
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 -	}
 -
 -	my_uid = getuid();
 -	my_euid = geteuid();
 -	my_gid = getgid();
 -	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 -	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 -		/* NOTREACHED */
 -	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
 -	sig_init();
 -
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 +	if (init() < 0)
 +		return (EXIT_FAILURE);
  
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	rv = EXIT_SUCCESS;
 +	if (stream_flag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgram_flag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (failed_flag ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +static int
 +socket_close(int fd)
 +{
 +	int rv;
 +
 +	rv = 0;
 +	if (close(fd) < 0) {
 +		logmsg("socket_close: close");
 +		rv = -1;
 +	}
 +	if (server_flag && fd == serv_sock_fd)
 +		if (unlink(serv_addr_sun.sun_path) < 0) {
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
 -/*
 - * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 - * or -1 if some error occurred.
 - */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +socket_create(void)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
  		return (-1);
  	}
 +	if (server_flag)
 +		serv_sock_fd = fd;
  
 -	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 -			logmsg("create_socket: snprintf failed");
 -			goto failed;
 -		}
 -		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 -			goto failed;
 -		}
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
  
 -		memset(addr, 0, sizeof(*addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +	if (server_flag) {
 +		if (bind(fd, (struct sockaddr *)&serv_addr_sun,
 +		    serv_addr_sun.sun_len) < 0) {
 +			logmsg("socket_create: bind(%s)",
 +			    serv_addr_sun.sun_path);
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
 +		if (sock_type == SOCK_STREAM) {
 +			int val;
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 -			logmsg("create_socket: bind(%s)", sock_path);
 -			goto failed;
 +			if (listen(fd, LISTENQ) < 0) {
 +				logmsg("socket_create: listen");
 +				goto failed;
 +			}
 +			val = fcntl(fd, F_GETFL, 0);
 +			if (val < 0) {
 +				logmsg("socket_create: fcntl(F_GETFL)");
 +				goto failed;
 +			}
 +			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +				logmsg("socket_create: fcntl(F_SETFL)");
 +				goto failed;
 +			}
  		}
  	}
  
 @@ -468,1163 +689,1325 @@ create_socket(char *sock_path, size_t so
  
  failed:
  	if (close(fd) < 0)
 -		logmsg("create_socket: close");
 +		logmsg("socket_create: close");
 +	if (server_flag)
 +		if (unlink(serv_addr_sun.sun_path) < 0)
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
  	return (-1);
  }
  
 -/*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 - */
  static int
 -create_server_socket(void)
 +socket_connect(int fd)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	dbgmsg("connect");
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	if (connect(fd, (struct sockaddr *)&serv_addr_sun,
 +	    serv_addr_sun.sun_len) < 0) {
 +		logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 - */
  static int
 -close_socket(const char *sock_path, int fd)
 +sync_recv(void)
  {
 -	int error = 0;
 +	ssize_t ssize;
 +	int fd;
 +	char buf;
  
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 -}
 +	dbgmsg("sync: wait");
  
 -/*
 - * Connect to server (socket address in servaddr).
 - */
 -static int
 -connect_server(int fd)
 -{
 -	dbgmsg(("connecting to %s", serv_sock_path));
 +	fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
  
 -	/*
 -	 * If PF_LOCAL listening socket's queue is full, then connect()
 -	 * returns ECONNREFUSED immediately, do not need timeout.
 -	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 -		logmsg("connect_server: connect(%s)", serv_sock_path);
 +	ssize = read(fd, &buf, 1);
 +	if (ssize < 0) {
 +		logmsg("sync_recv: read");
 +		return (-1);
 +	}
 +	if (ssize < 1) {
 +		logmsgx("sync_recv: read %zd of 1 byte", ssize);
  		return (-1);
  	}
  
 +	dbgmsg("sync: received");
 +
  	return (0);
  }
  
 -/*
 - * sendmsg() with timeout.
 - */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +sync_send(void)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	ssize_t ssize;
 +	int fd;
  
 -	nsent = sendmsg(fd, msg, 0);
 +	dbgmsg("sync: send");
  
 -	(void)alarm(0);
 +	fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
  
 -	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +	ssize = write(fd, "", 1);
 +	if (ssize < 0) {
 +		logmsg("sync_send: write");
  		return (-1);
  	}
 -
 -	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +	if (ssize < 1) {
 +		logmsgx("sync_send: sent %zd of 1 byte", ssize);
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * accept() with timeout.
 - */
  static int
 -accept_timeout(int listenfd)
 +message_send(int fd, const struct msghdr *msghdr)
  {
 -	int fd;
 -
 -	dbgmsg(("accepting connection"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	size_t size;
 +	ssize_t ssize;
 +	const struct cmsghdr *cmsghdr;
 +	int i;
 +
 +	size = 0;
 +	for (i = 0; i < msghdr->msg_iovlen; ++i)
 +		size += msghdr->msg_iov[i].iov_len;
 +
 +	cmsghdr = CMSG_FIRSTHDR(msghdr);
 +	dbgmsg("send: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	if (cmsghdr != NULL)
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +	dbgmsg("send: data size %zu", size);
 +
 +	ssize = sendmsg(fd, msghdr, 0);
 +	if (ssize < 0) {
 +		logmsg("message_send: sendmsg");
 +		return (-1);
 +	}
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 +	if (!send_data_flag)
 +		if (sync_send() < 0)
 +			return (-1);
  
 -	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	return (0);
 +}
  
 -	(void)alarm(0);
 +static int
 +message_sendn(int fd, struct msghdr *msghdr)
 +{
 +	u_int i;
  
 -	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 -		return (-1);
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +		if (message_send(fd, msghdr) < 0)
 +			return (-1);
  	}
 -
 -	return (fd);
 +	return (0);
  }
  
 -/*
 - * recvmsg() with timeout.
 - */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +message_recv(int fd, struct msghdr *msghdr)
  {
 -	ssize_t nread;
 +	size_t size;
 +	ssize_t ssize;
 +	int i;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 +	if (!send_data_flag)
 +		if (sync_recv() < 0)
 +			return (-1);
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 +	size = 0;
 +	for (i = 0; i < msghdr->msg_iovlen; ++i)
 +		size += msghdr->msg_iov[i].iov_len;
 +
 +	dbgmsg("recv: data size %zu", size);
 +
 +	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
 +	if (ssize < 0) {
 +		logmsg("message_recv: recvmsg");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
 -	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +	if (send_data_flag && memcmp(ipc_msg.buf_recv, ipc_msg.buf_orig,
 +	    ipc_msg.buf_size) != 0) {
 +		logmsgx("message_recv: recvmsg: message has wrong content");
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * Wait for synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_recv(int fd)
 +socket_accept(int listenfd)
  {
 -	ssize_t nread;
 -	char buf;
 -
 -	dbgmsg(("waiting for sync message"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
 +
 +	dbgmsg("accept");
 +
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("socket_accept: select");
 +		return (-1);
 +	}
 +	if (rv == 0) {
 +		logmsgx("socket_accept: select timeout");
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 +	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	if (fd < 0) {
 +		logmsg("socket_accept: accept");
 +		return (-1);
 +	}
  
 -	nread = read(fd, &buf, 1);
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("socket_accept: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("socket_accept: fcntl(F_SETFL)");
 +		goto failed;
 +	}
  
 -	(void)alarm(0);
 +	return (fd);
  
 -	if (nread < 0) {
 -		logmsg("sync_recv: read");
 -		return (-1);
 -	}
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("socket_accept: close");
 +	return (-1);
 +}
  
 -	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +static int
 +check_nxthdr(struct msghdr *msghdr, struct cmsghdr *cmsghdr)
 +{
 +	if (CMSG_NXTHDR(msghdr, cmsghdr) != NULL) {
 +		logmsgx("ancillary data has extra object");
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * Send synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_send(int fd)
 +check_msghdr(const struct msghdr *msghdr, size_t size)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending sync message"));
 +	dbgmsg("recv: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 +	if (msghdr->msg_flags & MSG_TRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_TRUNC");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
 -	if (nsent < 0) {
 -		logmsg("sync_send: write");
 +	if (msghdr->msg_flags & MSG_CTRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_CTRUNC");
  		return (-1);
  	}
 -
 -	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +	if (msghdr->msg_controllen < size) {
 +		logmsgx("msghdr.msg_controllen %u < %zu",
 +		    (u_int)msghdr->msg_controllen, size);
 +		return (-1);
 +	}
 +	if (msghdr->msg_controllen > 0 && size == 0) {
 +		logmsgx("msghdr.msg_controllen %u > 0",
 +		    (u_int)msghdr->msg_controllen);
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * waitpid() for client with timeout.
 - */
  static int
 -wait_client(void)
 +check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
  {
 -	int status;
 -	pid_t pid;
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 +	if (cmsghdr == NULL) {
 +		logmsgx("cmsghdr is NULL");
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 -
 -	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 +	dbgmsg("recv: cmsghdr.cmsg_len %u", (u_int)cmsghdr->cmsg_len);
  
 -	if (pid == (pid_t)-1) {
 -		logmsg("wait_client: waitpid");
 +	if (cmsghdr->cmsg_level != SOL_SOCKET) {
 +		logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
 +		    cmsghdr->cmsg_level);
  		return (-1);
  	}
 -
 -	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 -			return (-1);
 -		}
 -	} else {
 -		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 -		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +	if (cmsghdr->cmsg_type != type) {
 +		logmsgx("cmsghdr.cmsg_type %d != %d",
 +		    cmsghdr->cmsg_type, type);
 +		return (-1);
 +	}
 +	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
 +		logmsgx("cmsghdr.cmsg_len %u != %zu",
 +		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 - */
  static int
 -check_groups(const gid_t *gids, int n)
 +check_groups(const char *gid_arr_str, const gid_t *gid_arr,
 +    const char *gid_num_str, int gid_num, bool all_gids)
  {
 -	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
 +	int i;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 -	} else
 -		error = 0;
 -	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 -			if (gids[i] == my_gids[j]) {
 -				if (match[j]) {
 -					logmsgx("duplicated GID %lu",
 -					    (u_long)gids[i]);
 -					error = -1;
 -				} else
 -					match[j] = 1;
 -				break;
 -			}
 +	for (i = 0; i < gid_num; ++i)
 +		dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
 +
 +	if (all_gids) {
 +		if (gid_num != proc_cred.gid_num) {
 +			logmsgx("%s %d != %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
 -		if (j == my_ngids) {
 -			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +	} else {
 +		if (gid_num > proc_cred.gid_num) {
 +			logmsgx("%s %d > %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 -		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 -		}
 -	return (error);
 +	if (memcmp(gid_arr, proc_cred.gid_arr,
 +	    gid_num * sizeof(*gid_arr)) != 0) {
 +		logmsgx("%s content is wrong", gid_arr_str);
 +		for (i = 0; i < gid_num; ++i)
 +			if (gid_arr[i] != proc_cred.gid_arr[i]) {
 +				logmsgx("%s[%d] %lu != %lu",
 +				    gid_arr_str, i, (u_long)gid_arr[i],
 +				    (u_long)proc_cred.gid_arr[i]);
 +				break;
 +			}
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Send n messages with data and control message with SCM_CREDS type
 - * to server and exit.
 - */
 -static void
 -t_cmsgcred_client(u_int n)
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred))];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -	u_int i;
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
  
 -	assert(n == 1 || n == 2);
 +	dbgmsg("xucred.cr_version %u", xucred->cr_version);
 +	dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
 +	dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
 +
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("xucred.cr_version %u != %d",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != proc_cred.euid) {
 +		logmsgx("xucred.cr_uid %lu != %lu (EUID)",
 +		   (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("xucred.cr_ngroups == 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("xucred.cr_ngroups < 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > XU_NGROUPS) {
 +		logmsgx("xucred.cr_ngroups %hu > %u (max)",
 +		    xucred->cr_ngroups, XU_NGROUPS);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != proc_cred.egid) {
 +		logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
 +		    (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (check_groups("xucred.cr_groups", xucred->cr_groups,
 +	    "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct cmsgcred *cmsgcred;
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
 +		return (-1);
  
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 +	cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
  
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) : sizeof(control_un.control);
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct cmsgcred));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_CREDS;
 -
 -	for (i = 0; i < n; ++i) {
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
 +	dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
 +	dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
 +	dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
 +	dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
 +
 +	if (cmsgcred->cmcred_pid != client_pid) {
 +		logmsgx("cmsgcred.cmcred_pid %ld != %ld",
 +		    (long)cmsgcred->cmcred_pid, (long)client_pid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_uid != proc_cred.uid) {
 +		logmsgx("cmsgcred.cmcred_uid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_euid != proc_cred.euid) {
 +		logmsgx("cmsgcred.cmcred_euid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_gid != proc_cred.gid) {
 +		logmsgx("cmsgcred.cmcred_gid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups == 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups == 0");
 +		return (-1);
  	}
 +	if (cmsgcred->cmcred_ngroups < 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d < 0",
 +		    cmsgcred->cmcred_ngroups);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d > %d",
 +		    cmsgcred->cmcred_ngroups, CMGROUP_MAX);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
 +		logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
 +		    (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
 +	    "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct sockcred *sockcred;
  
 -	_exit(0);
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS,
 +	    SOCKCREDSIZE(proc_cred.gid_num)) < 0)
 +		return (-1);
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
  
 -failed:
 -	_exit(1);
 +	dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
 +	dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
 +	dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
 +	dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
 +	dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
 +
 +	if (sockcred->sc_uid != proc_cred.uid) {
 +		logmsgx("sockcred.sc_uid %lu != %lu",
 +		    (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_euid != proc_cred.euid) {
 +		logmsgx("sockcred.sc_euid %lu != %lu",
 +		    (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_gid != proc_cred.gid) {
 +		logmsgx("sockcred.sc_gid %lu != %lu",
 +		    (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_egid != proc_cred.egid) {
 +		logmsgx("sockcred.sc_egid %lu != %lu",
 +		    (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups == 0) {
 +		logmsgx("sockcred.sc_ngroups == 0");
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups < 0) {
 +		logmsgx("sockcred.sc_ngroups %d < 0",
 +		    sockcred->sc_ngroups);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups != proc_cred.gid_num) {
 +		logmsgx("sockcred.sc_ngroups %d != %u",
 +		    sockcred->sc_ngroups, proc_cred.gid_num);
 +		return (-1);
 +	}
 +	if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
 +	    "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
 +		return (-1);
 +	return (0);
  }
  
 -/*
 - * Receive two messages with data and control message with SCM_CREDS
 - * type followed by struct cmsgcred{} from client.  fd1 is a listen
 - * socket for stream sockets or simply socket for datagram sockets.
 - */
  static int
 -t_cmsgcred_server(int fd1)
 +check_scm_timestamp(struct cmsghdr *cmsghdr)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 -	u_int i;
 +	const struct timeval *timeval;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -	} else
 -		fd2 = fd1;
 +	if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
 +		return (-1);
  
 -	error = 0;
 +	timeval = (struct timeval *)CMSG_DATA(cmsghdr);
  
 -	controllen = sizeof(control_un.control);
 +	dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
 +	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
  
 -	for (i = 0; i < 2; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof(buf);
 +	return (0);
 +}
  
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 -		msg.msg_flags = 0;
 +static int
 +check_scm_bintime(struct cmsghdr *cmsghdr)
 +{
 +	const struct bintime *bintime;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 +	if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
 +		return (-1);
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +	bintime = (struct bintime *)CMSG_DATA(cmsghdr);
  
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 -		}
 +	dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
 +	    (intmax_t)bintime->sec, bintime->frac);
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 -		}
 +	return (0);
 +}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
 +{
 +	msghdr->msg_name = NULL;
 +	msghdr->msg_namelen = 0;
 +	if (send_data_flag) {
 +		iov->iov_base = server_flag ?
 +		    ipc_msg.buf_recv : ipc_msg.buf_orig;
 +		iov->iov_len = ipc_msg.buf_size;
 +		msghdr->msg_iov = iov;
 +		msghdr->msg_iovlen = 1;
 +	} else {
 +		msghdr->msg_iov = NULL;
 +		msghdr->msg_iovlen = 0;
 +	}
 +	msghdr->msg_control = cmsg_data;
 +	msghdr->msg_flags = 0;
 +}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +static void
 +msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size)
 +{
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	msghdr->msg_controllen = cmsg_size;
 +	dbgmsg("init: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
 +	    msghdr->msg_iov->iov_len : (size_t)0);
 +}
  
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
 +{
 +	struct cmsghdr *cmsghdr;
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	if (cmsg_data != NULL) {
 +		msghdr->msg_controllen = send_array_flag ?
 +		    cmsg_size : CMSG_SPACE(0);
 +		cmsghdr = CMSG_FIRSTHDR(msghdr);
 +		cmsghdr->cmsg_level = SOL_SOCKET;
 +		cmsghdr->cmsg_type = type;
 +		cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
 +	} else
 +		msghdr->msg_controllen = 0;
 +}
  
 -		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 -		}
 +static int
 +t_generic(int (*client_func)(int), int (*server_func)(int))
 +{
 +	int fd, rv, rv_client;
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +	switch (client_fork()) {
 +	case 0:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = client_func(fd);
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = server_func(fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		break;
 +	default:
 +		rv = -2;
 +	}
 +	return (rv);
 +}
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 -			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 -			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 -		}
 +static int
 +t_cmsgcred_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -		if (error2)
 -			goto next_error;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 -		}
 +	rv = -2;
  
 -		continue;
 -next_error:
 -		error = -1;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_CREDS, sizeof(struct cmsgcred));
  
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred(void)
 +t_cmsgcred_server(int fd1)
  {
 -	int error, fd;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred)) + CMSG_SPACE_EXTRA;
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(2);
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +			break;
 +
 +		if (check_nxthdr(&msghdr, cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_cmsgcred(void)
 +{
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
  }
  
 -/*
 - * Send two messages with data to server and exit.
 - */
 -static void
 -t_sockcred_client(int type)
 +static int
 +t_sockcred_client(int type, int fd)
  {
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	int fd;
 -	u_int i;
 -
 -	assert(type == 0 || type == 1);
 +	int rv;
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (type == 1)
 -		if (sync_recv(fd) < 0)
 -			goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = NULL;
 -	msg.msg_controllen = 0;
 -	msg.msg_flags = 0;
 -
 -	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -	_exit(0);
 +	if (type == 2)
 +		if (sync_recv() < 0)
 +			goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_CREDS
 - * type followed by struct sockcred{} and if n is not equal 1, then
 - * receive another one message with data.  fd1 is a listen socket for
 - * stream sockets or simply socket for datagram sockets.  If type is
 - * 1, then set LOCAL_CREDS option for accepted stream socket.
 - */
  static int
 -t_sockcred_server(int type, int fd1, u_int n)
 +t_sockcred_server(int type, int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
  	u_int i;
 +	int fd2, rv, val;
  
 -	assert(n == 1 || n == 2);
 -	assert(type == 0 || type == 1);
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -		if (type == 1) {
 -			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 -			}
 -			if (sync_send(fd2) < 0)
 -				goto failed;
 -		}
 -	} else
 -		fd2 = fd1;
 -
 -	error = 0;
 -
 -	for (i = 0; i < n; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 -
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 -		msg.msg_flags = 0;
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 +	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num)) +
 +	    CMSG_SPACE_EXTRA;
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 +	if (type == 1) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
  		}
 +	}
  
 -		if (i != 0 && sock_type == SOCK_STREAM) {
 -			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 -			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
 -			continue;
 -		}
 +	if (sync_send() < 0)
 +		goto done;
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 -		}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 +	if (type == 2) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
 +		}
 +		if (sync_send() < 0)
 +			goto done;
 +	}
 +
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
  		}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 -		}
 +		if (i > 1 && sock_type == SOCK_STREAM) {
 +			if (check_msghdr(&msghdr, 0) < 0)
 +				break;
 +		} else {
 +			if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +				break;
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +			cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
  
 -		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			if (check_nxthdr(&msghdr, cmsghdr) < 0)
 +				break;
  		}
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +static int
 +t_sockcred_1(void)
 +{
 +	u_int i;
 +	int fd, rv, rv_client;
  
 -		error2 = 0;
 -		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_ngroups > NGROUPS_MAX) {
 -			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
 -			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 +	switch (client_fork()) {
 +	case 0:
 +		for (i = 1; i <= 2; ++i) {
 +			dbgmsg("client #%u", i);
 +			fd = socket_create();
 +			if (fd < 0)
 +				rv = -2;
 +			else {
 +				rv = t_sockcred_client(1, fd);
 +				if (socket_close(fd) < 0)
 +					rv = -2;
  			}
 +			if (rv != 0)
 +				break;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = t_sockcred_server(1, fd);
 +			if (rv == 0)
 +				rv = t_sockcred_server(3, fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
  		}
 -
 -		continue;
 -next_error:
 -		error = -1;
 +		break;
 +	default:
 +		rv = -2;
  	}
  
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_sockcred_2_client(int fd)
 +{
 +	return (t_sockcred_client(2, fd));
  }
  
  static int
 -t_sockcred(int type)
 +t_sockcred_2_server(int fd)
  {
 -	int error, fd, optval;
 +	return (t_sockcred_server(2, fd));
 +}
  
 -	assert(type == 0 || type == 1);
 +static int
 +t_sockcred_2(void)
 +{
 +	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
 +}
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +static int
 +t_cmsgcred_sockcred_server(int fd1)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data, *cmsg1_data, *cmsg2_data;
 +	size_t cmsg_size, cmsg1_size, cmsg2_size;
 +	u_int i;
 +	int fd2, rv, val;
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (type == 0) {
 -		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 -		}
 +	cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num)) +
 +	    CMSG_SPACE_EXTRA;
 +	cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred)) + CMSG_SPACE_EXTRA;
 +	cmsg1_data = malloc(cmsg1_size);
 +	cmsg2_data = malloc(cmsg2_size);
 +	if (cmsg1_data == NULL || cmsg2_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	dbgmsg("setting LOCAL_CREDS");
 +	val = 1;
 +	if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS)");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_sockcred_client(type);
 -	}
 +	if (sync_send() < 0)
 +		goto done;
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if (wait_client() < 0)
 -		goto failed;
 +	cmsg_data = cmsg1_data;
 +	cmsg_size = cmsg1_size;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 -}
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (i == 1 || sock_type == SOCK_DGRAM) {
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
 +		} else {
 +			if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +				break;
 +		}
  
 -static int
 -t_sockcred_stream1(void)
 -{
 -	return (t_sockcred(0));
 +		if (check_nxthdr(&msghdr, cmsghdr) < 0)
 +			break;
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg1_data);
 +	free(cmsg2_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
 -t_sockcred_stream2(void)
 +t_cmsgcred_sockcred(void)
  {
 -	return (t_sockcred(1));
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
  }
  
  static int
 -t_sockcred_dgram(void)
 +t_timeval_client(int fd)
  {
 -	return (t_sockcred(0));
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
 +
 +	if (sync_recv() < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_TIMESTAMP, sizeof(struct timeval));
 +
 +	if (socket_connect(fd) < 0)
 +		goto done;
 +
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred_sockcred(void)
 +t_timeval_server(int fd1)
  {
 -	int error, fd, optval;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE_EXTRA;
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(1);
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_timestamp(cmsghdr) < 0)
 +			break;
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		if (check_nxthdr(&msghdr, cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_timeval(void)
 +{
 +	return (t_generic(t_timeval_client, t_timeval_server));
  }
  
 -/*
 - * Send one message with data and control message with SCM_TIMESTAMP
 - * type to server and exit.
 - */
 -static void
 -t_timestamp_client(void)
 +static int
 +t_bintime_client(int fd)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval))];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 -
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct timeval));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_TIMESTAMP;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_BINTIME, sizeof(struct bintime));
  
 -	_exit(0);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_TIMESTAMP
 - * type followed by struct timeval{} from client.
 - */
  static int
 -t_timestamp_server(int fd1)
 +t_bintime_server(int fd1)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct timeval *timeval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
 +
 +	if (sync_send() < 0)
 +		return (-2);
 +
 +	fd2 = -1;
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime)) + CMSG_SPACE_EXTRA;
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
  	} else
  		fd2 = fd1;
  
 -	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;
 -	msg.msg_flags = 0;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	error = -1;
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_bintime(cmsghdr) < 0)
 +			break;
  
 -	if (msg.msg_flags & MSG_CTRUNC) {
 -		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -		goto done;
 +		if (check_nxthdr(&msghdr, cmsghdr) < 0)
 +			break;
  	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -		goto done;
 -	}
 +static int
 +t_bintime(void)
 +{
 +	return (t_generic(t_bintime_client, t_bintime_server));
 +}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -		logmsgx("CMSG_FIRSTHDR is NULL");
 -		goto done;
 -	}
 +static int
 +t_cmsg_len_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	socklen_t socklen;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (cmptr->cmsg_level != SOL_SOCKET) {
 -		logmsgx("cmsg_level %d != SOL_SOCKET", cmptr->cmsg_level);
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
  		goto done;
  	}
  
 -	if (cmptr->cmsg_type != SCM_TIMESTAMP) {
 -		logmsgx("cmsg_type %d != SCM_TIMESTAMP", cmptr->cmsg_type);
 +	if (socket_connect(fd) < 0)
  		goto done;
 +
 +	iov[0].iov_base = ipc_msg.buf_orig;
 +	iov[0].iov_len = ipc_msg.buf_size;
 +
 +	msghdr.msg_name = NULL;
 +	msghdr.msg_namelen = 0;
 +	msghdr.msg_iov = iov;
 +	msghdr.msg_iovlen = 1;
 +	msghdr.msg_control = cmsg_data;
 +	msghdr.msg_flags = 0;
 +	msghdr.msg_controllen = cmsg_size;
 +
 +	cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +	cmsghdr->cmsg_level = SOL_SOCKET;
 +	cmsghdr->cmsg_type = SCM_CREDS;
 +
 +	for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
 +		cmsghdr->cmsg_len = socklen;
 +		dbgmsg("send: msghdr.msg_controllen %u",
 +		    (u_int)msghdr.msg_controllen);
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +		dbgmsg("send: data size %zu", iov[0].iov_len);
 +		if (sendmsg(fd, &msghdr, 0) < 0)
 +			continue;
 +		logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
 +		    (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
 +		rv = -1;
 +		break;
  	}
 +	if (socklen == CMSG_LEN(0))
 +		rv = 0;
  
 -	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +	if (sync_send() < 0) {
 +		rv = -2;
  		goto done;
  	}
 +done:
 +	free(cmsg_data);
 +	return (rv);
 +}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +static int
 +t_cmsg_len_server(int fd1)
 +{
 +	int fd2, rv;
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 -	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
 +	if (sync_send() < 0)
 +		return (-2);
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 -		goto done;
 -	}
 +	rv = -2;
  
 -	error = 0;
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 +	if (sync_recv() < 0)
 +		goto done;
 +
 +	rv = 0;
  done:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_cmsg_len(void)
 +{
 +	return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
  }
  
  static int
 -t_timestamp(void)
 +t_peercred_client(int fd)
  {
 -	int error, fd;
 +	struct xucred xucred;
 +	socklen_t len;
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +	if (sync_recv() < 0)
 +		return (-1);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	if (socket_connect(fd) < 0)
 +		return (-1);
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		return (-1);
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_timestamp_client();
 -	}
 +	if (check_xucred(&xucred, len) < 0)
 +		return (-1);
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	return (0);
 +}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 +	if (sync_send() < 0)
  		return (-2);
 +
 +	fd2 = socket_accept(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
  	}
 -	return (error);
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (socket_close(fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	return (t_generic(t_peercred_client, t_peercred_server));
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/unix_cmsg.t	2013-02-07 12:09:45.000000000 +0200
 @@ -11,47 +11,66 @@ n=0
  
  run()
  {
 -	result=`${cmd} -t $2 $3 $4 2>&1`
 -	if [ $? -eq 0 ]; then
 -		echo -n "ok $1"
 -	else
 -		echo -n "not ok $1"
 +	result=`${cmd} -t $2 $3 ${5%% *} 2>&1`
 +	if [ $? -ne 0 ]; then
 +		echo -n "not "
  	fi
 -	echo " -" $5
 +	echo "ok $1 - $4 ${5#* }"
  	echo ${result} | grep -E "SERVER|CLIENT" | while read line; do
  		echo "# ${line}"
  	done
  }
  
 -echo "1..15"
 +echo "1..38"
  
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"4 Sending cmsgcred, receiving sockcred" \
 +	"5 Sending, receiving timeval" \
 +	"6 Sending, receiving bintime"
  do
 -	n=`expr ${n} + 1`
 -	run ${n} stream "" ${n} "STREAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
 +	done
  done
  
 -i=0
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "2 Receiving sockcred (listening socket)"
 +
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "3 Receiving sockcred (accepted socket)"
 +
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"2 Receiving sockcred" \
 +	"4 Sending, receiving timeval" \
 +	"5 Sending, receiving bintime"
  do
 -	i=`expr ${i} + 1`
 -	n=`expr ${n} + 1`
 -	run ${n} dgram "" ${i} "DGRAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
 +	done
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 -
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +n=$((n + 1))
 +run ${n} dgram "-z 0" DGRAM "3 Sending cmsgcred, receiving sockcred"
 +
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "7 Check cmsghdr.cmsg_len"
 +
 +n=$((n + 1))
 +run ${n} dgram "-z 0" DGRAM "6 Check cmsghdr.cmsg_len"
 +
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option"

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Fri, 8 Feb 2013 12:08:54 +0200

 Completely redesigned unix_cmsg with improved logic (corrected version).
 
 Details in README.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/README	2013-02-08 12:08:52.000000000 +0200
 @@ -1,127 +1,160 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.2 2012/11/17 01:53:57 svnexp Exp $
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
  
  Description of tests
  ====================
  
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
 +
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.
 +    All these attempts should fail, since cmsg_len in all requests is less
 +    than CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2012-11-20 11:26:18.000000000 +0200
 +++ unix_cmsg/unix_cmsg.c	2013-02-08 11:42:08.000000000 +0200
 @@ -27,48 +27,46 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.5 2012/11/19 22:59:17 svnexp Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of
 + * a client process.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	3
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	tempdir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_send;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +365,325 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_send[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", tempdir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: number is %s",
 +				    errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: number is %s",
 +				    errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: number is %s",
 +				    errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "test number is %s", errstr);
  	} else
 -		testno1 = 0;
 -
 -	if (dgramflag == 0 && streamflag == 0)
 -		dgramflag = streamflag = 1;
 +		testno = 0;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 +	if (!dgram_flag && !stream_flag)
 +		dgram_flag = stream_flag = true;
  
 -	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 +	if (dgram_flag && stream_flag && testno != 0)
 +		errx(EXIT_FAILURE, "particular test can be used "
 +		    "with the -t option only");
 +
 +	if (stream_flag) {
 +		if (testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
  	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 -	}
 -
 -	my_uid = getuid();
 -	my_euid = geteuid();
 -	my_gid = getgid();
 -	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 -	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 -		/* NOTREACHED */
 -	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 +		if (testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	}
  
 -	sig_init();
 -
  	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +		err(EXIT_FAILURE, "mkdtemp");
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 -	}
 +	if (init() < 0)
 +		return (EXIT_FAILURE);
  
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +	rv = EXIT_SUCCESS;
 +	if (stream_flag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			rv = EXIT_FAILURE;
 +	if (dgram_flag && rv == EXIT_SUCCESS)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			rv = EXIT_FAILURE;
  
  	if (rmdir(tempdir) < 0) {
  		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +		rv = EXIT_FAILURE;
  	}
  
 -	return (nfailed ? EX_OSERR : EX_OK);
 +	return (failed_flag ? EXIT_FAILURE : rv);
 +}
  
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +static int
 +socket_close(int fd)
 +{
 +	int rv;
 +
 +	rv = 0;
 +	if (close(fd) < 0) {
 +		logmsg("socket_close: close");
 +		rv = -1;
 +	}
 +	if (server_flag && fd == serv_sock_fd)
 +		if (unlink(serv_addr_sun.sun_path) < 0) {
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
 -/*
 - * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 - * or -1 if some error occurred.
 - */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +socket_create(void)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
  		return (-1);
  	}
 +	if (server_flag)
 +		serv_sock_fd = fd;
  
 -	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 -			logmsg("create_socket: snprintf failed");
 -			goto failed;
 -		}
 -		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 -			goto failed;
 -		}
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
  
 -		memset(addr, 0, sizeof(*addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +	if (server_flag) {
 +		if (bind(fd, (struct sockaddr *)&serv_addr_sun,
 +		    serv_addr_sun.sun_len) < 0) {
 +			logmsg("socket_create: bind(%s)",
 +			    serv_addr_sun.sun_path);
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
 +		if (sock_type == SOCK_STREAM) {
 +			int val;
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 -			logmsg("create_socket: bind(%s)", sock_path);
 -			goto failed;
 +			if (listen(fd, LISTENQ) < 0) {
 +				logmsg("socket_create: listen");
 +				goto failed;
 +			}
 +			val = fcntl(fd, F_GETFL, 0);
 +			if (val < 0) {
 +				logmsg("socket_create: fcntl(F_GETFL)");
 +				goto failed;
 +			}
 +			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +				logmsg("socket_create: fcntl(F_SETFL)");
 +				goto failed;
 +			}
  		}
  	}
  
 @@ -468,1163 +691,1282 @@ create_socket(char *sock_path, size_t so
  
  failed:
  	if (close(fd) < 0)
 -		logmsg("create_socket: close");
 +		logmsg("socket_create: close");
 +	if (server_flag)
 +		if (unlink(serv_addr_sun.sun_path) < 0)
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
  	return (-1);
  }
  
 -/*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 - */
  static int
 -create_server_socket(void)
 +socket_connect(int fd)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	dbgmsg("connect");
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	if (connect(fd, (struct sockaddr *)&serv_addr_sun,
 +	    serv_addr_sun.sun_len) < 0) {
 +		logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 - */
  static int
 -close_socket(const char *sock_path, int fd)
 +sync_recv(void)
  {
 -	int error = 0;
 +	ssize_t ssize;
 +	int fd;
 +	char buf;
  
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 -}
 +	dbgmsg("sync: wait");
  
 -/*
 - * Connect to server (socket address in servaddr).
 - */
 -static int
 -connect_server(int fd)
 -{
 -	dbgmsg(("connecting to %s", serv_sock_path));
 +	fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
  
 -	/*
 -	 * If PF_LOCAL listening socket's queue is full, then connect()
 -	 * returns ECONNREFUSED immediately, do not need timeout.
 -	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 -		logmsg("connect_server: connect(%s)", serv_sock_path);
 +	ssize = read(fd, &buf, 1);
 +	if (ssize < 0) {
 +		logmsg("sync_recv: read");
 +		return (-1);
 +	}
 +	if (ssize < 1) {
 +		logmsgx("sync_recv: read %zd of 1 byte", ssize);
  		return (-1);
  	}
  
 +	dbgmsg("sync: received");
 +
  	return (0);
  }
  
 -/*
 - * sendmsg() with timeout.
 - */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +sync_send(void)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	ssize_t ssize;
 +	int fd;
  
 -	nsent = sendmsg(fd, msg, 0);
 +	dbgmsg("sync: send");
  
 -	(void)alarm(0);
 +	fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
  
 -	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +	ssize = write(fd, "", 1);
 +	if (ssize < 0) {
 +		logmsg("sync_send: write");
  		return (-1);
  	}
 -
 -	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +	if (ssize < 1) {
 +		logmsgx("sync_send: sent %zd of 1 byte", ssize);
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * accept() with timeout.
 - */
  static int
 -accept_timeout(int listenfd)
 +message_send(int fd, const struct msghdr *msghdr)
  {
 -	int fd;
 -
 -	dbgmsg(("accepting connection"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	const struct cmsghdr *cmsghdr;
 +	size_t size;
 +	ssize_t ssize;
 +
 +	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
 +	dbgmsg("send: data size %zu", size);
 +	dbgmsg("send: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	cmsghdr = CMSG_FIRSTHDR(msghdr);
 +	if (cmsghdr != NULL)
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +
 +	ssize = sendmsg(fd, msghdr, 0);
 +	if (ssize < 0) {
 +		logmsg("message_send: sendmsg");
 +		return (-1);
 +	}
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 +	if (!send_data_flag)
 +		if (sync_send() < 0)
 +			return (-1);
  
 -	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	return (0);
 +}
  
 -	(void)alarm(0);
 +static int
 +message_sendn(int fd, struct msghdr *msghdr)
 +{
 +	u_int i;
  
 -	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 -		return (-1);
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +		if (message_send(fd, msghdr) < 0)
 +			return (-1);
  	}
 -
 -	return (fd);
 +	return (0);
  }
  
 -/*
 - * recvmsg() with timeout.
 - */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +message_recv(int fd, struct msghdr *msghdr)
  {
 -	ssize_t nread;
 +	const struct cmsghdr *cmsghdr;
 +	size_t size;
 +	ssize_t ssize;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 +	if (!send_data_flag)
 +		if (sync_recv() < 0)
 +			return (-1);
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 +	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
 +	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
 +	if (ssize < 0) {
 +		logmsg("message_recv: recvmsg");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
 -	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +	dbgmsg("recv: data size %zd", ssize);
 +	dbgmsg("recv: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	cmsghdr = CMSG_FIRSTHDR(msghdr);
 +	if (cmsghdr != NULL)
 +		dbgmsg("recv: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +
 +	if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) {
 +		logmsgx("message_recv: received message has wrong content");
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * Wait for synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_recv(int fd)
 +socket_accept(int listenfd)
  {
 -	ssize_t nread;
 -	char buf;
 -
 -	dbgmsg(("waiting for sync message"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
 +
 +	dbgmsg("accept");
 +
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("socket_accept: select");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
 -	if (nread < 0) {
 -		logmsg("sync_recv: read");
 +	if (rv == 0) {
 +		logmsgx("socket_accept: select timeout");
  		return (-1);
  	}
  
 -	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	if (fd < 0) {
 +		logmsg("socket_accept: accept");
  		return (-1);
  	}
  
 -	return (0);
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("socket_accept: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("socket_accept: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("socket_accept: close");
 +	return (-1);
  }
  
 -/*
 - * Send synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_send(int fd)
 +check_msghdr(const struct msghdr *msghdr, size_t size)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending sync message"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 +	if (msghdr->msg_flags & MSG_TRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_TRUNC");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
 -	if (nsent < 0) {
 -		logmsg("sync_send: write");
 +	if (msghdr->msg_flags & MSG_CTRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_CTRUNC");
  		return (-1);
  	}
 -
 -	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +	if (msghdr->msg_controllen < size) {
 +		logmsgx("msghdr.msg_controllen %u < %zu",
 +		    (u_int)msghdr->msg_controllen, size);
 +		return (-1);
 +	}
 +	if (msghdr->msg_controllen > 0 && size == 0) {
 +		logmsgx("msghdr.msg_controllen %u > 0",
 +		    (u_int)msghdr->msg_controllen);
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * waitpid() for client with timeout.
 - */
  static int
 -wait_client(void)
 +check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
  {
 -	int status;
 -	pid_t pid;
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 +	if (cmsghdr == NULL) {
 +		logmsgx("cmsghdr is NULL");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
 -	if (pid == (pid_t)-1) {
 -		logmsg("wait_client: waitpid");
 +	if (cmsghdr->cmsg_level != SOL_SOCKET) {
 +		logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
 +		    cmsghdr->cmsg_level);
  		return (-1);
  	}
 -
 -	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 -			return (-1);
 -		}
 -	} else {
 -		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 -		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +	if (cmsghdr->cmsg_type != type) {
 +		logmsgx("cmsghdr.cmsg_type %d != %d",
 +		    cmsghdr->cmsg_type, type);
 +		return (-1);
 +	}
 +	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
 +		logmsgx("cmsghdr.cmsg_len %u != %zu",
 +		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 - */
  static int
 -check_groups(const gid_t *gids, int n)
 +check_groups(const char *gid_arr_str, const gid_t *gid_arr,
 +    const char *gid_num_str, int gid_num, bool all_gids)
  {
 -	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
 +	int i;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 -	} else
 -		error = 0;
 -	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 -			if (gids[i] == my_gids[j]) {
 -				if (match[j]) {
 -					logmsgx("duplicated GID %lu",
 -					    (u_long)gids[i]);
 -					error = -1;
 -				} else
 -					match[j] = 1;
 -				break;
 -			}
 +	for (i = 0; i < gid_num; ++i)
 +		dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
 +
 +	if (all_gids) {
 +		if (gid_num != proc_cred.gid_num) {
 +			logmsgx("%s %d != %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
 -		if (j == my_ngids) {
 -			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +	} else {
 +		if (gid_num > proc_cred.gid_num) {
 +			logmsgx("%s %d > %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 -		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 -		}
 -	return (error);
 +	if (memcmp(gid_arr, proc_cred.gid_arr,
 +	    gid_num * sizeof(*gid_arr)) != 0) {
 +		logmsgx("%s content is wrong", gid_arr_str);
 +		for (i = 0; i < gid_num; ++i)
 +			if (gid_arr[i] != proc_cred.gid_arr[i]) {
 +				logmsgx("%s[%d] %lu != %lu",
 +				    gid_arr_str, i, (u_long)gid_arr[i],
 +				    (u_long)proc_cred.gid_arr[i]);
 +				break;
 +			}
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Send n messages with data and control message with SCM_CREDS type
 - * to server and exit.
 - */
 -static void
 -t_cmsgcred_client(u_int n)
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred))];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -	u_int i;
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
  
 -	assert(n == 1 || n == 2);
 +	dbgmsg("xucred.cr_version %u", xucred->cr_version);
 +	dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
 +	dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
 +
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("xucred.cr_version %u != %d",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != proc_cred.euid) {
 +		logmsgx("xucred.cr_uid %lu != %lu (EUID)",
 +		   (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("xucred.cr_ngroups == 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("xucred.cr_ngroups < 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > XU_NGROUPS) {
 +		logmsgx("xucred.cr_ngroups %hu > %u (max)",
 +		    xucred->cr_ngroups, XU_NGROUPS);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != proc_cred.egid) {
 +		logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
 +		    (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (check_groups("xucred.cr_groups", xucred->cr_groups,
 +	    "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct cmsgcred *cmsgcred;
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
 +		return (-1);
  
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 +	cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
  
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) : sizeof(control_un.control);
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct cmsgcred));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_CREDS;
 -
 -	for (i = 0; i < n; ++i) {
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
 +	dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
 +	dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
 +	dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
 +	dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
 +
 +	if (cmsgcred->cmcred_pid != client_pid) {
 +		logmsgx("cmsgcred.cmcred_pid %ld != %ld",
 +		    (long)cmsgcred->cmcred_pid, (long)client_pid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_uid != proc_cred.uid) {
 +		logmsgx("cmsgcred.cmcred_uid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_euid != proc_cred.euid) {
 +		logmsgx("cmsgcred.cmcred_euid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_gid != proc_cred.gid) {
 +		logmsgx("cmsgcred.cmcred_gid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups == 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups == 0");
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups < 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d < 0",
 +		    cmsgcred->cmcred_ngroups);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d > %d",
 +		    cmsgcred->cmcred_ngroups, CMGROUP_MAX);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
 +		logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
 +		    (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
  	}
 +	if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
 +	    "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct sockcred *sockcred;
  
 -	_exit(0);
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS,
 +	    SOCKCREDSIZE(proc_cred.gid_num)) < 0)
 +		return (-1);
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
  
 -failed:
 -	_exit(1);
 +	dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
 +	dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
 +	dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
 +	dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
 +	dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
 +
 +	if (sockcred->sc_uid != proc_cred.uid) {
 +		logmsgx("sockcred.sc_uid %lu != %lu",
 +		    (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_euid != proc_cred.euid) {
 +		logmsgx("sockcred.sc_euid %lu != %lu",
 +		    (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_gid != proc_cred.gid) {
 +		logmsgx("sockcred.sc_gid %lu != %lu",
 +		    (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_egid != proc_cred.egid) {
 +		logmsgx("sockcred.sc_egid %lu != %lu",
 +		    (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups == 0) {
 +		logmsgx("sockcred.sc_ngroups == 0");
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups < 0) {
 +		logmsgx("sockcred.sc_ngroups %d < 0",
 +		    sockcred->sc_ngroups);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups != proc_cred.gid_num) {
 +		logmsgx("sockcred.sc_ngroups %d != %u",
 +		    sockcred->sc_ngroups, proc_cred.gid_num);
 +		return (-1);
 +	}
 +	if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
 +	    "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
 +		return (-1);
 +	return (0);
  }
  
 -/*
 - * Receive two messages with data and control message with SCM_CREDS
 - * type followed by struct cmsgcred{} from client.  fd1 is a listen
 - * socket for stream sockets or simply socket for datagram sockets.
 - */
  static int
 -t_cmsgcred_server(int fd1)
 +check_scm_timestamp(struct cmsghdr *cmsghdr)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 -	u_int i;
 +	const struct timeval *timeval;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -	} else
 -		fd2 = fd1;
 +	if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
 +		return (-1);
  
 -	error = 0;
 +	timeval = (struct timeval *)CMSG_DATA(cmsghdr);
  
 -	controllen = sizeof(control_un.control);
 +	dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
 +	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
  
 -	for (i = 0; i < 2; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof(buf);
 +	return (0);
 +}
  
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 -		msg.msg_flags = 0;
 +static int
 +check_scm_bintime(struct cmsghdr *cmsghdr)
 +{
 +	const struct bintime *bintime;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 +	if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
 +		return (-1);
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +	bintime = (struct bintime *)CMSG_DATA(cmsghdr);
  
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 -		}
 +	dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
 +	    (intmax_t)bintime->sec, bintime->frac);
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 -		}
 +	return (0);
 +}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
 +{
 +	msghdr->msg_name = NULL;
 +	msghdr->msg_namelen = 0;
 +	if (send_data_flag) {
 +		iov->iov_base = server_flag ?
 +		    ipc_msg.buf_recv : ipc_msg.buf_send;
 +		iov->iov_len = ipc_msg.buf_size;
 +		msghdr->msg_iov = iov;
 +		msghdr->msg_iovlen = 1;
 +	} else {
 +		msghdr->msg_iov = NULL;
 +		msghdr->msg_iovlen = 0;
 +	}
 +	msghdr->msg_control = cmsg_data;
 +	msghdr->msg_flags = 0;
 +}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +static void
 +msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size)
 +{
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	msghdr->msg_controllen = cmsg_size;
 +	dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
 +	    msghdr->msg_iov->iov_len : (size_t)0);
 +	dbgmsg("init: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +}
  
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
 +{
 +	struct cmsghdr *cmsghdr;
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	if (cmsg_data != NULL) {
 +		msghdr->msg_controllen = send_array_flag ?
 +		    cmsg_size : CMSG_SPACE(0);
 +		cmsghdr = CMSG_FIRSTHDR(msghdr);
 +		cmsghdr->cmsg_level = SOL_SOCKET;
 +		cmsghdr->cmsg_type = type;
 +		cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
 +	} else
 +		msghdr->msg_controllen = 0;
 +}
  
 -		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 -		}
 +static int
 +t_generic(int (*client_func)(int), int (*server_func)(int))
 +{
 +	int fd, rv, rv_client;
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +	switch (client_fork()) {
 +	case 0:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = client_func(fd);
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = server_func(fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		break;
 +	default:
 +		rv = -2;
 +	}
 +	return (rv);
 +}
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 -			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 -			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 -		}
 +static int
 +t_cmsgcred_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -		if (error2)
 -			goto next_error;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 -		}
 +	rv = -2;
  
 -		continue;
 -next_error:
 -		error = -1;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_CREDS, sizeof(struct cmsgcred));
  
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred(void)
 +t_cmsgcred_server(int fd1)
  {
 -	int error, fd;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(2);
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_cmsgcred(void)
 +{
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
  }
  
 -/*
 - * Send two messages with data to server and exit.
 - */
 -static void
 -t_sockcred_client(int type)
 +static int
 +t_sockcred_client(int type, int fd)
  {
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	int fd;
 -	u_int i;
 -
 -	assert(type == 0 || type == 1);
 +	int rv;
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (type == 1)
 -		if (sync_recv(fd) < 0)
 -			goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = NULL;
 -	msg.msg_controllen = 0;
 -	msg.msg_flags = 0;
 -
 -	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -	_exit(0);
 +	if (type == 2)
 +		if (sync_recv() < 0)
 +			goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_CREDS
 - * type followed by struct sockcred{} and if n is not equal 1, then
 - * receive another one message with data.  fd1 is a listen socket for
 - * stream sockets or simply socket for datagram sockets.  If type is
 - * 1, then set LOCAL_CREDS option for accepted stream socket.
 - */
  static int
 -t_sockcred_server(int type, int fd1, u_int n)
 +t_sockcred_server(int type, int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
  	u_int i;
 +	int fd2, rv, val;
  
 -	assert(n == 1 || n == 2);
 -	assert(type == 0 || type == 1);
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -		if (type == 1) {
 -			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 -			}
 -			if (sync_send(fd2) < 0)
 -				goto failed;
 -		}
 -	} else
 -		fd2 = fd1;
 -
 -	error = 0;
 -
 -	for (i = 0; i < n; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 -
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 -		msg.msg_flags = 0;
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 -
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 -		}
 -
 -		if (i != 0 && sock_type == SOCK_STREAM) {
 -			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 -			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
 -			continue;
 -		}
 +	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +	if (type == 1) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
  		}
 +	}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 -		}
 +	if (sync_send() < 0)
 +		goto done;
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 +	if (type == 2) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
 +		}
 +		if (sync_send() < 0)
 +			goto done;
 +	}
 +
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
  		}
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +		if (i > 1 && sock_type == SOCK_STREAM) {
 +			if (check_msghdr(&msghdr, 0) < 0)
 +				break;
 +		} else {
 +			if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +				break;
  
 -		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
  		}
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +static int
 +t_sockcred_1(void)
 +{
 +	u_int i;
 +	int fd, rv, rv_client;
  
 -		error2 = 0;
 -		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_ngroups > NGROUPS_MAX) {
 -			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
 -			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 +	switch (client_fork()) {
 +	case 0:
 +		for (i = 1; i <= 2; ++i) {
 +			dbgmsg("client #%u", i);
 +			fd = socket_create();
 +			if (fd < 0)
 +				rv = -2;
 +			else {
 +				rv = t_sockcred_client(1, fd);
 +				if (socket_close(fd) < 0)
 +					rv = -2;
  			}
 +			if (rv != 0)
 +				break;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = t_sockcred_server(1, fd);
 +			if (rv == 0)
 +				rv = t_sockcred_server(3, fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
  		}
 -
 -		continue;
 -next_error:
 -		error = -1;
 +		break;
 +	default:
 +		rv = -2;
  	}
  
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_sockcred_2_client(int fd)
 +{
 +	return (t_sockcred_client(2, fd));
  }
  
  static int
 -t_sockcred(int type)
 +t_sockcred_2_server(int fd)
  {
 -	int error, fd, optval;
 +	return (t_sockcred_server(2, fd));
 +}
  
 -	assert(type == 0 || type == 1);
 +static int
 +t_sockcred_2(void)
 +{
 +	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
 +}
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +static int
 +t_cmsgcred_sockcred_server(int fd1)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data, *cmsg1_data, *cmsg2_data;
 +	size_t cmsg_size, cmsg1_size, cmsg2_size;
 +	u_int i;
 +	int fd2, rv, val;
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (type == 0) {
 -		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 -		}
 +	cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
 +	cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg1_data = malloc(cmsg1_size);
 +	cmsg2_data = malloc(cmsg2_size);
 +	if (cmsg1_data == NULL || cmsg2_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	dbgmsg("setting LOCAL_CREDS");
 +	val = 1;
 +	if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS)");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_sockcred_client(type);
 -	}
 +	if (sync_send() < 0)
 +		goto done;
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if (wait_client() < 0)
 -		goto failed;
 +	cmsg_data = cmsg1_data;
 +	cmsg_size = cmsg1_size;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 -}
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (i == 1 || sock_type == SOCK_DGRAM) {
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
 +		} else {
 +			if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +				break;
 +		}
  
 -static int
 -t_sockcred_stream1(void)
 -{
 -	return (t_sockcred(0));
 +		cmsg_data = cmsg2_data;
 +		cmsg_size = cmsg2_size;
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg1_data);
 +	free(cmsg2_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
 -t_sockcred_stream2(void)
 +t_cmsgcred_sockcred(void)
  {
 -	return (t_sockcred(1));
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
  }
  
  static int
 -t_sockcred_dgram(void)
 +t_timeval_client(int fd)
  {
 -	return (t_sockcred(0));
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
 +
 +	if (sync_recv() < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_TIMESTAMP, sizeof(struct timeval));
 +
 +	if (socket_connect(fd) < 0)
 +		goto done;
 +
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred_sockcred(void)
 +t_timeval_server(int fd1)
  {
 -	int error, fd, optval;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 -	}
 -
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(1);
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_timestamp(cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_timeval(void)
 +{
 +	return (t_generic(t_timeval_client, t_timeval_server));
  }
  
 -/*
 - * Send one message with data and control message with SCM_TIMESTAMP
 - * type to server and exit.
 - */
 -static void
 -t_timestamp_client(void)
 +static int
 +t_bintime_client(int fd)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval))];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 -
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct timeval));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_TIMESTAMP;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_BINTIME, sizeof(struct bintime));
  
 -	_exit(0);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_TIMESTAMP
 - * type followed by struct timeval{} from client.
 - */
  static int
 -t_timestamp_server(int fd1)
 +t_bintime_server(int fd1)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct timeval *timeval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
 +
 +	if (sync_send() < 0)
 +		return (-2);
 +
 +	fd2 = -1;
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
  	} else
  		fd2 = fd1;
  
 -	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;
 -	msg.msg_flags = 0;
 -
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	error = -1;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (msg.msg_flags & MSG_CTRUNC) {
 -		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -		goto done;
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_bintime(cmsghdr) < 0)
 +			break;
  	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -		goto done;
 -	}
 +static int
 +t_bintime(void)
 +{
 +	return (t_generic(t_bintime_client, t_bintime_server));
 +}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -		logmsgx("CMSG_FIRSTHDR is NULL");
 -		goto done;
 -	}
 +static int
 +t_cmsg_len_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t size, cmsg_size;
 +	socklen_t socklen;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
 +
 +	rv = -2;
  
 -	if (cmptr->cmsg_level != SOL_SOCKET) {
 -		logmsgx("cmsg_level %d != SOL_SOCKET", cmptr->cmsg_level);
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
  		goto done;
  	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_CREDS, sizeof(struct cmsgcred));
 +	cmsghdr = CMSG_FIRSTHDR(&msghdr);
  
 -	if (cmptr->cmsg_type != SCM_TIMESTAMP) {
 -		logmsgx("cmsg_type %d != SCM_TIMESTAMP", cmptr->cmsg_type);
 +	if (socket_connect(fd) < 0)
  		goto done;
 +
 +	size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
 +	rv = -1;
 +	for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
 +		cmsghdr->cmsg_len = socklen;
 +		dbgmsg("send: data size %zu", size);
 +		dbgmsg("send: msghdr.msg_controllen %u",
 +		    (u_int)msghdr.msg_controllen);
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +		if (sendmsg(fd, &msghdr, 0) < 0)
 +			continue;
 +		logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
 +		    (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
 +		break;
  	}
 +	if (socklen == CMSG_LEN(0))
 +		rv = 0;
  
 -	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +	if (sync_send() < 0) {
 +		rv = -2;
  		goto done;
  	}
 +done:
 +	free(cmsg_data);
 +	return (rv);
 +}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +static int
 +t_cmsg_len_server(int fd1)
 +{
 +	int fd2, rv;
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 -	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
 +	if (sync_send() < 0)
 +		return (-2);
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 -		goto done;
 -	}
 +	rv = -2;
  
 -	error = 0;
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 +	if (sync_recv() < 0)
 +		goto done;
 +
 +	rv = 0;
  done:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_cmsg_len(void)
 +{
 +	return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
  }
  
  static int
 -t_timestamp(void)
 +t_peercred_client(int fd)
  {
 -	int error, fd;
 +	struct xucred xucred;
 +	socklen_t len;
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +	if (sync_recv() < 0)
 +		return (-1);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	if (socket_connect(fd) < 0)
 +		return (-1);
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		return (-1);
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_timestamp_client();
 -	}
 +	if (check_xucred(&xucred, len) < 0)
 +		return (-1);
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	return (0);
 +}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 +	if (sync_send() < 0)
  		return (-2);
 +
 +	fd2 = socket_accept(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
  	}
 -	return (error);
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (socket_close(fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	return (t_generic(t_peercred_client, t_peercred_server));
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/unix_cmsg.t	2013-02-08 12:08:52.000000000 +0200
 @@ -11,47 +11,78 @@ n=0
  
  run()
  {
 -	result=`${cmd} -t $2 $3 $4 2>&1`
 -	if [ $? -eq 0 ]; then
 -		echo -n "ok $1"
 -	else
 -		echo -n "not ok $1"
 +	result=`${cmd} -t $2 $3 ${5%% *} 2>&1`
 +	if [ $? -ne 0 ]; then
 +		echo -n "not "
  	fi
 -	echo " -" $5
 +	echo "ok $1 - $4 ${5#* }"
  	echo ${result} | grep -E "SERVER|CLIENT" | while read line; do
  		echo "# ${line}"
  	done
  }
  
 -echo "1..15"
 +echo "1..47"
  
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"4 Sending cmsgcred, receiving sockcred" \
 +	"5 Sending, receiving timeval" \
 +	"6 Sending, receiving bintime" \
 +	"7 Check cmsghdr.cmsg_len"
  do
 -	n=`expr ${n} + 1`
 -	run ${n} stream "" ${n} "STREAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
 +	done
 +done
 +
 +for t1 in \
 +	"2 Receiving sockcred (listening socket)" \
 +	"3 Receiving sockcred (accepted socket)"
 +do
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)"
 +	do
 +		n=$((n + 1))
 +		run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
 +	done
  done
  
 -i=0
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option"
 +
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"3 Sending cmsgcred, receiving sockcred" \
 +	"4 Sending, receiving timeval" \
 +	"5 Sending, receiving bintime" \
 +	"6 Check cmsghdr.cmsg_len"
  do
 -	i=`expr ${i} + 1`
 -	n=`expr ${n} + 1`
 -	run ${n} dgram "" ${i} "DGRAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
 +	done
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 -
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +for t1 in \
 +	"2 Receiving sockcred"
 +do
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)"
 +	do
 +		n=$((n + 1))
 +		run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
 +	done
 +done

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Sat, 9 Feb 2013 14:35:42 +0200

 Proposed commit log:
 
 ----
 
 - Added tests for SCM_BINTIME, LOCAL_PEERCRED, cmsghdr.cmsg_len
 - Code that checks correctness of groups was corrected (getgroups(2) change)
 - unix_cmsg.c was completely redesigned and simplified
 - Use less timeout value in unix_cmsg.c for faster work
 - Added support for not sending data in a message, not sending data and
   data array associated with a cmsghdr structure in a message
 - Existent tests were improved
 - unix_cmsg.t was redesigned and simplified
 
 Correctness of unix_cmsg verified on 7.2-STABLE, 9.1-STABLE and 10-CURRENT.
 
 Submitted by:    Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
 
 ----
 
 I've found one bug with the working directory in unix_cmsg.c and
 simplified unix_cmsg.c:main() a bit, this is the corrected version.
 
 diff -ruNp unix_cmsg.orig/README unix_cmsg/README
 --- unix_cmsg.orig/README	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/README	2013-02-09 14:29:23.000000000 +0200
 @@ -1,127 +1,160 @@
  $FreeBSD: src/tools/regression/sockets/unix_cmsg/README,v 1.2 2012/11/17 01:53:57 svnexp Exp $
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 -
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 -Description of tests
 -====================
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
 +
 +Description
 +===========
 +
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
  
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.
 +    All these attempts should fail, since cmsg_len in all requests is less
 +    than CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 diff -ruNp unix_cmsg.orig/unix_cmsg.c unix_cmsg/unix_cmsg.c
 --- unix_cmsg.orig/unix_cmsg.c	2012-11-20 11:26:18.000000000 +0200
 +++ unix_cmsg/unix_cmsg.c	2013-02-09 03:10:58.000000000 +0200
 @@ -27,48 +27,46 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/tools/regression/sockets/unix_cmsg/unix_cmsg.c,v 1.5 2012/11/19 22:59:17 svnexp Exp $");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of
 + * a client process.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD: src/tools/regression
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	2
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_send;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_send[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", work_dir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: number is %s",
 +				    errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: number is %s",
 +				    errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: number is %s",
 +				    errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "test number is %s", errstr);
 +		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
 +		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for datagram "
 +			    "sockets does not exist", testno);
  	} else
 -		testno1 = 0;
 -
 -	if (dgramflag == 0 && streamflag == 0)
 -		dgramflag = streamflag = 1;
 +		testno = 0;
  
 -	if (dgramflag && streamflag && testno1 != 0)
 -		errx(EX_USAGE, "you can use particular test, only with datagram or stream sockets");
 -
 -	if (streamflag) {
 -		if (testno1 > TEST_STREAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for stream sockets does not exist",
 -			    testno1);
 -	} else {
 -		if (testno1 > TEST_DGRAM_NO_MAX)
 -			errx(EX_USAGE, "given test %u for datagram sockets does not exist",
 -			    testno1);
 +	if (!dgram_flag && !stream_flag) {
 +		if (testno != 0)
 +			errx(EXIT_FAILURE, "particular test number "
 +			    "can be used with the -t option only");
 +		dgram_flag = stream_flag = true;
  	}
  
 -	my_uid = getuid();
 -	my_euid = geteuid();
 -	my_gid = getgid();
 -	my_egid = getegid();
 -	switch (my_ngids = getgroups(sizeof(my_gids) / sizeof(my_gids[0]), my_gids)) {
 -	case -1:
 -		err(EX_SOFTWARE, "getgroups");
 -		/* NOTREACHED */
 -	case 0:
 -		errx(EX_OSERR, "getgroups returned 0 groups");
 -	}
 +	if (mkdtemp(work_dir) == NULL)
 +		err(EXIT_FAILURE, "mkdtemp(%s)", work_dir);
  
 -	sig_init();
 +	rv = EXIT_FAILURE;
 +	if (init() < 0)
 +		goto done;
  
 -	if (mkdtemp(tempdir) == NULL)
 -		err(EX_OSERR, "mkdtemp");
 +	if (stream_flag)
 +		if (run_tests(SOCK_STREAM, testno) < 0)
 +			goto done;
 +	if (dgram_flag)
 +		if (run_tests(SOCK_DGRAM, testno) < 0)
 +			goto done;
  
 -	if (streamflag) {
 -		sock_type = SOCK_STREAM;
 -		sock_type_str = "SOCK_STREAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_STREAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -		testno1 = 0;
 +	rv = EXIT_SUCCESS;
 +done:
 +	if (rmdir(work_dir) < 0) {
 +		logmsg("rmdir(%s)", work_dir);
 +		rv = EXIT_FAILURE;
  	}
 +	return (failed_flag ? EXIT_FAILURE : rv);
 +}
  
 -	if (dgramflag) {
 -		sock_type = SOCK_DGRAM;
 -		sock_type_str = "SOCK_DGRAM";
 -		if (testno1 == 0) {
 -			testno1 = 1;
 -			testno2 = TEST_DGRAM_NO_MAX;
 -		} else
 -			testno2 = testno1;
 -		if (run_tests(testno1, testno2) < 0)
 -			goto failed;
 -	}
 +static int
 +socket_close(int fd)
 +{
 +	int rv;
  
 -	if (rmdir(tempdir) < 0) {
 -		logmsg("rmdir(%s)", tempdir);
 -		return (EX_OSERR);
 +	rv = 0;
 +	if (close(fd) < 0) {
 +		logmsg("socket_close: close");
 +		rv = -1;
  	}
 -
 -	return (nfailed ? EX_OSERR : EX_OK);
 -
 -failed:
 -	if (rmdir(tempdir) < 0)
 -		logmsg("rmdir(%s)", tempdir);
 -	return (EX_OSERR);
 +	if (server_flag && fd == serv_sock_fd)
 +		if (unlink(serv_addr_sun.sun_path) < 0) {
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
 +			rv = -1;
 +		}
 +	return (rv);
  }
  
 -/*
 - * Create PF_LOCAL socket, if sock_path is not equal to NULL, then
 - * bind() it.  Return socket address in addr.  Return file descriptor
 - * or -1 if some error occurred.
 - */
  static int
 -create_socket(char *sock_path, size_t sock_path_len, struct sockaddr_un *addr)
 +socket_create(void)
  {
 -	int rv, fd;
 +	struct timeval tv;
 +	int fd;
  
 -	if ((fd = socket(PF_LOCAL, sock_type, 0)) < 0) {
 -		logmsg("create_socket: socket(PF_LOCAL, %s, 0)", sock_type_str);
 +	fd = socket(PF_LOCAL, sock_type, 0);
 +	if (fd < 0) {
 +		logmsg("socket_create: socket(PF_LOCAL, %s, 0)", sock_type_str);
  		return (-1);
  	}
 +	if (server_flag)
 +		serv_sock_fd = fd;
  
 -	if (sock_path != NULL) {
 -		if ((rv = snprintf(sock_path, sock_path_len, "%s/%s",
 -		    tempdir, myname)) < 0) {
 -			logmsg("create_socket: snprintf failed");
 -			goto failed;
 -		}
 -		if ((size_t)rv >= sock_path_len) {
 -			logmsgx("create_socket: too long path name for given buffer");
 -			goto failed;
 -		}
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
 +	    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
 +		logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
 +		goto failed;
 +	}
  
 -		memset(addr, 0, sizeof(*addr));
 -		addr->sun_family = AF_LOCAL;
 -		if (strlen(sock_path) >= sizeof(addr->sun_path)) {
 -			logmsgx("create_socket: too long path name (>= %lu) for local domain socket",
 -			    (u_long)sizeof(addr->sun_path));
 +	if (server_flag) {
 +		if (bind(fd, (struct sockaddr *)&serv_addr_sun,
 +		    serv_addr_sun.sun_len) < 0) {
 +			logmsg("socket_create: bind(%s)",
 +			    serv_addr_sun.sun_path);
  			goto failed;
  		}
 -		strcpy(addr->sun_path, sock_path);
 +		if (sock_type == SOCK_STREAM) {
 +			int val;
  
 -		if (bind(fd, (struct sockaddr *)addr, SUN_LEN(addr)) < 0) {
 -			logmsg("create_socket: bind(%s)", sock_path);
 -			goto failed;
 +			if (listen(fd, LISTENQ) < 0) {
 +				logmsg("socket_create: listen");
 +				goto failed;
 +			}
 +			val = fcntl(fd, F_GETFL, 0);
 +			if (val < 0) {
 +				logmsg("socket_create: fcntl(F_GETFL)");
 +				goto failed;
 +			}
 +			if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
 +				logmsg("socket_create: fcntl(F_SETFL)");
 +				goto failed;
 +			}
  		}
  	}
  
 @@ -468,1163 +688,1282 @@ create_socket(char *sock_path, size_t so
  
  failed:
  	if (close(fd) < 0)
 -		logmsg("create_socket: close");
 +		logmsg("socket_create: close");
 +	if (server_flag)
 +		if (unlink(serv_addr_sun.sun_path) < 0)
 +			logmsg("socket_close: unlink(%s)",
 +			    serv_addr_sun.sun_path);
  	return (-1);
  }
  
 -/*
 - * Call create_socket() for server listening socket.
 - * Return socket descriptor or -1 if some error occurred.
 - */
  static int
 -create_server_socket(void)
 +socket_connect(int fd)
  {
 -	return (create_socket(serv_sock_path, sizeof(serv_sock_path), &servaddr));
 -}
 +	dbgmsg("connect");
  
 -/*
 - * Create unbound socket.
 - */
 -static int
 -create_unbound_socket(void)
 -{
 -	return (create_socket((char *)NULL, 0, (struct sockaddr_un *)NULL));
 +	if (connect(fd, (struct sockaddr *)&serv_addr_sun,
 +	    serv_addr_sun.sun_len) < 0) {
 +		logmsg("socket_connect: connect(%s)", serv_addr_sun.sun_path);
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Close socket descriptor, if sock_path is not equal to NULL,
 - * then unlink the given path.
 - */
  static int
 -close_socket(const char *sock_path, int fd)
 +sync_recv(void)
  {
 -	int error = 0;
 +	ssize_t ssize;
 +	int fd;
 +	char buf;
  
 -	if (close(fd) < 0) {
 -		logmsg("close_socket: close");
 -		error = -1;
 -	}
 -	if (sock_path != NULL)
 -		if (unlink(sock_path) < 0) {
 -			logmsg("close_socket: unlink(%s)", sock_path);
 -			error = -1;
 -		}
 -	return (error);
 -}
 +	dbgmsg("sync: wait");
  
 -/*
 - * Connect to server (socket address in servaddr).
 - */
 -static int
 -connect_server(int fd)
 -{
 -	dbgmsg(("connecting to %s", serv_sock_path));
 +	fd = sync_fd[server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
  
 -	/*
 -	 * If PF_LOCAL listening socket's queue is full, then connect()
 -	 * returns ECONNREFUSED immediately, do not need timeout.
 -	 */
 -	if (connect(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
 -		logmsg("connect_server: connect(%s)", serv_sock_path);
 +	ssize = read(fd, &buf, 1);
 +	if (ssize < 0) {
 +		logmsg("sync_recv: read");
 +		return (-1);
 +	}
 +	if (ssize < 1) {
 +		logmsgx("sync_recv: read %zd of 1 byte", ssize);
  		return (-1);
  	}
  
 +	dbgmsg("sync: received");
 +
  	return (0);
  }
  
 -/*
 - * sendmsg() with timeout.
 - */
  static int
 -sendmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +sync_send(void)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending %lu bytes", (u_long)n));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sendmsg_timeout: cannot send message to %s (timeout)", serv_sock_path);
 -		return (-1);
 -	}
 -
 -	(void)alarm(TIMEOUT);
 +	ssize_t ssize;
 +	int fd;
  
 -	nsent = sendmsg(fd, msg, 0);
 +	dbgmsg("sync: send");
  
 -	(void)alarm(0);
 +	fd = sync_fd[server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
  
 -	if (nsent < 0) {
 -		logmsg("sendmsg_timeout: sendmsg");
 +	ssize = write(fd, "", 1);
 +	if (ssize < 0) {
 +		logmsg("sync_send: write");
  		return (-1);
  	}
 -
 -	if ((size_t)nsent != n) {
 -		logmsgx("sendmsg_timeout: sendmsg: short send: %ld of %lu bytes",
 -		    (long)nsent, (u_long)n);
 +	if (ssize < 1) {
 +		logmsgx("sync_send: sent %zd of 1 byte", ssize);
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * accept() with timeout.
 - */
  static int
 -accept_timeout(int listenfd)
 +message_send(int fd, const struct msghdr *msghdr)
  {
 -	int fd;
 -
 -	dbgmsg(("accepting connection"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("accept_timeout: cannot accept connection (timeout)");
 +	const struct cmsghdr *cmsghdr;
 +	size_t size;
 +	ssize_t ssize;
 +
 +	size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
 +	dbgmsg("send: data size %zu", size);
 +	dbgmsg("send: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	cmsghdr = CMSG_FIRSTHDR(msghdr);
 +	if (cmsghdr != NULL)
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +
 +	ssize = sendmsg(fd, msghdr, 0);
 +	if (ssize < 0) {
 +		logmsg("message_send: sendmsg");
 +		return (-1);
 +	}
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	(void)alarm(TIMEOUT);
 +	if (!send_data_flag)
 +		if (sync_send() < 0)
 +			return (-1);
  
 -	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	return (0);
 +}
  
 -	(void)alarm(0);
 +static int
 +message_sendn(int fd, struct msghdr *msghdr)
 +{
 +	u_int i;
  
 -	if (fd < 0) {
 -		logmsg("accept_timeout: accept");
 -		return (-1);
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +		if (message_send(fd, msghdr) < 0)
 +			return (-1);
  	}
 -
 -	return (fd);
 +	return (0);
  }
  
 -/*
 - * recvmsg() with timeout.
 - */
  static int
 -recvmsg_timeout(int fd, struct msghdr *msg, size_t n)
 +message_recv(int fd, struct msghdr *msghdr)
  {
 -	ssize_t nread;
 +	const struct cmsghdr *cmsghdr;
 +	size_t size;
 +	ssize_t ssize;
  
 -	dbgmsg(("receiving %lu bytes", (u_long)n));
 +	if (!send_data_flag)
 +		if (sync_recv() < 0)
 +			return (-1);
  
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("recvmsg_timeout: cannot receive message (timeout)");
 +	size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
 +	ssize = recvmsg(fd, msghdr, MSG_WAITALL);
 +	if (ssize < 0) {
 +		logmsg("message_recv: recvmsg");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nread = recvmsg(fd, msg, MSG_WAITALL);
 -
 -	(void)alarm(0);
 -
 -	if (nread < 0) {
 -		logmsg("recvmsg_timeout: recvmsg");
 +	if ((size_t)ssize != size) {
 +		logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
 +		    ssize, size);
  		return (-1);
  	}
  
 -	if ((size_t)nread != n) {
 -		logmsgx("recvmsg_timeout: recvmsg: short read: %ld of %lu bytes",
 -		    (long)nread, (u_long)n);
 +	dbgmsg("recv: data size %zd", ssize);
 +	dbgmsg("recv: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +	cmsghdr = CMSG_FIRSTHDR(msghdr);
 +	if (cmsghdr != NULL)
 +		dbgmsg("recv: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +
 +	if (memcmp(ipc_msg.buf_recv, ipc_msg.buf_send, size) != 0) {
 +		logmsgx("message_recv: received message has wrong content");
  		return (-1);
  	}
  
  	return (0);
  }
  
 -/*
 - * Wait for synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_recv(int fd)
 +socket_accept(int listenfd)
  {
 -	ssize_t nread;
 -	char buf;
 -
 -	dbgmsg(("waiting for sync message"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_recv: cannot receive sync message (timeout)");
 +	fd_set rset;
 +	struct timeval tv;
 +	int fd, rv, val;
 +
 +	dbgmsg("accept");
 +
 +	FD_ZERO(&rset);
 +	FD_SET(listenfd, &rset);
 +	tv.tv_sec = TIMEOUT;
 +	tv.tv_usec = 0;
 +	rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
 +	if (rv < 0) {
 +		logmsg("socket_accept: select");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nread = read(fd, &buf, 1);
 -
 -	(void)alarm(0);
 -
 -	if (nread < 0) {
 -		logmsg("sync_recv: read");
 +	if (rv == 0) {
 +		logmsgx("socket_accept: select timeout");
  		return (-1);
  	}
  
 -	if (nread != 1) {
 -		logmsgx("sync_recv: read: short read: %ld of 1 byte",
 -		    (long)nread);
 +	fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 +	if (fd < 0) {
 +		logmsg("socket_accept: accept");
  		return (-1);
  	}
  
 -	return (0);
 +	val = fcntl(fd, F_GETFL, 0);
 +	if (val < 0) {
 +		logmsg("socket_accept: fcntl(F_GETFL)");
 +		goto failed;
 +	}
 +	if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
 +		logmsg("socket_accept: fcntl(F_SETFL)");
 +		goto failed;
 +	}
 +
 +	return (fd);
 +
 +failed:
 +	if (close(fd) < 0)
 +		logmsg("socket_accept: close");
 +	return (-1);
  }
  
 -/*
 - * Send synchronization message (1 byte) with timeout.
 - */
  static int
 -sync_send(int fd)
 +check_msghdr(const struct msghdr *msghdr, size_t size)
  {
 -	ssize_t nsent;
 -
 -	dbgmsg(("sending sync message"));
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("sync_send: cannot send sync message (timeout)");
 +	if (msghdr->msg_flags & MSG_TRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_TRUNC");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	nsent = write(fd, "", 1);
 -
 -	(void)alarm(0);
 -
 -	if (nsent < 0) {
 -		logmsg("sync_send: write");
 +	if (msghdr->msg_flags & MSG_CTRUNC) {
 +		logmsgx("msghdr.msg_flags has MSG_CTRUNC");
  		return (-1);
  	}
 -
 -	if (nsent != 1) {
 -		logmsgx("sync_send: write: short write: %ld of 1 byte",
 -		    (long)nsent);
 +	if (msghdr->msg_controllen < size) {
 +		logmsgx("msghdr.msg_controllen %u < %zu",
 +		    (u_int)msghdr->msg_controllen, size);
 +		return (-1);
 +	}
 +	if (msghdr->msg_controllen > 0 && size == 0) {
 +		logmsgx("msghdr.msg_controllen %u > 0",
 +		    (u_int)msghdr->msg_controllen);
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * waitpid() for client with timeout.
 - */
  static int
 -wait_client(void)
 +check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
  {
 -	int status;
 -	pid_t pid;
 -
 -	if (sigsetjmp(env_alrm, 1) != 0) {
 -		logmsgx("wait_client: cannot get exit status of client PID %ld (timeout)",
 -		    (long)client_pid);
 +	if (cmsghdr == NULL) {
 +		logmsgx("cmsghdr is NULL");
  		return (-1);
  	}
 -
 -	(void)alarm(TIMEOUT);
 -
 -	pid = waitpid(client_pid, &status, 0);
 -
 -	(void)alarm(0);
 -
 -	if (pid == (pid_t)-1) {
 -		logmsg("wait_client: waitpid");
 +	if (cmsghdr->cmsg_level != SOL_SOCKET) {
 +		logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
 +		    cmsghdr->cmsg_level);
  		return (-1);
  	}
 -
 -	if (WIFEXITED(status)) {
 -		if (WEXITSTATUS(status) != 0) {
 -			logmsgx("wait_client: exit status of client PID %ld is %d",
 -			    (long)client_pid, WEXITSTATUS(status));
 -			return (-1);
 -		}
 -	} else {
 -		if (WIFSIGNALED(status))
 -			logmsgx("wait_client: abnormal termination of client PID %ld, signal %d%s",
 -			    (long)client_pid, WTERMSIG(status), WCOREDUMP(status) ? " (core file generated)" : "");
 -		else
 -			logmsgx("wait_client: termination of client PID %ld, unknown status",
 -			    (long)client_pid);
 +	if (cmsghdr->cmsg_type != type) {
 +		logmsgx("cmsghdr.cmsg_type %d != %d",
 +		    cmsghdr->cmsg_type, type);
 +		return (-1);
 +	}
 +	if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
 +		logmsgx("cmsghdr.cmsg_len %u != %zu",
 +		    (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
  		return (-1);
  	}
 -
  	return (0);
  }
  
 -/*
 - * Check if n supplementary GIDs in gids are correct.  (my_gids + 1)
 - * has (my_ngids - 1) supplementary GIDs of current process.
 - */
  static int
 -check_groups(const gid_t *gids, int n)
 +check_groups(const char *gid_arr_str, const gid_t *gid_arr,
 +    const char *gid_num_str, int gid_num, bool all_gids)
  {
 -	char match[NGROUPS_MAX] = { 0 };
 -	int error, i, j;
 +	int i;
  
 -	if (n != my_ngids - 1) {
 -		logmsgx("wrong number of groups %d != %d (returned from getgroups() - 1)",
 -		    n, my_ngids - 1);
 -		error = -1;
 -	} else
 -		error = 0;
 -	for (i = 0; i < n; ++i) {
 -		for (j = 1; j < my_ngids; ++j) {
 -			if (gids[i] == my_gids[j]) {
 -				if (match[j]) {
 -					logmsgx("duplicated GID %lu",
 -					    (u_long)gids[i]);
 -					error = -1;
 -				} else
 -					match[j] = 1;
 -				break;
 -			}
 +	for (i = 0; i < gid_num; ++i)
 +		dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
 +
 +	if (all_gids) {
 +		if (gid_num != proc_cred.gid_num) {
 +			logmsgx("%s %d != %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
 -		if (j == my_ngids) {
 -			logmsgx("unexpected GID %lu", (u_long)gids[i]);
 -			error = -1;
 +	} else {
 +		if (gid_num > proc_cred.gid_num) {
 +			logmsgx("%s %d > %d", gid_num_str, gid_num,
 +			    proc_cred.gid_num);
 +			return (-1);
  		}
  	}
 -	for (j = 1; j < my_ngids; ++j)
 -		if (match[j] == 0) {
 -			logmsgx("did not receive supplementary GID %u", my_gids[j]);
 -			error = -1;
 -		}
 -	return (error);
 +	if (memcmp(gid_arr, proc_cred.gid_arr,
 +	    gid_num * sizeof(*gid_arr)) != 0) {
 +		logmsgx("%s content is wrong", gid_arr_str);
 +		for (i = 0; i < gid_num; ++i)
 +			if (gid_arr[i] != proc_cred.gid_arr[i]) {
 +				logmsgx("%s[%d] %lu != %lu",
 +				    gid_arr_str, i, (u_long)gid_arr[i],
 +				    (u_long)proc_cred.gid_arr[i]);
 +				break;
 +			}
 +		return (-1);
 +	}
 +	return (0);
  }
  
 -/*
 - * Send n messages with data and control message with SCM_CREDS type
 - * to server and exit.
 - */
 -static void
 -t_cmsgcred_client(u_int n)
 +static int
 +check_xucred(const struct xucred *xucred, socklen_t len)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred))];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -	u_int i;
 +	if (len != sizeof(*xucred)) {
 +		logmsgx("option value size %zu != %zu",
 +		    (size_t)len, sizeof(*xucred));
 +		return (-1);
 +	}
  
 -	assert(n == 1 || n == 2);
 +	dbgmsg("xucred.cr_version %u", xucred->cr_version);
 +	dbgmsg("xucred.cr_uid %lu", (u_long)xucred->cr_uid);
 +	dbgmsg("xucred.cr_ngroups %d", xucred->cr_ngroups);
 +
 +	if (xucred->cr_version != XUCRED_VERSION) {
 +		logmsgx("xucred.cr_version %u != %d",
 +		    xucred->cr_version, XUCRED_VERSION);
 +		return (-1);
 +	}
 +	if (xucred->cr_uid != proc_cred.euid) {
 +		logmsgx("xucred.cr_uid %lu != %lu (EUID)",
 +		   (u_long)xucred->cr_uid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups == 0) {
 +		logmsgx("xucred.cr_ngroups == 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups < 0) {
 +		logmsgx("xucred.cr_ngroups < 0");
 +		return (-1);
 +	}
 +	if (xucred->cr_ngroups > XU_NGROUPS) {
 +		logmsgx("xucred.cr_ngroups %hu > %u (max)",
 +		    xucred->cr_ngroups, XU_NGROUPS);
 +		return (-1);
 +	}
 +	if (xucred->cr_groups[0] != proc_cred.egid) {
 +		logmsgx("xucred.cr_groups[0] %lu != %lu (EGID)",
 +		    (u_long)xucred->cr_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (check_groups("xucred.cr_groups", xucred->cr_groups,
 +	    "xucred.cr_ngroups", xucred->cr_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct cmsgcred *cmsgcred;
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(*cmsgcred)) < 0)
 +		return (-1);
  
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 +	cmsgcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
  
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) : sizeof(control_un.control);
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct cmsgcred));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_CREDS;
 -
 -	for (i = 0; i < n; ++i) {
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmsgcred->cmcred_pid);
 +	dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmsgcred->cmcred_uid);
 +	dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmsgcred->cmcred_euid);
 +	dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmsgcred->cmcred_gid);
 +	dbgmsg("cmsgcred.cmcred_ngroups %d", cmsgcred->cmcred_ngroups);
 +
 +	if (cmsgcred->cmcred_pid != client_pid) {
 +		logmsgx("cmsgcred.cmcred_pid %ld != %ld",
 +		    (long)cmsgcred->cmcred_pid, (long)client_pid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_uid != proc_cred.uid) {
 +		logmsgx("cmsgcred.cmcred_uid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_euid != proc_cred.euid) {
 +		logmsgx("cmsgcred.cmcred_euid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_gid != proc_cred.gid) {
 +		logmsgx("cmsgcred.cmcred_gid %lu != %lu",
 +		    (u_long)cmsgcred->cmcred_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups == 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups == 0");
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups < 0) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d < 0",
 +		    cmsgcred->cmcred_ngroups);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_ngroups > CMGROUP_MAX) {
 +		logmsgx("cmsgcred.cmcred_ngroups %d > %d",
 +		    cmsgcred->cmcred_ngroups, CMGROUP_MAX);
 +		return (-1);
 +	}
 +	if (cmsgcred->cmcred_groups[0] != proc_cred.egid) {
 +		logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
 +		    (u_long)cmsgcred->cmcred_groups[0], (u_long)proc_cred.egid);
 +		return (-1);
  	}
 +	if (check_groups("cmsgcred.cmcred_groups", cmsgcred->cmcred_groups,
 +	    "cmsgcred.cmcred_ngroups", cmsgcred->cmcred_ngroups, false) < 0)
 +		return (-1);
 +	return (0);
 +}
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +static int
 +check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
 +{
 +	const struct sockcred *sockcred;
  
 -	_exit(0);
 +	if (check_cmsghdr(cmsghdr, SCM_CREDS,
 +	    SOCKCREDSIZE(proc_cred.gid_num)) < 0)
 +		return (-1);
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	sockcred = (struct sockcred *)CMSG_DATA(cmsghdr);
  
 -failed:
 -	_exit(1);
 +	dbgmsg("sockcred.sc_uid %lu", (u_long)sockcred->sc_uid);
 +	dbgmsg("sockcred.sc_euid %lu", (u_long)sockcred->sc_euid);
 +	dbgmsg("sockcred.sc_gid %lu", (u_long)sockcred->sc_gid);
 +	dbgmsg("sockcred.sc_egid %lu", (u_long)sockcred->sc_egid);
 +	dbgmsg("sockcred.sc_ngroups %d", sockcred->sc_ngroups);
 +
 +	if (sockcred->sc_uid != proc_cred.uid) {
 +		logmsgx("sockcred.sc_uid %lu != %lu",
 +		    (u_long)sockcred->sc_uid, (u_long)proc_cred.uid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_euid != proc_cred.euid) {
 +		logmsgx("sockcred.sc_euid %lu != %lu",
 +		    (u_long)sockcred->sc_euid, (u_long)proc_cred.euid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_gid != proc_cred.gid) {
 +		logmsgx("sockcred.sc_gid %lu != %lu",
 +		    (u_long)sockcred->sc_gid, (u_long)proc_cred.gid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_egid != proc_cred.egid) {
 +		logmsgx("sockcred.sc_egid %lu != %lu",
 +		    (u_long)sockcred->sc_egid, (u_long)proc_cred.egid);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups == 0) {
 +		logmsgx("sockcred.sc_ngroups == 0");
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups < 0) {
 +		logmsgx("sockcred.sc_ngroups %d < 0",
 +		    sockcred->sc_ngroups);
 +		return (-1);
 +	}
 +	if (sockcred->sc_ngroups != proc_cred.gid_num) {
 +		logmsgx("sockcred.sc_ngroups %d != %u",
 +		    sockcred->sc_ngroups, proc_cred.gid_num);
 +		return (-1);
 +	}
 +	if (check_groups("sockcred.sc_groups", sockcred->sc_groups,
 +	    "sockcred.sc_ngroups", sockcred->sc_ngroups, true) < 0)
 +		return (-1);
 +	return (0);
  }
  
 -/*
 - * Receive two messages with data and control message with SCM_CREDS
 - * type followed by struct cmsgcred{} from client.  fd1 is a listen
 - * socket for stream sockets or simply socket for datagram sockets.
 - */
  static int
 -t_cmsgcred_server(int fd1)
 +check_scm_timestamp(struct cmsghdr *cmsghdr)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct cmsgcred)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 -	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct cmsgcred *cmcredptr;
 -	socklen_t controllen;
 -	int error, error2, fd2;
 -	u_int i;
 +	const struct timeval *timeval;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -	} else
 -		fd2 = fd1;
 +	if (check_cmsghdr(cmsghdr, SCM_TIMESTAMP, sizeof(struct timeval)) < 0)
 +		return (-1);
  
 -	error = 0;
 +	timeval = (struct timeval *)CMSG_DATA(cmsghdr);
  
 -	controllen = sizeof(control_un.control);
 +	dbgmsg("timeval.tv_sec %"PRIdMAX", timeval.tv_usec %"PRIdMAX,
 +	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec);
  
 -	for (i = 0; i < 2; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof(buf);
 +	return (0);
 +}
  
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = controllen;
 -		msg.msg_flags = 0;
 +static int
 +check_scm_bintime(struct cmsghdr *cmsghdr)
 +{
 +	const struct bintime *bintime;
  
 -		controllen = CMSG_SPACE(sizeof(struct cmsgcred));
 +	if (check_cmsghdr(cmsghdr, SCM_BINTIME, sizeof(struct bintime)) < 0)
 +		return (-1);
  
 -		if (recvmsg_timeout(fd2, &msg, sizeof(buf)) < 0)
 -			goto failed;
 +	bintime = (struct bintime *)CMSG_DATA(cmsghdr);
  
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("#%u control data was truncated, MSG_CTRUNC flag is on",
 -			    i);
 -			goto next_error;
 -		}
 +	dbgmsg("bintime.sec %"PRIdMAX", bintime.frac %"PRIu64,
 +	    (intmax_t)bintime->sec, bintime->frac);
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 -		}
 +	return (0);
 +}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
 +{
 +	msghdr->msg_name = NULL;
 +	msghdr->msg_namelen = 0;
 +	if (send_data_flag) {
 +		iov->iov_base = server_flag ?
 +		    ipc_msg.buf_recv : ipc_msg.buf_send;
 +		iov->iov_len = ipc_msg.buf_size;
 +		msghdr->msg_iov = iov;
 +		msghdr->msg_iovlen = 1;
 +	} else {
 +		msghdr->msg_iov = NULL;
 +		msghdr->msg_iovlen = 0;
 +	}
 +	msghdr->msg_control = cmsg_data;
 +	msghdr->msg_flags = 0;
 +}
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +static void
 +msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size)
 +{
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	msghdr->msg_controllen = cmsg_size;
 +	dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
 +	    msghdr->msg_iov->iov_len : (size_t)0);
 +	dbgmsg("init: msghdr.msg_controllen %u",
 +	    (u_int)msghdr->msg_controllen);
 +}
  
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 -		}
 +static void
 +msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
 +    void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
 +{
 +	struct cmsghdr *cmsghdr;
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +	msghdr_init_generic(msghdr, iov, cmsg_data);
 +	if (cmsg_data != NULL) {
 +		msghdr->msg_controllen = send_array_flag ?
 +		    cmsg_size : CMSG_SPACE(0);
 +		cmsghdr = CMSG_FIRSTHDR(msghdr);
 +		cmsghdr->cmsg_level = SOL_SOCKET;
 +		cmsghdr->cmsg_type = type;
 +		cmsghdr->cmsg_len = CMSG_LEN(send_array_flag ? arr_size : 0);
 +	} else
 +		msghdr->msg_controllen = 0;
 +}
  
 -		if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(sizeof(struct cmsgcred))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct cmsgcred)));
 -			goto next_error;
 -		}
 +static int
 +t_generic(int (*client_func)(int), int (*server_func)(int))
 +{
 +	int fd, rv, rv_client;
  
 -		cmcredptr = (const struct cmsgcred *)CMSG_DATA(cmptr);
 +	switch (client_fork()) {
 +	case 0:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = client_func(fd);
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = server_func(fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
 +		}
 +		break;
 +	default:
 +		rv = -2;
 +	}
 +	return (rv);
 +}
  
 -		error2 = 0;
 -		if (cmcredptr->cmcred_pid != client_pid) {
 -			logmsgx("#%u cmcred_pid %ld != %ld (PID of client)",
 -			    i, (long)cmcredptr->cmcred_pid, (long)client_pid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_uid != my_uid) {
 -			logmsgx("#%u cmcred_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_euid != my_euid) {
 -			logmsgx("#%u cmcred_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_gid != my_gid) {
 -			logmsgx("#%u cmcred_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)cmcredptr->cmcred_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (cmcredptr->cmcred_ngroups == 0) {
 -			logmsgx("#%u cmcred_ngroups = 0, this is wrong", i);
 -			error2 = 1;
 -		} else {
 -			if (cmcredptr->cmcred_ngroups > NGROUPS_MAX) {
 -				logmsgx("#%u cmcred_ngroups %d > %u (NGROUPS_MAX)",
 -				    i, cmcredptr->cmcred_ngroups, NGROUPS_MAX);
 -				error2 = 1;
 -			} else if (cmcredptr->cmcred_ngroups < 0) {
 -				logmsgx("#%u cmcred_ngroups %d < 0",
 -				    i, cmcredptr->cmcred_ngroups);
 -				error2 = 1;
 -			} else {
 -				dbgmsg(("#%u cmcred_ngroups = %d", i,
 -				    cmcredptr->cmcred_ngroups));
 -				if (cmcredptr->cmcred_groups[0] != my_egid) {
 -					logmsgx("#%u cmcred_groups[0] %lu != %lu (EGID of current process)",
 -					    i, (u_long)cmcredptr->cmcred_groups[0], (u_long)my_egid);
 -					error2 = 1;
 -				}
 -				if (check_groups(cmcredptr->cmcred_groups + 1, cmcredptr->cmcred_ngroups - 1) < 0) {
 -					logmsgx("#%u cmcred_groups has wrong GIDs", i);
 -					error2 = 1;
 -				}
 -			}
 -		}
 +static int
 +t_cmsgcred_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -		if (error2)
 -			goto next_error;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header", i);
 -			goto next_error;
 -		}
 +	rv = -2;
  
 -		continue;
 -next_error:
 -		error = -1;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_CREDS, sizeof(struct cmsgcred));
  
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred(void)
 +t_cmsgcred_server(int fd1)
  {
 -	int error, fd;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(2);
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if ((error = t_cmsgcred_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_cmsgcred(void)
 +{
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_server));
  }
  
 -/*
 - * Send two messages with data to server and exit.
 - */
 -static void
 -t_sockcred_client(int type)
 +static int
 +t_sockcred_client(int type, int fd)
  {
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	int fd;
 -	u_int i;
 -
 -	assert(type == 0 || type == 1);
 +	int rv;
  
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (type == 1)
 -		if (sync_recv(fd) < 0)
 -			goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = NULL;
 -	msg.msg_controllen = 0;
 -	msg.msg_flags = 0;
 -
 -	for (i = 0; i < 2; ++i)
 -		if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -			goto failed_close;
 +	msghdr_init_client(&msghdr, iov, NULL, 0, 0, 0);
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -	_exit(0);
 +	if (type == 2)
 +		if (sync_recv() < 0)
 +			goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_CREDS
 - * type followed by struct sockcred{} and if n is not equal 1, then
 - * receive another one message with data.  fd1 is a listen socket for
 - * stream sockets or simply socket for datagram sockets.  If type is
 - * 1, then set LOCAL_CREDS option for accepted stream socket.
 - */
  static int
 -t_sockcred_server(int type, int fd1, u_int n)
 +t_sockcred_server(int type, int fd1)
  {
 -	char buf[IPC_MESSAGE_SIZE];
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct sockcred *sockcred;
 -	int error, error2, fd2, optval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
  	u_int i;
 +	int fd2, rv, val;
  
 -	assert(n == 1 || n == 2);
 -	assert(type == 0 || type == 1);
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 -		if (type == 1) {
 -			optval = 1;
 -			if (setsockopt(fd2, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -				logmsg("setsockopt(LOCAL_CREDS) for accepted socket");
 -				if (errno == ENOPROTOOPT) {
 -					error = -1;
 -					goto done_close;
 -				}
 -				goto failed;
 -			}
 -			if (sync_send(fd2) < 0)
 -				goto failed;
 -		}
 -	} else
 -		fd2 = fd1;
 -
 -	error = 0;
 -
 -	for (i = 0; i < n; ++i) {
 -		iov[0].iov_base = buf;
 -		iov[0].iov_len = sizeof buf;
 -
 -		msg.msg_name = NULL;
 -		msg.msg_namelen = 0;
 -		msg.msg_iov = iov;
 -		msg.msg_iovlen = 1;
 -		msg.msg_control = control_un.control;
 -		msg.msg_controllen = sizeof control_un.control;
 -		msg.msg_flags = 0;
 -
 -		if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -			goto failed;
 -
 -		if (msg.msg_flags & MSG_CTRUNC) {
 -			logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -			goto next_error;
 -		}
 -
 -		if (i != 0 && sock_type == SOCK_STREAM) {
 -			if (msg.msg_controllen != 0) {
 -				logmsgx("second message has control data, this is wrong for stream sockets");
 -				goto next_error;
 -			}
 -			dbgmsg(("#%u msg_controllen = %u", i,
 -			    (u_int)msg.msg_controllen));
 -			continue;
 -		}
 +	cmsg_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
 -		if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -			logmsgx("#%u msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -			    i, (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -			goto next_error;
 +	if (type == 1) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
  		}
 +	}
  
 -		if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -			logmsgx("CMSG_FIRSTHDR is NULL");
 -			goto next_error;
 -		}
 +	if (sync_send() < 0)
 +		goto done;
  
 -		dbgmsg(("#%u msg_controllen = %u, cmsg_len = %u", i,
 -		    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -		if (cmptr->cmsg_level != SOL_SOCKET) {
 -			logmsgx("#%u cmsg_level %d != SOL_SOCKET", i,
 -			    cmptr->cmsg_level);
 -			goto next_error;
 +	if (type == 2) {
 +		dbgmsg("setting LOCAL_CREDS");
 +		val = 1;
 +		if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +			logmsg("setsockopt(LOCAL_CREDS)");
 +			goto done;
 +		}
 +		if (sync_send() < 0)
 +			goto done;
 +	}
 +
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
  		}
  
 -		if (cmptr->cmsg_type != SCM_CREDS) {
 -			logmsgx("#%u cmsg_type %d != SCM_CREDS", i,
 -			    cmptr->cmsg_type);
 -			goto next_error;
 -		}
 +		if (i > 1 && sock_type == SOCK_STREAM) {
 +			if (check_msghdr(&msghdr, 0) < 0)
 +				break;
 +		} else {
 +			if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +				break;
  
 -		if (cmptr->cmsg_len < CMSG_LEN(SOCKCREDSIZE(1))) {
 -			logmsgx("#%u cmsg_len %u != %lu (CMSG_LEN(SOCKCREDSIZE(1)))",
 -			    i, (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(SOCKCREDSIZE(1)));
 -			goto next_error;
 +			cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
  		}
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -		sockcred = (const struct sockcred *)CMSG_DATA(cmptr);
 +static int
 +t_sockcred_1(void)
 +{
 +	u_int i;
 +	int fd, rv, rv_client;
  
 -		error2 = 0;
 -		if (sockcred->sc_uid != my_uid) {
 -			logmsgx("#%u sc_uid %lu != %lu (UID of current process)",
 -			    i, (u_long)sockcred->sc_uid, (u_long)my_uid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_euid != my_euid) {
 -			logmsgx("#%u sc_euid %lu != %lu (EUID of current process)",
 -			    i, (u_long)sockcred->sc_euid, (u_long)my_euid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_gid != my_gid) {
 -			logmsgx("#%u sc_gid %lu != %lu (GID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_gid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_egid != my_egid) {
 -			logmsgx("#%u sc_egid %lu != %lu (EGID of current process)",
 -			    i, (u_long)sockcred->sc_gid, (u_long)my_egid);
 -			error2 = 1;
 -		}
 -		if (sockcred->sc_ngroups > NGROUPS_MAX) {
 -			logmsgx("#%u sc_ngroups %d > %u (NGROUPS_MAX)",
 -			    i, sockcred->sc_ngroups, NGROUPS_MAX);
 -			error2 = 1;
 -		} else if (sockcred->sc_ngroups < 0) {
 -			logmsgx("#%u sc_ngroups %d < 0",
 -			    i, sockcred->sc_ngroups);
 -			error2 = 1;
 -		} else {
 -			dbgmsg(("#%u sc_ngroups = %d", i, sockcred->sc_ngroups));
 -			if (check_groups(sockcred->sc_groups, sockcred->sc_ngroups) < 0) {
 -				logmsgx("#%u sc_groups has wrong GIDs", i);
 -				error2 = 1;
 +	switch (client_fork()) {
 +	case 0:
 +		for (i = 1; i <= 2; ++i) {
 +			dbgmsg("client #%u", i);
 +			fd = socket_create();
 +			if (fd < 0)
 +				rv = -2;
 +			else {
 +				rv = t_sockcred_client(1, fd);
 +				if (socket_close(fd) < 0)
 +					rv = -2;
  			}
 +			if (rv != 0)
 +				break;
  		}
 -
 -		if (error2)
 -			goto next_error;
 -
 -		if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -			logmsgx("#%u control data has extra header, this is wrong",
 -			    i);
 -			goto next_error;
 +		client_exit(rv);
 +		break;
 +	case 1:
 +		fd = socket_create();
 +		if (fd < 0)
 +			rv = -2;
 +		else {
 +			rv = t_sockcred_server(1, fd);
 +			if (rv == 0)
 +				rv = t_sockcred_server(3, fd);
 +			rv_client = client_wait();
 +			if (rv == 0 || (rv == -2 && rv_client != 0))
 +				rv = rv_client;
 +			if (socket_close(fd) < 0)
 +				rv = -2;
  		}
 -
 -		continue;
 -next_error:
 -		error = -1;
 +		break;
 +	default:
 +		rv = -2;
  	}
  
 -done_close:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_sockcred_2_client(int fd)
 +{
 +	return (t_sockcred_client(2, fd));
  }
  
  static int
 -t_sockcred(int type)
 +t_sockcred_2_server(int fd)
  {
 -	int error, fd, optval;
 +	return (t_sockcred_server(2, fd));
 +}
  
 -	assert(type == 0 || type == 1);
 +static int
 +t_sockcred_2(void)
 +{
 +	return (t_generic(t_sockcred_2_client, t_sockcred_2_server));
 +}
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +static int
 +t_cmsgcred_sockcred_server(int fd1)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data, *cmsg1_data, *cmsg2_data;
 +	size_t cmsg_size, cmsg1_size, cmsg2_size;
 +	u_int i;
 +	int fd2, rv, val;
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	if (type == 0) {
 -		optval = 1;
 -		if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -			logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -			    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -			if (errno == ENOPROTOOPT) {
 -				error = -1;
 -				goto done_close;
 -			}
 -			goto failed;
 -		}
 +	cmsg1_size = CMSG_SPACE(SOCKCREDSIZE(proc_cred.gid_num));
 +	cmsg2_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg1_data = malloc(cmsg1_size);
 +	cmsg2_data = malloc(cmsg2_size);
 +	if (cmsg1_data == NULL || cmsg2_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	dbgmsg("setting LOCAL_CREDS");
 +	val = 1;
 +	if (setsockopt(fd1, 0, LOCAL_CREDS, &val, sizeof(val)) < 0) {
 +		logmsg("setsockopt(LOCAL_CREDS)");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_sockcred_client(type);
 -	}
 +	if (sync_send() < 0)
 +		goto done;
  
 -	if ((error = t_sockcred_server(type, fd, 2)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if (wait_client() < 0)
 -		goto failed;
 +	cmsg_data = cmsg1_data;
 +	cmsg_size = cmsg1_size;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 -	}
 -	return (error);
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 -}
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (i == 1 || sock_type == SOCK_DGRAM) {
 +			if (check_scm_creds_sockcred(cmsghdr) < 0)
 +				break;
 +		} else {
 +			if (check_scm_creds_cmsgcred(cmsghdr) < 0)
 +				break;
 +		}
  
 -static int
 -t_sockcred_stream1(void)
 -{
 -	return (t_sockcred(0));
 +		cmsg_data = cmsg2_data;
 +		cmsg_size = cmsg2_size;
 +	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg1_data);
 +	free(cmsg2_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
  }
  
  static int
 -t_sockcred_stream2(void)
 +t_cmsgcred_sockcred(void)
  {
 -	return (t_sockcred(1));
 +	return (t_generic(t_cmsgcred_client, t_cmsgcred_sockcred_server));
  }
  
  static int
 -t_sockcred_dgram(void)
 +t_timeval_client(int fd)
  {
 -	return (t_sockcred(0));
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
 +
 +	if (sync_recv() < 0)
 +		return (-2);
 +
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_TIMESTAMP, sizeof(struct timeval));
 +
 +	if (socket_connect(fd) < 0)
 +		goto done;
 +
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
 +
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
  static int
 -t_cmsgcred_sockcred(void)
 +t_timeval_server(int fd1)
  {
 -	int error, fd, optval;
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
  
 -	if ((fd = create_server_socket()) < 0)
 +	if (sync_send() < 0)
  		return (-2);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	fd2 = -1;
 +	rv = -2;
  
 -	optval = 1;
 -	if (setsockopt(fd, 0, LOCAL_CREDS, &optval, sizeof optval) < 0) {
 -		logmsg("setsockopt(LOCAL_CREDS) for %s socket",
 -		    sock_type == SOCK_STREAM ? "stream listening" : "datagram");
 -		if (errno == ENOPROTOOPT) {
 -			error = -1;
 -			goto done_close;
 -		}
 -		goto failed;
 -	}
 -
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct timeval));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_cmsgcred_client(1);
 -	}
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 -	if ((error = t_sockcred_server(0, fd, 1)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -done_close:
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 -		return (-2);
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_timestamp(cmsghdr) < 0)
 +			break;
  	}
 -	return (error);
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +static int
 +t_timeval(void)
 +{
 +	return (t_generic(t_timeval_client, t_timeval_server));
  }
  
 -/*
 - * Send one message with data and control message with SCM_TIMESTAMP
 - * type to server and exit.
 - */
 -static void
 -t_timestamp_client(void)
 +static int
 +t_bintime_client(int fd)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval))];
 -	} control_un;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	int fd;
 -
 -	if ((fd = create_unbound_socket()) < 0)
 -		goto failed;
 -
 -	if (connect_server(fd) < 0)
 -		goto failed_close;
 -
 -	iov[0].iov_base = ipc_message;
 -	iov[0].iov_len = IPC_MESSAGE_SIZE;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = no_control_data ?
 -	    sizeof(struct cmsghdr) :sizeof control_un.control;
 -	msg.msg_flags = 0;
 -
 -	cmptr = CMSG_FIRSTHDR(&msg);
 -	cmptr->cmsg_len = CMSG_LEN(no_control_data ?
 -	    0 : sizeof(struct timeval));
 -	cmptr->cmsg_level = SOL_SOCKET;
 -	cmptr->cmsg_type = SCM_TIMESTAMP;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
  
 -	if (sendmsg_timeout(fd, &msg, IPC_MESSAGE_SIZE) < 0)
 -		goto failed_close;
 +	rv = -2;
  
 -	if (close_socket((const char *)NULL, fd) < 0)
 -		goto failed;
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_BINTIME, sizeof(struct bintime));
  
 -	_exit(0);
 +	if (socket_connect(fd) < 0)
 +		goto done;
  
 -failed_close:
 -	(void)close_socket((const char *)NULL, fd);
 +	if (message_sendn(fd, &msghdr) < 0)
 +		goto done;
  
 -failed:
 -	_exit(1);
 +	rv = 0;
 +done:
 +	free(cmsg_data);
 +	return (rv);
  }
  
 -/*
 - * Receive one message with data and control message with SCM_TIMESTAMP
 - * type followed by struct timeval{} from client.
 - */
  static int
 -t_timestamp_server(int fd1)
 +t_bintime_server(int fd1)
  {
 -	union {
 -		struct cmsghdr	cm;
 -		char	control[CMSG_SPACE(sizeof(struct timeval)) + EXTRA_CMSG_SPACE];
 -	} control_un;
 -	char buf[IPC_MESSAGE_SIZE];
 -	int error, fd2;
 -	struct msghdr msg;
 +	struct msghdr msghdr;
  	struct iovec iov[1];
 -	struct cmsghdr *cmptr;
 -	const struct timeval *timeval;
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t cmsg_size;
 +	u_int i;
 +	int fd2, rv;
 +
 +	if (sync_send() < 0)
 +		return (-2);
 +
 +	fd2 = -1;
 +	rv = -2;
 +
 +	cmsg_size = CMSG_SPACE(sizeof(struct bintime));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
 +		goto done;
 +	}
  
  	if (sock_type == SOCK_STREAM) {
 -		if ((fd2 = accept_timeout(fd1)) < 0)
 -			return (-2);
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
  	} else
  		fd2 = fd1;
  
 -	iov[0].iov_base = buf;
 -	iov[0].iov_len = sizeof buf;
 -
 -	msg.msg_name = NULL;
 -	msg.msg_namelen = 0;
 -	msg.msg_iov = iov;
 -	msg.msg_iovlen = 1;
 -	msg.msg_control = control_un.control;
 -	msg.msg_controllen = sizeof control_un.control;
 -	msg.msg_flags = 0;
 -
 -	if (recvmsg_timeout(fd2, &msg, sizeof buf) < 0)
 -		goto failed;
 +	rv = -1;
 +	for (i = 1; i <= ipc_msg.msg_num; ++i) {
 +		dbgmsg("message #%u", i);
 +
 +		msghdr_init_server(&msghdr, iov, cmsg_data, cmsg_size);
 +		if (message_recv(fd2, &msghdr) < 0) {
 +			rv = -2;
 +			break;
 +		}
  
 -	error = -1;
 +		if (check_msghdr(&msghdr, sizeof(*cmsghdr)) < 0)
 +			break;
  
 -	if (msg.msg_flags & MSG_CTRUNC) {
 -		logmsgx("control data was truncated, MSG_CTRUNC flag is on");
 -		goto done;
 +		cmsghdr = CMSG_FIRSTHDR(&msghdr);
 +		if (check_scm_bintime(cmsghdr) < 0)
 +			break;
  	}
 +	if (i > ipc_msg.msg_num)
 +		rv = 0;
 +done:
 +	free(cmsg_data);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -	if (msg.msg_controllen < sizeof(struct cmsghdr)) {
 -		logmsgx("msg_controllen %u < %lu (sizeof(struct cmsghdr))",
 -		    (u_int)msg.msg_controllen, (u_long)sizeof(struct cmsghdr));
 -		goto done;
 -	}
 +static int
 +t_bintime(void)
 +{
 +	return (t_generic(t_bintime_client, t_bintime_server));
 +}
  
 -	if ((cmptr = CMSG_FIRSTHDR(&msg)) == NULL) {
 -		logmsgx("CMSG_FIRSTHDR is NULL");
 -		goto done;
 -	}
 +static int
 +t_cmsg_len_client(int fd)
 +{
 +	struct msghdr msghdr;
 +	struct iovec iov[1];
 +	struct cmsghdr *cmsghdr;
 +	void *cmsg_data;
 +	size_t size, cmsg_size;
 +	socklen_t socklen;
 +	int rv;
  
 -	dbgmsg(("msg_controllen = %u, cmsg_len = %u",
 -	    (u_int)msg.msg_controllen, (u_int)cmptr->cmsg_len));
 +	if (sync_recv() < 0)
 +		return (-2);
 +
 +	rv = -2;
  
 -	if (cmptr->cmsg_level != SOL_SOCKET) {
 -		logmsgx("cmsg_level %d != SOL_SOCKET", cmptr->cmsg_level);
 +	cmsg_size = CMSG_SPACE(sizeof(struct cmsgcred));
 +	cmsg_data = malloc(cmsg_size);
 +	if (cmsg_data == NULL) {
 +		logmsg("malloc");
  		goto done;
  	}
 +	msghdr_init_client(&msghdr, iov, cmsg_data, cmsg_size,
 +	    SCM_CREDS, sizeof(struct cmsgcred));
 +	cmsghdr = CMSG_FIRSTHDR(&msghdr);
  
 -	if (cmptr->cmsg_type != SCM_TIMESTAMP) {
 -		logmsgx("cmsg_type %d != SCM_TIMESTAMP", cmptr->cmsg_type);
 +	if (socket_connect(fd) < 0)
  		goto done;
 +
 +	size = msghdr.msg_iov != NULL ? msghdr.msg_iov->iov_len : 0;
 +	rv = -1;
 +	for (socklen = 0; socklen < CMSG_LEN(0); ++socklen) {
 +		cmsghdr->cmsg_len = socklen;
 +		dbgmsg("send: data size %zu", size);
 +		dbgmsg("send: msghdr.msg_controllen %u",
 +		    (u_int)msghdr.msg_controllen);
 +		dbgmsg("send: cmsghdr.cmsg_len %u",
 +		    (u_int)cmsghdr->cmsg_len);
 +		if (sendmsg(fd, &msghdr, 0) < 0)
 +			continue;
 +		logmsgx("sent message with cmsghdr.cmsg_len %u < %u",
 +		    (u_int)cmsghdr->cmsg_len, (u_int)CMSG_LEN(0));
 +		break;
  	}
 +	if (socklen == CMSG_LEN(0))
 +		rv = 0;
  
 -	if (cmptr->cmsg_len != CMSG_LEN(sizeof(struct timeval))) {
 -		logmsgx("cmsg_len %u != %lu (CMSG_LEN(sizeof(struct timeval))",
 -		    (u_int)cmptr->cmsg_len, (u_long)CMSG_LEN(sizeof(struct timeval)));
 +	if (sync_send() < 0) {
 +		rv = -2;
  		goto done;
  	}
 +done:
 +	free(cmsg_data);
 +	return (rv);
 +}
  
 -	timeval = (const struct timeval *)CMSG_DATA(cmptr);
 +static int
 +t_cmsg_len_server(int fd1)
 +{
 +	int fd2, rv;
  
 -	dbgmsg(("timeval tv_sec %jd, tv_usec %jd",
 -	    (intmax_t)timeval->tv_sec, (intmax_t)timeval->tv_usec));
 +	if (sync_send() < 0)
 +		return (-2);
  
 -	if ((cmptr = CMSG_NXTHDR(&msg, cmptr)) != NULL) {
 -		logmsgx("control data has extra header");
 -		goto done;
 -	}
 +	rv = -2;
  
 -	error = 0;
 +	if (sock_type == SOCK_STREAM) {
 +		fd2 = socket_accept(fd1);
 +		if (fd2 < 0)
 +			goto done;
 +	} else
 +		fd2 = fd1;
  
 +	if (sync_recv() < 0)
 +		goto done;
 +
 +	rv = 0;
  done:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0) {
 -			logmsg("close");
 -			return (-2);
 -		}
 -	return (error);
 +	if (sock_type == SOCK_STREAM && fd2 >= 0)
 +		if (socket_close(fd2) < 0)
 +			rv = -2;
 +	return (rv);
 +}
  
 -failed:
 -	if (sock_type == SOCK_STREAM)
 -		if (close(fd2) < 0)
 -			logmsg("close");
 -	return (-2);
 +static int
 +t_cmsg_len(void)
 +{
 +	return (t_generic(t_cmsg_len_client, t_cmsg_len_server));
  }
  
  static int
 -t_timestamp(void)
 +t_peercred_client(int fd)
  {
 -	int error, fd;
 +	struct xucred xucred;
 +	socklen_t len;
  
 -	if ((fd = create_server_socket()) < 0)
 -		return (-2);
 +	if (sync_recv() < 0)
 +		return (-1);
  
 -	if (sock_type == SOCK_STREAM)
 -		if (listen(fd, LISTENQ) < 0) {
 -			logmsg("listen");
 -			goto failed;
 -		}
 +	if (socket_connect(fd) < 0)
 +		return (-1);
  
 -	if ((client_pid = fork()) == (pid_t)-1) {
 -		logmsg("fork");
 -		goto failed;
 +	len = sizeof(xucred);
 +	if (getsockopt(fd, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		return (-1);
  	}
  
 -	if (client_pid == 0) {
 -		myname = "CLIENT";
 -		if (close_socket((const char *)NULL, fd) < 0)
 -			_exit(1);
 -		t_timestamp_client();
 -	}
 +	if (check_xucred(&xucred, len) < 0)
 +		return (-1);
  
 -	if ((error = t_timestamp_server(fd)) == -2) {
 -		(void)wait_client();
 -		goto failed;
 -	}
 +	return (0);
 +}
  
 -	if (wait_client() < 0)
 -		goto failed;
 +static int
 +t_peercred_server(int fd1)
 +{
 +	struct xucred xucred;
 +	socklen_t len;
 +	int fd2, rv;
  
 -	if (close_socket(serv_sock_path, fd) < 0) {
 -		logmsgx("close_socket failed");
 +	if (sync_send() < 0)
  		return (-2);
 +
 +	fd2 = socket_accept(fd1);
 +	if (fd2 < 0)
 +		return (-2);
 +
 +	len = sizeof(xucred);
 +	if (getsockopt(fd2, 0, LOCAL_PEERCRED, &xucred, &len) < 0) {
 +		logmsg("getsockopt(LOCAL_PEERCRED)");
 +		rv = -2;
 +		goto done;
  	}
 -	return (error);
  
 -failed:
 -	if (close_socket(serv_sock_path, fd) < 0)
 -		logmsgx("close_socket failed");
 -	return (-2);
 +	if (check_xucred(&xucred, len) < 0) {
 +		rv = -1;
 +		goto done;
 +	}
 +
 +	rv = 0;
 +done:
 +	if (socket_close(fd2) < 0)
 +		rv = -2;
 +	return (rv);
 +}
 +
 +static int
 +t_peercred(void)
 +{
 +	return (t_generic(t_peercred_client, t_peercred_server));
  }
 diff -ruNp unix_cmsg.orig/unix_cmsg.t unix_cmsg/unix_cmsg.t
 --- unix_cmsg.orig/unix_cmsg.t	2012-11-19 14:38:48.000000000 +0200
 +++ unix_cmsg/unix_cmsg.t	2013-02-08 12:08:52.000000000 +0200
 @@ -11,47 +11,78 @@ n=0
  
  run()
  {
 -	result=`${cmd} -t $2 $3 $4 2>&1`
 -	if [ $? -eq 0 ]; then
 -		echo -n "ok $1"
 -	else
 -		echo -n "not ok $1"
 +	result=`${cmd} -t $2 $3 ${5%% *} 2>&1`
 +	if [ $? -ne 0 ]; then
 +		echo -n "not "
  	fi
 -	echo " -" $5
 +	echo "ok $1 - $4 ${5#* }"
  	echo ${result} | grep -E "SERVER|CLIENT" | while read line; do
  		echo "# ${line}"
  	done
  }
  
 -echo "1..15"
 +echo "1..47"
  
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred (listening socket has LOCAL_CREDS) # TODO" \
 -	"Receiving sockcred (accepted socket has LOCAL_CREDS) # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"4 Sending cmsgcred, receiving sockcred" \
 +	"5 Sending, receiving timeval" \
 +	"6 Sending, receiving bintime" \
 +	"7 Check cmsghdr.cmsg_len"
  do
 -	n=`expr ${n} + 1`
 -	run ${n} stream "" ${n} "STREAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
 +	done
 +done
 +
 +for t1 in \
 +	"2 Receiving sockcred (listening socket)" \
 +	"3 Receiving sockcred (accepted socket)"
 +do
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)"
 +	do
 +		n=$((n + 1))
 +		run ${n} stream "-z ${t2%% *}" STREAM "${t1} ${t2#* }"
 +	done
  done
  
 -i=0
 -for desc in \
 -	"Sending, receiving cmsgcred" \
 -	"Receiving sockcred # TODO" \
 -	"Sending cmsgcred, receiving sockcred # TODO" \
 -	"Sending, receiving timestamp"
 +n=$((n + 1))
 +run ${n} stream "-z 0" STREAM "8 Check LOCAL_PEERCRED socket option"
 +
 +for t1 in \
 +	"1 Sending, receiving cmsgcred" \
 +	"3 Sending cmsgcred, receiving sockcred" \
 +	"4 Sending, receiving timeval" \
 +	"5 Sending, receiving bintime" \
 +	"6 Check cmsghdr.cmsg_len"
  do
 -	i=`expr ${i} + 1`
 -	n=`expr ${n} + 1`
 -	run ${n} dgram "" ${i} "DGRAM ${desc}"
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)" \
 +		"2 (no array)" \
 +		"3 (no data, array)"
 +	do
 +		n=$((n + 1))
 +		run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
 +	done
  done
  
 -run 10 stream -z 1 "STREAM Sending, receiving cmsgcred (no control data)"
 -run 11 stream -z 4 "STREAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 12 stream -z 5 "STREAM Sending, receiving timestamp (no control data)"
 -
 -run 13 dgram -z 1 "DGRAM Sending, receiving cmsgcred (no control data)"
 -run 14 dgram -z 3 "DGRAM Sending cmsgcred, receiving sockcred (no control data) # TODO"
 -run 15 dgram -z 4 "DGRAM Sending, receiving timestamp (no control data)"
 +for t1 in \
 +	"2 Receiving sockcred"
 +do
 +	for t2 in \
 +		"0 " \
 +		"1 (no data)"
 +	do
 +		n=$((n + 1))
 +		run ${n} dgram "-z ${t2%% *}" DGRAM "${t1} ${t2#* }"
 +	done
 +done

From: Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/131567: Update for regression/sockets/unix_cmsg
Date: Mon, 11 Feb 2013 11:38:02 +0200

 Correctness of unix_cmsg verified on 7.1-STABLE, 9.1-STABLE and 10-CURRENT.
 
                                      ^^^\
 				         \ Corrected typo in previous message.
Responsible-Changed-From-To: freebsd-net->pluknet 
Responsible-Changed-By: pluknet 
Responsible-Changed-When: Mon Feb 11 12:27:51 UTC 2013 
Responsible-Changed-Why:  
Take. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=131567 
State-Changed-From-To: open->patched 
State-Changed-By: pluknet 
State-Changed-When: Mon Feb 11 12:56:51 UTC 2013 
State-Changed-Why:  
Committed to HEAD, thanks! MFC period set to 2 weeks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=131567 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/131567: commit references a PR
Date: Mon, 11 Feb 2013 12:56:39 +0000 (UTC)

 Author: pluknet
 Date: Mon Feb 11 12:56:23 2013
 New Revision: 246670
 URL: http://svnweb.freebsd.org/changeset/base/246670
 
 Log:
   Major update for unix_cmsg from Andrey Simonenko.
   
   Quoting the submitter:
   - Added tests for SCM_BINTIME, LOCAL_PEERCRED, cmsghdr.cmsg_len
   - Code that checks correctness of groups was corrected (getgroups(2) change)
   - unix_cmsg.c was completely redesigned and simplified
   - Use less timeout value in unix_cmsg.c for faster work
   - Added support for not sending data in a message, not sending data and
     data array associated with a cmsghdr structure in a message
   - Existent tests were improved
   - unix_cmsg.t was redesigned and simplified
   
   Correctness of unix_cmsg verified on 7.1-STABLE, 9.1-STABLE and 10-CURRENT.
   
   PR:		bin/131567
   Submitted by:	Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
   MFC after:	2 weeks
 
 Modified:
   head/tools/regression/sockets/unix_cmsg/README
   head/tools/regression/sockets/unix_cmsg/unix_cmsg.c
   head/tools/regression/sockets/unix_cmsg/unix_cmsg.t
 
 Modified: head/tools/regression/sockets/unix_cmsg/README
 ==============================================================================
 --- head/tools/regression/sockets/unix_cmsg/README	Mon Feb 11 12:55:24 2013	(r246669)
 +++ head/tools/regression/sockets/unix_cmsg/README	Mon Feb 11 12:56:23 2013	(r246670)
 @@ -1,127 +1,160 @@
  $FreeBSD$
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 -
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 -Description of tests
 -====================
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
 +
 +Description
 +===========
 +
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
  
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.
 +    All these attempts should fail, since cmsg_len in all requests is less
 +    than CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 
 Modified: head/tools/regression/sockets/unix_cmsg/unix_cmsg.c
 ==============================================================================
 --- head/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Feb 11 12:55:24 2013	(r246669)
 +++ head/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Feb 11 12:56:23 2013	(r246670)
 @@ -27,48 +27,46 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of
 + * a client process.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD$");
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	2
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_send;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_send[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", work_dir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: number is %s",
 +				    errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: number is %s",
 +				    errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: number is %s",
 +				    errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "test number is %s", errstr);
 +		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
 +		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
 
 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: pluknet 
State-Changed-When: Mon Apr 29 21:33:59 UTC 2013 
State-Changed-Why:  
Merged to supported branches. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=131567 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/131567: commit references a PR
Date: Mon, 29 Apr 2013 21:30:21 +0000 (UTC)

 Author: pluknet
 Date: Mon Apr 29 21:30:04 2013
 New Revision: 250076
 URL: http://svnweb.freebsd.org/changeset/base/250076
 
 Log:
   MFC r243314:
    Zero the whole struct not just the size of a pointer.
   
   MFC r246670:
    Major update for unix_cmsg.
   
   PR:		bin/131567
   Submitted by:	Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
 
 Modified:
   stable/9/tools/regression/sockets/unix_cmsg/README
   stable/9/tools/regression/sockets/unix_cmsg/unix_cmsg.c
   stable/9/tools/regression/sockets/unix_cmsg/unix_cmsg.t
 Directory Properties:
   stable/9/tools/regression/sockets/   (props changed)
 
 Modified: stable/9/tools/regression/sockets/unix_cmsg/README
 ==============================================================================
 --- stable/9/tools/regression/sockets/unix_cmsg/README	Mon Apr 29 21:12:25 2013	(r250075)
 +++ stable/9/tools/regression/sockets/unix_cmsg/README	Mon Apr 29 21:30:04 2013	(r250076)
 @@ -1,127 +1,160 @@
  $FreeBSD$
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 -
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 -Description of tests
 -====================
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
 +
 +Description
 +===========
 +
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
  
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.
 +    All these attempts should fail, since cmsg_len in all requests is less
 +    than CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 
 Modified: stable/9/tools/regression/sockets/unix_cmsg/unix_cmsg.c
 ==============================================================================
 --- stable/9/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Apr 29 21:12:25 2013	(r250075)
 +++ stable/9/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Apr 29 21:30:04 2013	(r250076)
 @@ -27,48 +27,46 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of
 + * a client process.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD$");
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	2
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_send;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_send[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", work_dir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: number is %s",
 +				    errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: number is %s",
 +				    errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: number is %s",
 +				    errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "test number is %s", errstr);
 +		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
 +		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
 
 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/131567: commit references a PR
Date: Mon, 29 Apr 2013 21:33:53 +0000 (UTC)

 Author: pluknet
 Date: Mon Apr 29 21:33:36 2013
 New Revision: 250077
 URL: http://svnweb.freebsd.org/changeset/base/250077
 
 Log:
   MFC r243314:
    Zero the whole struct not just the size of a pointer.
   
   MFC r246670:
    Major update for unix_cmsg.
   
   PR:		bin/131567
   Submitted by:	Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>
 
 Modified:
   stable/8/tools/regression/sockets/unix_cmsg/README
   stable/8/tools/regression/sockets/unix_cmsg/unix_cmsg.c
   stable/8/tools/regression/sockets/unix_cmsg/unix_cmsg.t
 Directory Properties:
   stable/8/tools/regression/sockets/   (props changed)
 
 Modified: stable/8/tools/regression/sockets/unix_cmsg/README
 ==============================================================================
 --- stable/8/tools/regression/sockets/unix_cmsg/README	Mon Apr 29 21:30:04 2013	(r250076)
 +++ stable/8/tools/regression/sockets/unix_cmsg/README	Mon Apr 29 21:33:36 2013	(r250077)
 @@ -1,127 +1,160 @@
  $FreeBSD$
  
  About unix_cmsg
 -================
 +===============
  
 -This program is a collection of regression tests for ancillary (control)
 -data for PF_LOCAL sockets (local domain or Unix domain sockets).  There
 -are tests for stream and datagram sockets.
 -
 -Usually each test does following steps: create Server, fork Client,
 -Client sends something to Server, Server verifies if everything
 -is correct in received message.  Sometimes Client sends several
 -messages to Server.
 +This program is a collection of regression tests for ancillary data
 +(control information) for PF_LOCAL sockets (local domain or Unix domain
 +sockets).  There are tests for stream and datagram sockets.
 +
 +Usually each test does following steps: creates Server, forks Client,
 +Client sends something to Server, Server verifies whether everything is
 +correct in received message(s).
  
  It is better to change the owner of unix_cmsg to some safe user
 -(eg. nobody:nogroup) and set SUID and SGID bits, else some tests
 -can give correct results for wrong implementation.
 +(eg. nobody:nogroup) and set SUID and SGID bits, else some tests that
 +check credentials can give correct results for wrong implementation.
 +
 +It is better to run this program by a user that belongs to more
 +than 16 groups.
  
  Available options
  =================
  
 --d	Output debugging information, values of different fields of
 -	received messages, etc.  Will produce many lines of information.
 -
 --h	Output help message and exit.
 -
 --t <socktype>
 -	Run tests only for the given socket type: "stream" or "dgram".
 -	With this option it is possible to run only particular test,
 -	not all of them.
 -
 --z	Do not send real control data if possible.  Struct cmsghdr{}
 -	should be followed by real control data.  It is not clear if
 -	a sender should give control data in all cases (this is not
 -	documented and an arbitrary application can choose anything).
 -
 -	At least for PF_LOCAL sockets' control messages with types
 -	SCM_CREDS and SCM_TIMESTAMP the kernel does not need any
 -	control data.  This option allow to not send real control data
 -	for SCM_CREDS and SCM_TIMESTAMP control messages.
 +usage: unix_cmsg [-dh] [-n num] [-s size] [-t type] [-z value] [testno]
  
 -Description of tests
 -====================
 + Options are:
 +  -d            Output debugging information
 +  -h            Output the help message and exit
 +  -n num        Number of messages to send
 +  -s size       Specify size of data for IPC
 +  -t type       Specify socket type (stream, dgram) for tests
 +  -z value      Do not send data in a message (bit 0x1), do not send
 +                data array associated with a cmsghdr structure (bit 0x2)
 +  testno        Run one test by its number (require the -t option)
 +
 +Description
 +===========
 +
 +If Client sends something to Server, then it sends 5 messages by default.
 +Number of messages can be changed in the -n command line option.  Number
 +of messages will be given as N in the following descriptions.
 +
 +If Client sends something to Server, then it sends some data (few bytes)
 +in each message by default.  The size of this data can be changed by the -s
 +command line option.  The "-s 0" command line option means, that Client will
 +send zero bytes represented by { NULL, 0 } value of struct iovec{}, referenced
 +by the msg_iov field from struct msghdr{}.  The "-z 1" or "-z 3" command line
 +option means, that Client will send zero bytes represented by the NULL value
 +in the msg_iov field from struct msghdr{}.
 +
 +If Client sends some ancillary data object, then this ancillary data object
 +always has associated data array by default.  The "-z 2" or "-z 3" option
 +means, that Client will not send associated data array if possible.
  
  For SOCK_STREAM sockets:
  -----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client connects to Server and sends two messages with data and
 -    control message with SCM_CREDS type to Server.  Server should
 -    receive two messages, in both messages there should be data and
 -    control message with SCM_CREDS type followed by struct cmsgcred{}
 -    and this structure should contain correct information.
 -
 - 2: Receiving sockcred (listening socket has LOCAL_CREDS)
 -
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends two messages with data
 -    to Server.  Server should receive two messages, in first message
 -    there should be data and control message with SCM_CREDS type followed
 -    by struct sockcred{} and this structure should contain correct
 -    information, in second message there should be data and no control
 -    message.
 -
 - 3: Receiving sockcred (accepted socket has LOCAL_CREDS)
 -
 -    Client connects to Server and sends two messages with data.  Server
 -    accepts connection and set socket option LOCAL_CREDS for just accepted
 -    socket (here synchronization is used, to allow Client to see just set
 -    flag on Server's socket before sending messages to Server).  Server
 -    should receive two messages, in first message there should be data and
 -    control message with SOCK_CRED type followed by struct sockcred{} and
 -    this structure should contain correct information, in second message
 -    there should be data and no control message.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
 +
 + 2: Receiving sockcred (listening socket)
 +
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket option for it.  Client connects to Server two times, each time
 +    it sends N messages.  Server accepts two connections and receives N
 +    messages from each connection.  The first message from each connection
 +    should have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    next messages from the same connection should not have ancillary data.
 +
 + 3: Receiving sockcred (accepted socket)
 +
 +    Client connects to Server.  Server accepts connection and sets the
 +    LOCAL_CREDS socket option for just accepted socket.  Client sends N
 +    messages to Server.  Server should receive N messages, the first
 +    message should have SCM_CREDS ancillary data object followed by
 +    struct sockcred{}, next messages should not have ancillary data.
  
   4: Sending cmsgcred, receiving sockcred
  
 -    Server creates listen socket and set socket option LOCAL_CREDS
 -    for it.  Client connects to Server and sends one message with data
 -    and control message with SCM_CREDS type to Server.  Server should
 -    receive one message with data and control message with SCM_CREDS type
 -    followed by struct sockcred{} and this structure should contain
 -    correct information.
 -
 - 5: Sending, receiving timestamp
 -
 -    Client connects to Server and sends message with data and control
 -    message with SCM_TIMESTAMP type to Server.  Server should receive
 -    message with data and control message with SCM_TIMESTAMP type
 -    followed by struct timeval{}.
 +    Server creates a listening stream socket and sets the LOCAL_CREDS
 +    socket  option for it.  Client connects to Server and sends N messages
 +    with SCM_CREDS ancillary data object.  Server should receive N messages,
 +    the first message should have SCM_CREDS ancillary data object followed
 +    by struct sockcred{}, each of next messages should have SCM_CREDS
 +    ancillary data object followed by struct cmsgcred{}.
 +
 + 5: Sending, receiving timeval
 +
 +    Client connects to Server and sends message with SCM_TIMESTAMP ancillary
 +    data object.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 6: Sending, receiving bintime
 +
 +    Client connects to Server and sends message with SCM_BINTIME ancillary
 +    data object.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 7: Checking cmsghdr.cmsg_len
 +
 +    Client connects to Server and tries to send several messages with
 +    SCM_CREDS ancillary data object that has wrong cmsg_len field in its
 +    struct cmsghdr{}.  All these attempts should fail, since cmsg_len
 +    in all requests is less than CMSG_LEN(0).
 +
 + 8: Check LOCAL_PEERCRED socket option
 +
 +    This test does not use ancillary data, but can be implemented here.
 +    Client connects to Server.  Both Client and Server verify that
 +    credentials of the peer are correct using LOCAL_PEERCRED socket option.
  
  For SOCK_DGRAM sockets:
  ----------------------
  
   1: Sending, receiving cmsgcred
  
 -    Client sends to Server two messages with data and control message
 -    with SCM_CREDS type to Server.  Server should receive two messages,
 -    in both messages there should be data and control message with
 -    SCM_CREDS type followed by struct cmsgcred{} and this structure
 -    should contain correct information.
 +    Client connects to Server and sends N messages with SCM_CREDS ancillary
 +    data object.  Server should receive N messages, each message should
 +    have SCM_CREDS ancillary data object followed by struct cmsgcred{}.
  
   2: Receiving sockcred
  
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends two messages with data to Server.  Server should
 -    receive two messages, in both messages there should be data and control
 -    message with SCM_CREDS type followed by struct sockcred{} and this
 -    structure should contain correct information.
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages to Server.  Server should receive N
 +    messages, each message should have SCM_CREDS ancillary data object
 +    followed by struct sockcred{}.
  
   3: Sending cmsgcred, receiving sockcred
 - 
 -    Server creates datagram socket and set socket option LOCAL_CREDS
 -    for it.  Client sends one message with data and control message with
 -    SOCK_CREDS type to Server.  Server should receive one message with
 -    data and control message with SCM_CREDS type followed by struct
 -    sockcred{} and this structure should contain correct information.
 -
 - 4: Sending, receiving timestamp
 -
 -    Client sends message with data and control message with SCM_TIMESTAMP
 -    type to Server.  Server should receive message with data and control
 -    message with SCM_TIMESTAMP type followed by struct timeval{}.
 +
 +    Server creates datagram socket and sets the LOCAL_CREDS socket option
 +    for it.  Client sends N messages with SCM_CREDS ancillary data object
 +    to Server.  Server should receive N messages, the first message should
 +    have SCM_CREDS ancillary data object followed by struct sockcred{},
 +    each of next messages should have SCM_CREDS ancillary data object
 +    followed by struct cmsgcred{}.
 +
 + 4: Sending, receiving timeval
 +
 +    Client sends one message with SCM_TIMESTAMP ancillary data object
 +    to Server.  Server should receive one message with SCM_TIMESTAMP
 +    ancillary data object followed by struct timeval{}.
 +
 + 5: Sending, receiving bintime
 +
 +    Client sends one message with SCM_BINTIME ancillary data object
 +    to Server.  Server should receive one message with SCM_BINTIME
 +    ancillary data object followed by struct bintime{}.
 +
 + 6: Checking cmsghdr.cmsg_len
 +
 +    Client tries to send Server several messages with SCM_CREDS ancillary
 +    data object that has wrong cmsg_len field in its struct cmsghdr{}.
 +    All these attempts should fail, since cmsg_len in all requests is less
 +    than CMSG_LEN(0).
  
  - Andrey Simonenko
 -simon@comsys.ntu-kpi.kiev.ua
 +andreysimonenko@users.sourceforge.net
 
 Modified: stable/8/tools/regression/sockets/unix_cmsg/unix_cmsg.c
 ==============================================================================
 --- stable/8/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Apr 29 21:30:04 2013	(r250076)
 +++ stable/8/tools/regression/sockets/unix_cmsg/unix_cmsg.c	Mon Apr 29 21:33:36 2013	(r250077)
 @@ -27,48 +27,46 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 -#include <sys/types.h>
 +#include <sys/param.h>
  #include <sys/resource.h>
  #include <sys/time.h>
 +#include <sys/select.h>
  #include <sys/socket.h>
 +#include <sys/ucred.h>
  #include <sys/un.h>
  #include <sys/wait.h>
  
 -#include <assert.h>
  #include <ctype.h>
  #include <err.h>
  #include <errno.h>
 +#include <fcntl.h>
  #include <inttypes.h>
  #include <limits.h>
 -#include <setjmp.h>
 +#include <paths.h>
  #include <signal.h>
  #include <stdarg.h>
 +#include <stdbool.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 -#include <sysexits.h>
  #include <unistd.h>
  
  /*
   * There are tables with tests descriptions and pointers to test
   * functions.  Each t_*() function returns 0 if its test passed,
 - * -1 if its test failed (something wrong was found in local domain
 - * control messages), -2 if some system error occurred.  If test
 - * function returns -2, then a program exits.
 + * -1 if its test failed, -2 if some system error occurred.
 + * If a test function returns -2, then a program exits.
   *
 - * Each test function completely control what to do (eg. fork or
 - * do not fork a client process).  If a test function forks a client
 - * process, then it waits for its termination.  If a return code of a
 - * client process is not equal to zero, or if a client process was
 - * terminated by a signal, then test function returns -2.
 + * If a test function forks a client process, then it waits for its
 + * termination.  If a return code of a client process is not equal
 + * to zero, or if a client process was terminated by a signal, then
 + * a test function returns -1 or -2 depending on exit status of
 + * a client process.
   *
 - * Each test function and complete program are not optimized
 - * a lot to allow easy to modify tests.
 - *
 - * Each function which can block, is run under TIMEOUT, if timeout
 - * occurs, then test function returns -2 or a client process exits
 - * with nonzero return code.
 + * Each function which can block, is run under TIMEOUT.  If timeout
 + * occurs, then a test function returns -2 or a client process exits
 + * with a non-zero return code.
   */
  
  #ifndef LISTENQ
 @@ -76,207 +74,290 @@ __FBSDID("$FreeBSD$");
  #endif
  
  #ifndef TIMEOUT
 -# define TIMEOUT	60
 +# define TIMEOUT	2
  #endif
  
 -#define EXTRA_CMSG_SPACE 512	/* Memory for not expected control data. */
 -
 -static int	t_cmsgcred(void), t_sockcred_stream1(void);
 -static int	t_sockcred_stream2(void), t_cmsgcred_sockcred(void);
 -static int	t_sockcred_dgram(void), t_timestamp(void);
 +static int	t_cmsgcred(void);
 +static int	t_sockcred_1(void);
 +static int	t_sockcred_2(void);
 +static int	t_cmsgcred_sockcred(void);
 +static int	t_timeval(void);
 +static int	t_bintime(void);
 +static int	t_cmsg_len(void);
 +static int	t_peercred(void);
  
  struct test_func {
 -	int	(*func)(void);	/* Pointer to function.	*/
 -	const char *desc;	/* Test description.	*/
 -};
 -
 -static struct test_func test_stream_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_stream1,	" 2: Receiving sockcred (listening socket has LOCAL_CREDS)" },
 -	{ t_sockcred_stream2,	" 3: Receiving sockcred (accepted socket has LOCAL_CREDS)" },
 -	{ t_cmsgcred_sockcred,	" 4: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 5: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +	int		(*func)(void);
 +	const char	*desc;
  };
  
 -static struct test_func test_dgram_tbl[] = {
 -	{ NULL,			" 0: All tests" },
 -	{ t_cmsgcred,		" 1: Sending, receiving cmsgcred" },
 -	{ t_sockcred_dgram,	" 2: Receiving sockcred" },
 -	{ t_cmsgcred_sockcred,	" 3: Sending cmsgcred, receiving sockcred" },
 -	{ t_timestamp,		" 4: Sending, receiving timestamp" },
 -	{ NULL, NULL }
 +static const struct test_func test_stream_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_1,
 +	  .desc = "Receiving sockcred (listening socket)"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred (accepted socket)"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	},
 +	{
 +	  .func = t_peercred,
 +	  .desc = "Check LOCAL_PEERCRED socket option"
 +	}
  };
  
 -#define TEST_STREAM_NO_MAX	(sizeof(test_stream_tbl) / sizeof(struct test_func) - 2)
 -#define TEST_DGRAM_NO_MAX	(sizeof(test_dgram_tbl) / sizeof(struct test_func) - 2)
 -
 -static const char *myname = "SERVER";	/* "SERVER" or "CLIENT" */
 -
 -static int	debug = 0;		/* 1, if -d. */
 -static int	no_control_data = 0;	/* 1, if -z. */
 -
 -static u_int	nfailed = 0;		/* Number of failed tests. */
 +#define TEST_STREAM_TBL_SIZE \
 +	(sizeof(test_stream_tbl) / sizeof(test_stream_tbl[0]))
  
 -static int	sock_type;		/* SOCK_STREAM or SOCK_DGRAM */
 -static const char *sock_type_str;	/* "SOCK_STREAM" or "SOCK_DGRAN" */
 -
 -static char	tempdir[] = "/tmp/unix_cmsg.XXXXXXX";
 -static char	serv_sock_path[PATH_MAX];
 -
 -static char	ipc_message[] = "hello";
 -
 -#define IPC_MESSAGE_SIZE	(sizeof(ipc_message))
 -
 -static struct sockaddr_un servaddr;	/* Server address. */
 -
 -static sigjmp_buf env_alrm;
 +static const struct test_func test_dgram_tbl[] = {
 +	{
 +	  .func = NULL,
 +	  .desc = "All tests"
 +	},
 +	{
 +	  .func = t_cmsgcred,
 +	  .desc = "Sending, receiving cmsgcred"
 +	},
 +	{
 +	  .func = t_sockcred_2,
 +	  .desc = "Receiving sockcred"
 +	},
 +	{
 +	  .func = t_cmsgcred_sockcred,
 +	  .desc = "Sending cmsgcred, receiving sockcred"
 +	},
 +	{
 +	  .func = t_timeval,
 +	  .desc = "Sending, receiving timeval"
 +	},
 +	{
 +	  .func = t_bintime,
 +	  .desc = "Sending, receiving bintime"
 +	},
 +	{
 +	  .func = t_cmsg_len,
 +	  .desc = "Check cmsghdr.cmsg_len"
 +	}
 +};
  
 -static uid_t	my_uid;
 -static uid_t	my_euid;
 -static gid_t	my_gid;
 -static gid_t	my_egid;
 +#define TEST_DGRAM_TBL_SIZE \
 +	(sizeof(test_dgram_tbl) / sizeof(test_dgram_tbl[0]))
  
 -/*
 - * my_gids[0] is EGID, next items are supplementary GIDs,
 - * my_ngids determines valid items in my_gids array.
 - */
 -static gid_t	my_gids[NGROUPS_MAX];
 -static int	my_ngids;
 +static bool	debug = false;
 +static bool	server_flag = true;
 +static bool	send_data_flag = true;
 +static bool	send_array_flag = true;	
 +static bool	failed_flag = false;
 +
 +static int	sock_type;
 +static const char *sock_type_str;
 +
 +static const char *proc_name;
 +
 +static char	work_dir[] = _PATH_TMP "unix_cmsg.XXXXXXX";
 +static int	serv_sock_fd;
 +static struct sockaddr_un serv_addr_sun;
 +
 +static struct {
 +	char		*buf_send;
 +	char		*buf_recv;
 +	size_t		buf_size;
 +	u_int		msg_num;
 +}		ipc_msg;
 +
 +#define IPC_MSG_NUM_DEF		5
 +#define IPC_MSG_NUM_MAX		10
 +#define IPC_MSG_SIZE_DEF	7
 +#define IPC_MSG_SIZE_MAX	128
 +
 +static struct {
 +	uid_t		uid;
 +	uid_t		euid;
 +	gid_t		gid;
 +	gid_t		egid;
 +	gid_t		*gid_arr;
 +	int		gid_num;
 +}		proc_cred;
 +
 +static pid_t	client_pid;
 +
 +#define SYNC_SERVER	0
 +#define SYNC_CLIENT	1
 +#define SYNC_RECV	0
 +#define SYNC_SEND	1
  
 -static pid_t	client_pid;		/* PID of forked client. */
 +static int	sync_fd[2][2];
  
 -#define dbgmsg(x)	do {			\
 -	if (debug)				\
 -	       logmsgx x ;			\
 -} while (/* CONSTCOND */0)
 +#define LOGMSG_SIZE	128
  
  static void	logmsg(const char *, ...) __printflike(1, 2);
  static void	logmsgx(const char *, ...) __printflike(1, 2);
 +static void	dbgmsg(const char *, ...) __printflike(1, 2);
  static void	output(const char *, ...) __printflike(1, 2);
  
 -extern char	*__progname;		/* The name of program. */
 -
 -/*
 - * Output the help message (-h switch).
 - */
  static void
 -usage(int quick)
 +usage(bool verbose)
  {
 -	const struct test_func *test_func;
 +	u_int i;
  
 -	fprintf(stderr, "Usage: %s [-dhz] [-t <socktype>] [testno]\n",
 -	    __progname);
 -	if (quick)
 +	printf("usage: %s [-dh] [-n num] [-s size] [-t type] "
 +	    "[-z value] [testno]\n", getprogname());
 +	if (!verbose)
  		return;
 -	fprintf(stderr, "\n Options are:\n\
 -  -d\t\t\tOutput debugging information\n\
 -  -h\t\t\tOutput this help message and exit\n\
 -  -t <socktype>\t\tRun test only for the given socket type:\n\
 -\t\t\tstream or dgram\n\
 -  -z\t\t\tDo not send real control data if possible\n\n");
 -	fprintf(stderr, " Available tests for stream sockets:\n");
 -	for (test_func = test_stream_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 -	fprintf(stderr, "\n Available tests for datagram sockets:\n");
 -	for (test_func = test_dgram_tbl; test_func->desc != NULL; ++test_func)
 -		fprintf(stderr, "  %s\n", test_func->desc);
 +	printf("\n Options are:\n\
 +  -d            Output debugging information\n\
 +  -h            Output the help message and exit\n\
 +  -n num        Number of messages to send\n\
 +  -s size       Specify size of data for IPC\n\
 +  -t type       Specify socket type (stream, dgram) for tests\n\
 +  -z value      Do not send data in a message (bit 0x1), do not send\n\
 +                data array associated with a cmsghdr structure (bit 0x2)\n\
 +  testno        Run one test by its number (require the -t option)\n\n");
 +	printf(" Available tests for stream sockets:\n");
 +	for (i = 0; i < TEST_STREAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_stream_tbl[i].desc);
 +	printf("\n Available tests for datagram sockets:\n");
 +	for (i = 0; i < TEST_DGRAM_TBL_SIZE; ++i)
 +		printf("   %u: %s\n", i, test_dgram_tbl[i].desc);
  }
  
 -/*
 - * printf-like function for outputting to STDOUT_FILENO.
 - */
  static void
  output(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "output: vsnprintf failed");
 +		err(EXIT_FAILURE, "output: vsnprintf failed");
  	write(STDOUT_FILENO, buf, strlen(buf));
  	va_end(ap);
  }
  
 -/*
 - * printf-like function for logging, also outputs message for errno.
 - */
  static void
  logmsg(const char *format, ...)
  {
 -	char buf[128];
 +	char buf[LOGMSG_SIZE];
  	va_list ap;
  	int errno_save;
  
 -	errno_save = errno;		/* Save errno. */
 -
 +	errno_save = errno;
  	va_start(ap, format);
  	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsg: vsnprintf failed");
 +		err(EXIT_FAILURE, "logmsg: vsnprintf failed");
  	if (errno_save == 0)
 -		output("%s: %s\n", myname, buf);
 +		output("%s: %s\n", proc_name, buf);
  	else
 -		output("%s: %s: %s\n", myname, buf, strerror(errno_save));
 +		output("%s: %s: %s\n", proc_name, buf, strerror(errno_save));
  	va_end(ap);
 +	errno = errno_save;
 +}
 +
 +static void
 +vlogmsgx(const char *format, va_list ap)
 +{
 +	char buf[LOGMSG_SIZE];
 +
 +	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 +		err(EXIT_FAILURE, "logmsgx: vsnprintf failed");
 +	output("%s: %s\n", proc_name, buf);
  
 -	errno = errno_save;		/* Restore errno. */
  }
  
 -/*
 - * printf-like function for logging, do not output message for errno.
 - */
  static void
  logmsgx(const char *format, ...)
  {
 -	char buf[128];
  	va_list ap;
  
  	va_start(ap, format);
 -	if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
 -		err(EX_SOFTWARE, "logmsgx: vsnprintf failed");
 -	output("%s: %s\n", myname, buf);
 +	vlogmsgx(format, ap);
  	va_end(ap);
  }
  
 -/*
 - * Run tests from testno1 to testno2.
 - */
 +static void
 +dbgmsg(const char *format, ...)
 +{
 +	va_list ap;
 +
 +	if (debug) {
 +		va_start(ap, format);
 +		vlogmsgx(format, ap);
 +		va_end(ap);
 +	}
 +}
 +
  static int
 -run_tests(u_int testno1, u_int testno2)
 +run_tests(int type, u_int testno1)
  {
 -	const struct test_func *test_func;
 -	u_int i, nfailed1;
 +	const struct test_func *tf;
 +	u_int i, testno2, failed_num;
  
 -	output("Running tests for %s sockets:\n", sock_type_str);
 -	test_func = (sock_type == SOCK_STREAM ?
 -	    test_stream_tbl : test_dgram_tbl) + testno1;
 +	sock_type = type;
 +	if (type == SOCK_STREAM) {
 +		sock_type_str = "SOCK_STREAM";
 +		tf = test_stream_tbl;
 +		i = TEST_STREAM_TBL_SIZE - 1;
 +	} else {
 +		sock_type_str = "SOCK_DGRAM";
 +		tf = test_dgram_tbl;
 +		i = TEST_DGRAM_TBL_SIZE - 1;
 +	}
 +	if (testno1 == 0) {
 +		testno1 = 1;
 +		testno2 = i;
 +	} else
 +		testno2 = testno1;
  
 -	nfailed1 = 0;
 -	for (i = testno1; i <= testno2; ++test_func, ++i) {
 -		output(" %s\n", test_func->desc);
 -		switch (test_func->func()) {
 +	output("Running tests for %s sockets:\n", sock_type_str);
 +	failed_num = 0;
 +	for (i = testno1, tf += testno1; i <= testno2; ++tf, ++i) {
 +		output("  %u: %s\n", i, tf->desc);
 +		switch (tf->func()) {
  		case -1:
 -			++nfailed1;
 +			++failed_num;
  			break;
  		case -2:
 -			logmsgx("some system error occurred, exiting");
 +			logmsgx("some system error or timeout occurred");
  			return (-1);
  		}
  	}
  
 -	nfailed += nfailed1;
 +	if (failed_num != 0)
 +		failed_flag = true;
  
  	if (testno1 != testno2) {
 -		if (nfailed1 == 0)
 -			output("-- all tests were passed!\n");
 +		if (failed_num == 0)
 +			output("-- all tests passed!\n");
  		else
 -			output("-- %u test%s failed!\n", nfailed1,
 -			    nfailed1 == 1 ? "" : "s");
 +			output("-- %u test%s failed!\n",
 +			    failed_num, failed_num == 1 ? "" : "s");
  	} else {
 -		if (nfailed == 0)
 -			output("-- test was passed!\n");
 +		if (failed_num == 0)
 +			output("-- test passed!\n");
  		else
  			output("-- test failed!\n");
  	}
 @@ -284,183 +365,322 @@ run_tests(u_int testno1, u_int testno2)
  	return (0);
  }
  
 -/* ARGSUSED */
 -static void
 -sig_alrm(int signo __unused)
 +static int
 +init(void)
 +{
 +	struct sigaction sigact;
 +	size_t idx;
 +	int rv;
 +
 +	proc_name = "SERVER";
 +
 +	sigact.sa_handler = SIG_IGN;
 +	sigact.sa_flags = 0;
 +	sigemptyset(&sigact.sa_mask);
 +	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
 +		logmsg("init: sigaction");
 +		return (-1);
 +	}
 +
 +	if (ipc_msg.buf_size == 0)
 +		ipc_msg.buf_send = ipc_msg.buf_recv = NULL;
 +	else {
 +		ipc_msg.buf_send = malloc(ipc_msg.buf_size);
 +		ipc_msg.buf_recv = malloc(ipc_msg.buf_size);
 +		if (ipc_msg.buf_send == NULL || ipc_msg.buf_recv == NULL) {
 +			logmsg("init: malloc");
 +			return (-1);
 +		}
 +		for (idx = 0; idx < ipc_msg.buf_size; ++idx)
 +			ipc_msg.buf_send[idx] = (char)idx;
 +	}
 +
 +	proc_cred.uid = getuid();
 +	proc_cred.euid = geteuid();
 +	proc_cred.gid = getgid();
 +	proc_cred.egid = getegid();
 +	proc_cred.gid_num = getgroups(0, (gid_t *)NULL);
 +	if (proc_cred.gid_num < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +	proc_cred.gid_arr = malloc(proc_cred.gid_num *
 +	    sizeof(*proc_cred.gid_arr));
 +	if (proc_cred.gid_arr == NULL) {
 +		logmsg("init: malloc");
 +		return (-1);
 +	}
 +	if (getgroups(proc_cred.gid_num, proc_cred.gid_arr) < 0) {
 +		logmsg("init: getgroups");
 +		return (-1);
 +	}
 +
 +	memset(&serv_addr_sun, 0, sizeof(serv_addr_sun));
 +	rv = snprintf(serv_addr_sun.sun_path, sizeof(serv_addr_sun.sun_path),
 +	    "%s/%s", work_dir, proc_name);
 +	if (rv < 0) {
 +		logmsg("init: snprintf");
 +		return (-1);
 +	}
 +	if ((size_t)rv >= sizeof(serv_addr_sun.sun_path)) {
 +		logmsgx("init: not enough space for socket pathname");
 +		return (-1);
 +	}
 +	serv_addr_sun.sun_family = PF_LOCAL;
 +	serv_addr_sun.sun_len = SUN_LEN(&serv_addr_sun);
 +
 +	return (0);
 +}
 +
 +static int
 +client_fork(void)
  {
 -	siglongjmp(env_alrm, 1);
 +	int fd1, fd2;
 +
 +	if (pipe(sync_fd[SYNC_SERVER]) < 0 ||
 +	    pipe(sync_fd[SYNC_CLIENT]) < 0) {
 +		logmsg("client_fork: pipe");
 +		return (-1);
 +	}
 +	client_pid = fork();
 +	if (client_pid == (pid_t)-1) {
 +		logmsg("client_fork: fork");
 +		return (-1);
 +	}
 +	if (client_pid == 0) {
 +		proc_name = "CLIENT";
 +		server_flag = false;
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_RECV];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_SEND];
 +	} else {
 +		fd1 = sync_fd[SYNC_SERVER][SYNC_SEND];
 +		fd2 = sync_fd[SYNC_CLIENT][SYNC_RECV];
 +	}
 +	if (close(fd1) < 0 || close(fd2) < 0) {
 +		logmsg("client_fork: close");
 +		return (-1);
 +	}
 +	return (client_pid != 0);
  }
  
 -/*
 - * Initialize signals handlers.
 - */
  static void
 -sig_init(void)
 +client_exit(int rv)
 +{
 +	if (close(sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
 +		logmsg("client_exit: close");
 +		rv = -1;
 +	}
 +	rv = rv == 0 ? EXIT_SUCCESS : -rv;
 +	dbgmsg("exit: code %d", rv);
 +	_exit(rv);
 +}
 +
 +static int
 +client_wait(void)
  {
 -	struct sigaction sa;
 +	int status;
 +	pid_t pid;
  
 -	sa.sa_handler = SIG_IGN;
 -	sigemptyset(&sa.sa_mask);
 -	sa.sa_flags = 0;
 -	if (sigaction(SIGPIPE, &sa, (struct sigaction *)NULL) < 0) 
 -		err(EX_OSERR, "sigaction(SIGPIPE)");
 -
 -	sa.sa_handler = sig_alrm;
 -	if (sigaction(SIGALRM, &sa, (struct sigaction *)NULL) < 0)
 -		err(EX_OSERR, "sigaction(SIGALRM)");
 +	dbgmsg("waiting for client");
 +
 +	if (close(sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
 +	    close(sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
 +		logmsg("client_wait: close");
 +		return (-1);
 +	}
 +
 +	pid = waitpid(client_pid, &status, 0);
 +	if (pid == (pid_t)-1) {
 +		logmsg("client_wait: waitpid");
 +		return (-1);
 +	}
 +
 +	if (WIFEXITED(status)) {
 +		if (WEXITSTATUS(status) != EXIT_SUCCESS) {
 +			logmsgx("client exit status is %d",
 +			    WEXITSTATUS(status));
 +			return (-WEXITSTATUS(status));
 +		}
 +	} else {
 +		if (WIFSIGNALED(status))
 +			logmsgx("abnormal termination of client, signal %d%s",
 +			    WTERMSIG(status), WCOREDUMP(status) ?
 +			    " (core file generated)" : "");
 +		else
 +			logmsgx("termination of client, unknown status");
 +		return (-1);
 +	}
 +
 +	return (0);
  }
  
  int
  main(int argc, char *argv[])
  {
  	const char *errstr;
 -	int opt, dgramflag, streamflag;
 -	u_int testno1, testno2;
 -
 -	dgramflag = streamflag = 0;
 -	while ((opt = getopt(argc, argv, "dht:z")) != -1)
 +	u_int testno, zvalue;
 +	int opt, rv;
 +	bool dgram_flag, stream_flag;
 +
 +	ipc_msg.buf_size = IPC_MSG_SIZE_DEF;
 +	ipc_msg.msg_num = IPC_MSG_NUM_DEF;
 +	dgram_flag = stream_flag = false;
 +	while ((opt = getopt(argc, argv, "dhn:s:t:z:")) != -1)
  		switch (opt) {
  		case 'd':
 -			debug = 1;
 +			debug = true;
  			break;
  		case 'h':
 -			usage(0);
 -			return (EX_OK);
 +			usage(true);
 +			return (EXIT_SUCCESS);
 +		case 'n':
 +			ipc_msg.msg_num = strtonum(optarg, 1,
 +			    IPC_MSG_NUM_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -n: number is %s",
 +				    errstr);
 +			break;
 +		case 's':
 +			ipc_msg.buf_size = strtonum(optarg, 0,
 +			    IPC_MSG_SIZE_MAX, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -s: number is %s",
 +				    errstr);
 +			break;
  		case 't':
  			if (strcmp(optarg, "stream") == 0)
 -				streamflag = 1;
 +				stream_flag = true;
  			else if (strcmp(optarg, "dgram") == 0)
 -				dgramflag = 1;
 +				dgram_flag = true;
  			else
 -				errx(EX_USAGE, "wrong socket type in -t option");
 +				errx(EXIT_FAILURE, "option -t: "
 +				    "wrong socket type");
  			break;
  		case 'z':
 -			no_control_data = 1;
 +			zvalue = strtonum(optarg, 0, 3, &errstr);
 +			if (errstr != NULL)
 +				errx(EXIT_FAILURE, "option -z: number is %s",
 +				    errstr);
 +			if (zvalue & 0x1)
 +				send_data_flag = false;
 +			if (zvalue & 0x2)
 +				send_array_flag = false;
  			break;
 -		case '?':
  		default:
 -			usage(1);
 -			return (EX_USAGE);
 +			usage(false);
 +			return (EXIT_FAILURE);
  		}
  
  	if (optind < argc) {
  		if (optind + 1 != argc)
 -			errx(EX_USAGE, "too many arguments");
 -		testno1 = strtonum(argv[optind], 0, UINT_MAX, &errstr);
 +			errx(EXIT_FAILURE, "too many arguments");
 +		testno = strtonum(argv[optind], 0, UINT_MAX, &errstr);
  		if (errstr != NULL)
 -			errx(EX_USAGE, "wrong test number: %s", errstr);
 +			errx(EXIT_FAILURE, "test number is %s", errstr);
 +		if (stream_flag && testno >= TEST_STREAM_TBL_SIZE)
 +			errx(EXIT_FAILURE, "given test %u for stream "
 +			    "sockets does not exist", testno);
 +		if (dgram_flag && testno >= TEST_DGRAM_TBL_SIZE)
 
 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
>Unformatted:
