svn commit: r250076 - stable/9/tools/regression/sockets/unix_cmsg

Sergey Kandaurov pluknet at FreeBSD.org
Mon Apr 29 21:30:05 UTC 2013


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 at 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 at comsys.ntu-kpi.kiev.ua
+andreysimonenko at 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 ***


More information about the svn-src-stable-9 mailing list