/*
 * Copyright (c) 2003-2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: t-sr-msg.c,v 1.13 2006/10/05 04:27:35 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/test.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/fcntl.h"
#include "prterr.h"
#if MTA_USE_STATETHREADS
#include "sm/stsock.h"
#endif
#include "sm/unixsock.h"
#include "sm/cmsg.h"
#include "sm/socket.h"		/* struct msghdr */
#include "sm/uio.h"		/* struct iovec */

#include <stdio.h>

/*
**  Test send/recvmsg()
*/

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;

static int Verbose;

#define SM_BUFSIZE	8192

#define NSOCKET	"./rdwrfd"	/* socket to use to exchange fd */
#define TEST_MSG "Send some text over the wire"

#if MTA_USE_STATETHREADS
# define sm_sock_close(fd)	un_st_socket_close(fd)
# define SM_INVALID_SOCKET	NULL
# define SM_IS_INVALID_SOCKET(fd)	((fd) == NULL)
#else /* MTA_USE_STATETHREADS */
# define sm_sock_close(fd)	close(fd)
# define SM_INVALID_SOCKET	(-1)
# define SM_IS_INVALID_SOCKET(fd)	((fd) < 0)
#endif /* MTA_USE_STATETHREADS */

static void
usage(const char *prg)
{
	fprintf(stderr, "usage: %s options\n", prg);
	fprintf(stderr, "-c: act as client [default]\n");
	fprintf(stderr, "-s: act as server\n");
	fprintf(stderr, "-V: increase verbosity\n");
	exit(0);
}

/*
**  CLIENT -- send a message
**
**	Parameters:
**		none
**
**	Returns:
**		none
*/

static int
client(void)
{
	fd_T fd;
	int res;
#if MTA_USE_STATETHREADS
	sm_ret_T ret;
#endif
	struct msghdr	msg;
	struct iovec	iov[1];
	char buf[SM_BUFSIZE];

	if (Verbose > 1)
		fprintf(stderr, "clt: connect\n");
	sm_memzero(&msg, sizeof(msg));

	/* connect to server */
#if MTA_USE_STATETHREADS
	ret = un_st_client_connect(NSOCKET, -1, &fd);
	if (sm_is_err(ret))
		return ret;
#else /* MTA_USE_STATETHREADS */
	(void) unix_client_connect(NSOCKET, &fd);
	if (Verbose > 1)
		fprintf(stderr, "clt: connected=%d\n", fd);
	SM_TEST(fd >= 0);
	if (fd < 0)
		return -1;
#endif /* MTA_USE_STATETHREADS */

	sm_snprintf(buf, sizeof(buf), TEST_MSG);

	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	iov[0].iov_base = buf;
	iov[0].iov_len = strlen(buf) + 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	res = sm_sendmsg(fd, &msg, 0);
	SM_TEST(res >= 0);
	if (sm_is_err(res))
		fprintf(stderr, "clt: write_fd failed=%x, errno=%d\n",
			res, errno);

	sm_sock_close(fd);
	sleep(1);
	return 0;
}

/*
**  SERVER -- receive message
**
**	Parameters:
**		none
**
**	Returns:
**		none
*/

static int
server(void)
{
	fd_T fd, lfd;
	int servfd, r;
#if MTA_USE_STATETHREADS
	sm_ret_T ret;
	int st_addrlen;
#else
	sockaddr_len_T addrlen;
#endif
	ssize_t i;
	struct sockaddr addr;
	struct msghdr	msg;
	struct iovec	iov[1];
	char buf[SM_BUFSIZE];

	sm_memzero(&msg, sizeof(msg));
	sm_memzero(&buf, sizeof(buf));
	lfd = fd = SM_INVALID_SOCKET;
	servfd = -1;
	r = 0;
#if MTA_USE_STATETHREADS
	ret = un_st_server_listen(NSOCKET, 10, &lfd);
	SM_TEST(ret == SM_SUCCESS);
	if (sm_is_err(ret))
		return ret;
	SM_TEST(lfd != NULL);
	st_addrlen = sizeof(addr);
	if (Verbose > 1)
		fprintf(stderr, "srv: accept\n");
	fd = st_accept(lfd, &addr, &st_addrlen, -1);
	SM_TEST(!SM_IS_INVALID_SOCKET(fd));
	if (SM_IS_INVALID_SOCKET(fd))
	{
		r = -1;
		goto err;
	}
#else /* MTA_USE_STATETHREADS */
	lfd = unix_server_listen(NSOCKET, 10);
	SM_TEST(lfd >= 0);
	if (lfd < 0)
		return -1;
	addrlen = sizeof(addr);
	if (Verbose > 1)
		fprintf(stderr, "srv: accept\n");
	fd = unix_server_accept(lfd, &addr, &addrlen);
	SM_TEST(fd >= 0);
	if (fd < 0)
	{
		r = -1;
		goto err;
	}
#endif /* MTA_USE_STATETHREADS */

	iov[0].iov_base = buf;
	iov[0].iov_len = sizeof(buf);
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	errno = 0;
	i = sm_recvmsg(fd, &msg, 0);
	SM_TEST(i > 0);
	if (i <= 0)
		return -1;
	SM_TEST(strcmp(buf, TEST_MSG) == 0);

	fd = SM_INVALID_SOCKET;
	if (Verbose > 0)
		fprintf(stderr, "srv: sm_recvmsg done=%d, buf='%s', errno=%d\n",
			(int) i, buf, errno);

  err:
	if (!SM_IS_INVALID_SOCKET(fd))
		sm_sock_close(fd);
	if (!SM_IS_INVALID_SOCKET(lfd))
		sm_sock_close(lfd);
	return r;
}

int
main(int argc, char *argv[])
{
	bool clt, any;
	int c;

	opterr = 0;
	clt = true;
	any = false;
	Verbose = 0;
	while ((c = getopt(argc, argv, "csV")) != -1)
	{
		any = true;
		switch (c)
		{
		  case 'c':
			clt = true;
			break;
		  case 's':
			clt = false;
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			usage(argv[0]);
			return(1);
		}
	}
	sm_test_begin(argc, argv, "test write/read fd");
	if (!any)
		goto end;
#if MTA_USE_STATETHREADS
	c = st_init();
	SM_TEST(c >= 0);
	if (c < 0)
		return sm_test_end();
#endif /* MTA_USE_STATETHREADS */

	if (clt)
		c = client();
	else
		c = server();
	SM_TEST(c == 0);
  end:
	unlink(NSOCKET);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1