/*
*	(Wb) bufferedSock.c provides buffered sockets
*		ATTN: has NEVER been tested with AF_INET sockets!!!!
*
*  Created by: Win Bausch and Bettina Kemme
*
*/


#include <signal.h>
#include <errno.h>

#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif

/* DAR HACK this is needed for SUN */
/* #include <sys/filio.h> */
#include <sys/ioctl.h>

#include <sys/stat.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/tcp.h>

#if defined(linux)
#ifndef SOMAXCONN
#define SOMAXCONN 5				/* from Linux listen(2) man page */
#endif	 /* SOMAXCONN */
#endif	 /* linux */

#include "postgres.h"
#include "miscadmin.h"
#include "libpq/pqsignal.h"

/*  swu
 * add #ifdef */ 
#ifdef REPLICATION
#include "replication/replication.h"

#ifdef REPL_UPDATE_EVERYWHERE
#include "storage/proc.h"
#endif
#endif

/*#define EBUF_DEBUG*/

/* --------------------------------------------------------------------- */
/* sockInit: initializes a buffered socket
 */
bufsockptr
sockInit(int bufsize)
{
	bufsockptr bsock = (bufsockptr) malloc(sizeof(buf_sock));
	 
	MemSet((char *) bsock, 0, sizeof(buf_sock));
	/*all variables in buf_sock are now zero'ed*/
	
	bsock->inBuffer = (char *) malloc(bufsize);
	bsock->outBuffer = (char *) malloc(bufsize);
	if (bsock->inBuffer == NULL || bsock->outBuffer == NULL)
	{
		sockDestroy(bsock);
		return NULL;
	}
	bsock->sock = -1;
	bsock->status = NOT_CONNECTED;
	bsock->inBufSize = bufsize;
	bsock->outBufSize = bufsize;
	return bsock;
}

/* --------------------------------------------------------------------- */
/* sockDestroy: destroys a buffered socket
 */
void
sockDestroy(bufsockptr bsock)
{
	if(!bsock)
		return;
	if (bsock->sock >= 0)
#ifdef WIN32
	closesocket(bsock->sock);
#else
	close(bsock->sock);
#endif
	if(bsock->inBuffer)
		free(bsock->inBuffer);
	if(bsock->outBuffer)
		free(bsock->outBuffer);
	free(bsock);
}
/* --------------------------------------------------------------------- */
/* sockGetc:
   get a character from the connection

   All these routines return 0 on success, EOF on error.
   Note that for the Get routines, EOF only means there is not enough
   data in the buffer, not that there is necessarily a hard error.
*/
int
sockGetc(char *result, bufsockptr bsock)
{
	if (bsock->inCursor >= bsock->inEnd)
		return EOF;

	*result = bsock->inBuffer[bsock->inCursor++];

	return 0;
}


/* --------------------------------------------------------------------- */
/* sockPutBytes: local routine to write N bytes to the connection,
   with buffering
 */
static int
sockPutBytes(const char *s, int nbytes, bufsockptr bsock)
{
	int			avail = 0;

	avail = bsock->outBufSize - bsock->outCount;
	while (nbytes > avail)
	{
		memcpy(bsock->outBuffer + bsock->outCount, s, avail);
		bsock->outCount += avail;
		s += avail;
		nbytes -= avail;
		if (sockFlush(bsock))
			return EOF;
		avail = bsock->outBufSize;
	}
	memcpy(bsock->outBuffer + bsock->outCount, s, nbytes);
	bsock->outCount += nbytes;
#ifdef EBUF_DEBUG
	elog(DEBUG, "sockPutBytes: buffer address is %x, count=%d, nbytes=%d were added", 
			bsock->outBuffer, bsock->outCount, nbytes);
#endif

	return 0;
}

/* --------------------------------------------------------------------- */
/* sockGets:
   get a null-terminated string from the connection,
   and store it in a buffer of size maxlen bytes.
   If the incoming string is >= maxlen bytes, all of it is read,
   but the excess characters are silently discarded.
*/
int
sockGets(char *s, int maxlen, bufsockptr bsock)
{
	/* Copy conn data to locals for faster search loop */
	char	   *inBuffer = bsock->inBuffer;
	int			inCursor = bsock->inCursor;
	int			inEnd = bsock->inEnd;
	int			slen;

	while (inCursor < inEnd && inBuffer[inCursor])
		inCursor++;

	if (inCursor >= inEnd)
	{	
		return EOF;
	}
	
	slen = inCursor - bsock->inCursor;
	if (slen < maxlen)
		strcpy(s, inBuffer + bsock->inCursor);
	else
	{
		strncpy(s, inBuffer + bsock->inCursor, maxlen - 1);
		s[maxlen - 1] = '\0';
	}
	
	bsock->inCursor = ++inCursor;

	return 0;
}

/* --------------------------------------------------------------------- */
/* sockPuts:
	put a null-terminated string into the buffer

*/
int
sockPuts(const char *s, bufsockptr bsock)
{
	if (sockPutBytes(s, strlen(s) + 1, bsock))
		return EOF;

	return 0;
}

/* --------------------------------------------------------------------- */
/* sockGetnchar:
   get a string of exactly len bytes in buffer s, no null termination
*/
int
sockGetnchar(char *s, int len, bufsockptr bsock)
{
	if (len < 0 || len > bsock->inEnd - bsock->inCursor)
		return EOF;
		
	//elog(NOTICE, "sockGetnchar: %d bytes unprocessed",sockNumLeft(bsock));
	memcpy(s, bsock->inBuffer + bsock->inCursor, len);
	/* no terminating null */

	bsock->inCursor += len;
	//elog(NOTICE, "sockGetnchar: %d bytes unprocessed",sockNumLeft(bsock));
	
	return 0;
}

/* --------------------------------------------------------------------- */
/* sockPutnchar:
   send a string of exactly len bytes, no null termination needed
*/
int
sockPutnchar(const char *s, int len, bufsockptr bsock)
{
	if (sockPutBytes(s, len, bsock))
		return EOF;
		
	return 0;
}

/* --------------------------------------------------------------------- */
/* sockGetInt
   read a 2 or 4 byte integer and convert from network byte order
   to local byte order
*/
int
sockGetInt(int *result, int bytes, bufsockptr bsock)
{
	uint16		tmp2;
	uint32		tmp4;

	//elog(NOTICE, "sockGetInt: %d bytes unprocessed",sockNumLeft(bsock));
	switch (bytes)
	{
		case 2:
			if (bsock->inCursor + 2 > bsock->inEnd)
				return EOF;
			memcpy(&tmp2, bsock->inBuffer + bsock->inCursor, 2);
			bsock->inCursor += 2;
			*result = (int) ntohs(tmp2);
			break;
		case 4:
			if (bsock->inCursor + 4 > bsock->inEnd)
				return EOF;
			memcpy(&tmp4, bsock->inBuffer + bsock->inCursor, 4);
			bsock->inCursor += 4;
			*result = (int) ntohl(tmp4);
			break;
		default:
			return EOF;
	}
	//elog(NOTICE, "sockGetInt: %d bytes unprocessed",sockNumLeft(bsock));

	return 0;
}

/* --------------------------------------------------------------------- */
/* sockPutInt
   send an integer of 2 or 4 bytes, converting from host byte order
   to network byte order.
*/
int
sockPutInt(int value, int bytes, bufsockptr bsock)
{
	uint16		tmp2;
	uint32		tmp4;

	switch (bytes)
	{
		case 2:
			tmp2 = htons((uint16) value);
			if (sockPutBytes((const char *) &tmp2, 2, bsock))
				return EOF;
			break;
		case 4:
			tmp4 = htonl((uint32) value);
			if (sockPutBytes((const char *) &tmp4, INT_SIZE, bsock))
				return EOF;
			break;
		default:
			snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
					"sockPutInt: int size %d not supported\n", bytes);
			return EOF;
	}

	return STATUS_OK;
}

/* --------------------------------------------------------------------- */
/* sockCheck: is select() saying the file is ready to read/write or is
*  there an exception pending?
 */
int
sockCheck(bufsockptr bsock, bool *forRead, bool *forWrite, bool *forExcept)
{
	fd_set		read_mask,
				write_mask,
				error_mask;
	struct timeval timeout;

	if (bsock->sock < 0)
		return (-2);

	FD_ZERO(&read_mask);
	FD_ZERO(&write_mask);
	FD_ZERO(&error_mask);
	if(*forRead)
		FD_SET(bsock->sock, &read_mask);
	if(*forWrite)
		FD_SET(bsock->sock, &write_mask);
	if(*forExcept)
		FD_SET(bsock->sock, &error_mask);
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	if (select(bsock->sock + 1, &read_mask, &write_mask, &error_mask,
			   &timeout) < 0)
	{
		return STATUS_ERROR;
	}
	*forRead = FALSE;
	*forWrite = FALSE;
	*forExcept = FALSE;
	if(FD_ISSET(bsock->sock, &read_mask))
		*forRead = TRUE;
	if(FD_ISSET(bsock->sock, &write_mask))
		*forWrite = TRUE;
	if(FD_ISSET(bsock->sock, &error_mask))
		*forExcept = TRUE;
	return STATUS_OK;
}

/* --------------------------------------------------------------------- */
/* sockReadData: read more data, if any is available
 * Possible return values:
 *	 1: successfully loaded at least one more byte
 *	 0: no data is presently available, but no error detected
 *	-1: error detected (including EOF = connection closure);
 *	
 * NOTE: callers must not assume that pointers or indexes into bsock->inBuffer
 * remain valid across this call!
 *
 *	useLimit - if TRUE, then maxbytes are read at most, not more
 *			   if FALSE all avalable bytes are read
 */
int
sockReadData(bufsockptr bsock, int maxbytes, bool useLimit)
{
	int			nread = 0,
				npass = 0,
				status;
	bool		forRead = TRUE;

	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: socket=%d, maxbytes=%d, useLimit=%s",
    		bsock->sock, maxbytes, useLimit ? "yes" : "no");
	#endif*/
	
	if(useLimit && (maxbytes == 0))
		return 0;
		
	if (bsock->sock < 0)
	{
		strcpy(bsock->errorMessage, "sockReadData() -- connection not open\n");
		return -1;
	}

	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: (at start) inStart=%d, inCursor=%d, inEnd=%d",
    		bsock->inStart, bsock->inCursor, bsock->inEnd);
	#endif*/

	/* Left-justify any data in the buffer to make room */
	if ((bsock->inStart != 0) && (bsock->inStart < bsock->inEnd))
	{
		memmove(bsock->inBuffer, bsock->inBuffer + bsock->inStart,
				bsock->inEnd - bsock->inStart);
		bsock->inEnd -= bsock->inStart;
		bsock->inCursor -= bsock->inStart;
		bsock->inStart = 0;
	}
	
	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: (after lefting) inStart=%d, inCursor=%d, inEnd=%d",
    		bsock->inStart, bsock->inCursor, bsock->inEnd);
	#endif*/

	if(bsock->inStart >= bsock->inEnd)
		bsock->inStart = bsock->inCursor = bsock->inEnd = 0;

	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: (after checking) inStart=%d, inCursor=%d, inEnd=%d",
    		bsock->inStart, bsock->inCursor, bsock->inEnd);
	#endif*/

	/*
	 * If the buffer is fairly full, enlarge it. We need to be able to
	 * enlarge the buffer in case a single message exceeds the initial
	 * buffer size.  We enlarge before filling the buffer entirely so as
	 * to avoid asking the kernel for a partial packet. New buffer is
	 *	allocated if there is less than a quarter of the initial space left
	 */
tryAgain:

	 //printf("sockReadData:  checking buffer size\n");
	 //fflush(stdout);
	
	if (bsock->inBufSize - bsock->inEnd < (bsock->inBufSize/REALLOC_FRACTION) ||
		(useLimit && (maxbytes > bsock->inBufSize - bsock->inEnd)))
	{
		int			newSize = bsock->inBufSize;
		char	   *newBuf;
		
		
		//printf("sockReadData: try allocating new buffer, size is %d\n", bsock->inBufSize);
		//fflush(stdout);

		/*#ifdef RMGR_DEBUG
    		elog(DEBUG, "sockReadData: allocating more space in the buffer");
		#endif*/

		do
		{
			newSize	*= 2;
		}
		while(useLimit && (maxbytes > newSize - bsock->inEnd));

		newBuf = (char *) realloc(bsock->inBuffer, newSize);

		if (newBuf)
		{
			//printf("sockReadData: allocated new buffer, size is %d\n", newSize);
			//fflush(stdout);
			bsock->inBuffer = newBuf;
			bsock->inBufSize = newSize;
		}
	}

	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: (after more size) inStart=%d, inCursor=%d, inEnd=%d",
    		bsock->inStart, bsock->inCursor, bsock->inEnd);
	#endif*/

	if(!useLimit)
		maxbytes = bsock->inBufSize - bsock->inEnd;
	/* OK, try to read some data */
	nread = recv(bsock->sock, bsock->inBuffer + bsock->inEnd,
				 maxbytes, 0);

	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: (after recv) received=%d",nread);
	#endif*/

	npass++;
	
	if (nread < 0)
	{
		if (errno == EINTR)
		{
			goto tryAgain;
		}
		
		/* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
		if (errno == EAGAIN)
		{
			return 0;
		}
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
		if (errno == EWOULDBLOCK)
		{	
			return 0;
		}
#endif
		/* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
		if (errno == ECONNRESET)
		{
			elog(NOTICE, "Backend died: %m");
			goto definitelyFailed;
		}
#endif
		snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
				"sockReadData() --  read() failed: errno=%d\n%s\n",
				errno, strerror(errno));

		/*  swu (rem) */
		/*#ifdef RMGR_DEBUG
    		elog(DEBUG, "sockReadData: before return -1");
		#endif*/
	
		return -1;
	}
	if (nread > 0)
	{
		bsock->inEnd += nread;
		/*elog(DEBUG, "sockReadData: %d bytes unprocessed", sockNumLeft(bsock));*/
		
		/*#ifdef RMGR_DEBUG
    		elog(DEBUG, "sockReadData: (before return: OK) inStart=%d, inCursor=%d, inEnd=%d",
    		bsock->inStart, bsock->inCursor, bsock->inEnd);
		#endif*/
		
		return nread;
	}

	/*
	*	If we read nothing, the socket might have been closed
	*	or there might be nothing to read. Check this...
	*/
checkAgain:
	/*  swu (rem) */
	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: checkAgain");
	#endif*/

	status = sockCheck(bsock, &forRead, FALSE, FALSE);

	/*  swu (rem) */
	/*#ifdef RMGR_DEBUG
    	elog(DEBUG, "sockReadData: after sockCheck status %d", status);
	#endif*/

	if (status == STATUS_ERROR)
	{
		if(errno == EINTR)
			goto checkAgain;
		elog(DEBUG, "sockReadData: status is %d and errno is %d", status, errno);
		goto definitelyFailed;
	}
	else if(status == STATUS_OK)
	{

		/*  swu (rem) */
		/*#ifdef RMGR_DEBUG
    		elog(DEBUG, "sockReadData: after sockCheck, STATUS_OK");
		#endif*/
		if(!forRead)
			return 0;
//		else if(npass > 1)
//			return (-1);
		goto tryAgain;
	}

definitelyFailed:
	snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
			"sockReadData() -- SRD: backend closed the channel unexpectedly.\n"
			"\tThis probably means the backend terminated abnormally"
			" before or while processing the request.\n");
	bsock->status = CONNECTION_ERROR;		/* No more connection to backend */
#ifdef WIN32
	closesocket(bsock->sock);
#else
	close(bsock->sock);
#endif
	bsock->sock = -1;

	return -1;
}


/* --------------------------------------------------------------------- */
/* sockFlush: send any data waiting in the output buffer
*
*/
int
sockFlush(bufsockptr bsock)
{
	char	   *ptr = bsock->outBuffer;
	int			len = bsock->outCount;

	if (bsock->sock < 0)
	{
		strcpy(bsock->errorMessage, "sockFlush() -- connection not open\n");
		return EOF;
	}

	while (len > 0)
	{
		/* Prevent being SIGPIPEd if backend has closed the connection. */
#ifndef WIN32
		pqsigfunc	oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
#endif

		int			sent = send(bsock->sock, ptr, len, 0);
#ifndef WIN32
		pqsignal(SIGPIPE, oldsighandler);
#endif

		if (sent < 0)
		{
			/*
			 * Anything except EAGAIN or EWOULDBLOCK is trouble.
			 * If it's EPIPE or ECONNRESET, assume we've lost the
			 *  connection permanently.
			 */
			switch (errno)
			{
#ifdef EAGAIN
				case EAGAIN:
					break;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
				case EWOULDBLOCK:
					break;
#endif
				case EPIPE:
#ifdef ECONNRESET
				case ECONNRESET:
#endif
					snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
							"sockFlush() -- backend closed the channel unexpectedly.\n"
							"\tThis probably means the backend terminated abnormally"
							" before or while processing the request.\n");
					bsock->status = CONNECTION_ERROR; /* No more connection */
#ifdef WIN32
					closesocket(bsock->sock);
#else
					close(bsock->sock);
#endif
					bsock->sock = -1;
					return EOF;
				default:
					snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
							"sockFlush() --  couldn't send data: errno=%d\n%s\n",
							errno, strerror(errno));
					/* We don't assume it's a fatal error... */
					return EOF;
			}
		}
		else
		{
			ptr += sent;
			len -= sent;
		}
		if (len > 0)
		{
			/* We didn't send it all, wait till we can send more */
			if (sockWait(bsock, FALSE, TRUE))
				return EOF;
		}
	}

	bsock->outCount = 0;
	return 0;
}

/* --------------------------------------------------------------------- */
/* sockWait: wait until we can read or write the connection socket
 */
int
sockWait(bufsockptr bsock, bool forRead, bool forWrite)
{
	fd_set		input_mask;
	fd_set		output_mask;

	if (bsock->sock < 0)
	{
		strcpy(bsock->errorMessage, "sockWait() -- connection not open\n");
		return EOF;
	}

	/* loop in case select returns EINTR */
	for (;;)
	{
		FD_ZERO(&input_mask);
		FD_ZERO(&output_mask);

		if (forRead)
			FD_SET(bsock->sock, &input_mask);
		if (forWrite)
			FD_SET(bsock->sock, &output_mask);

		if (select(bsock->sock + 1, &input_mask, &output_mask, NULL, NULL) < 0)
		{
			if (errno == EINTR)
				continue;
			snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
					"sockWait() -- select() failed: errno=%d\n%s\n",
					errno, strerror(errno));
			return EOF;
		}
		/* On nonerror return, assume we're done */
		break;
	}

	return 0;
}

#if 0
#ifdef REPL_UPDATE_EVERYWHERE
/*  swu (rem) */
/* sockWaitWithCheckAbort: 
 *		wait until we can read or write the connection socket.
 *		and check abort signal from r. bknd periodically, if 
 *		wait_time is not NULL
 */
int
sockWaitWithCheckAbort(bufsockptr bsock, bool forRead, 
					bool forWrite, struct timeval *wait_time)
{
	fd_set		input_mask;
	fd_set		output_mask;
	int nread;

	/*  swu (rem) */
	elog(DEBUG, "sockWaitWithCheckAbort: just enter");				

	if (bsock->sock < 0)
	{
		strcpy(bsock->errorMessage, "sockWait() -- connection not open\n");
		return EOF;
	}

	/*  swu (rem) */
	elog(DEBUG, "sockWaitWithCheckAbort: bsock->sock >= 0");				

	/* loop in case select returns EINTR */
	for (;;){
		FD_ZERO(&input_mask);
		FD_ZERO(&output_mask);

		if(forRead)
			FD_SET(bsock->sock, &input_mask);
		if(forWrite)
			FD_SET(bsock->sock, &output_mask);

		nread = select(bsock->sock + 1, &input_mask, &output_mask, NULL, wait_time);

		/*  swu (rem) */
		elog(DEBUG, "sockWaitWithCheckAbort: # read %d", nread);

		if(nread < 0){
			if (errno == EINTR){
				continue;
			}
			
			/*  swu (rem) */
			elog(DEBUG, "sockWaitWithCheckAbort: before return EOF");				

			snprintf(bsock->errorMessage, MAX_ERRORMSG_LEN,
					"sockWait() -- select() failed: errno=%d\n%s\n",
					errno, strerror(errno));
			return EOF;
		}
		/* we have to check if this l. txn is aborted by a r. txn.
		 * when reaches here, the l. txn is waiting for MSG_RECEIVED
		 */
		else if(nread == 0){
			/*  swu (rem) */
			elog(DEBUG, "sockWaitWithCheckAbort: check abort signal");				

			if(wait_time != NULL && MyProc->abortFlag == MUST_ABORT){
				/* bump holdoff count to make ProcessInterrupts() a no-op */
				/* until we are done getting ready for it */
				InterruptHoldoffCount++;
				DisableNotifyInterrupt();
				/* Make sure HandleDeadLock won't run while shutting down... */
				LockWaitCancel();
				InterruptHoldoffCount--;

				MyProc->abortFlag = NO_ABORT;  /* reset abortFlag */
				InterruptPending = false;
				QueryCancelPending = false;

				elog(ERROR, "Query was cancelled.");
			}

			continue;
		} 

		/* On nonerror return, assume we're done */
		break;
	}

	return 0;
}
#endif
#endif


/* --------------------------------------------------------------------- */
/* sockServerPort: sets up server port
 */
int
sockServerPort(const char *hostName, short portNumber, int *fdP, 
			struct sockaddr *serverAddr)
{
	SockAddr	saddr;
	int			fd,
				family;
	size_t		len;
	int			one = 1;

	/*
	elog(DEBUG, "sockServerPort: hostName=%s, portNumber=%d",
		 hostName, portNumber);
	*/
	/* (mc) we have to force a AF_INET type here for recovery purposes */
	/*family = ((hostName != NULL) ? AF_INET : AF_UNIX);*/
	family = AF_INET;

	if ((fd = socket(family, SOCK_STREAM, 0)) < 0)
	{
		elog(DEBUG, "sockServerPort: socket failed");
		return STATUS_ERROR;
	}
	if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
					sizeof(one))) == -1)
	{
		elog(DEBUG, "sockServerPort: setsockopt failed: %m");
		return STATUS_ERROR;
	}
	MemSet((char *) &saddr, 0, sizeof(saddr));
	saddr.sa.sa_family = family;
	if (family == AF_UNIX)
	{
		// DAR HACK fix to match UNIXSOCK_PATH as in pqcomm
		UNIXSOCK_PATH(saddr.un, portNumber, UnixSocketDir);
		len = UNIXSOCK_LEN(saddr.un);

		memmove(serverAddr, &saddr, sizeof(saddr));

		/*
		 * Grab an interlock file associated with the socket file.
		 */
		if (!CreateSocketLockFile(saddr.un.sun_path, true))
			return STATUS_ERROR;

		/*
		 * Once we have the interlock, we can safely delete any
		 * pre-existing socket file to avoid failure at bind() time.
		 */
		unlink(saddr.un.sun_path);
	}
	else
	{
		if (hostName == NULL)
			saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
		else
		{
			struct hostent *hp;

			hp = gethostbyname(hostName);
			if ((hp == NULL) || (hp->h_addrtype != AF_INET))
			{
				elog(DEBUG, "sockServerPort: gethostbyname(%s) failed", hostName);
				return STATUS_ERROR;
			}
			memmove((char *) &(saddr.in.sin_addr), (char *) hp->h_addr, hp->h_length);
		}

		saddr.in.sin_port = htons(portNumber);
		len = sizeof(struct sockaddr_in);
		
		
		/* old stuff (mc)
		saddr.in.sin_addr.s_addr = htonl(INADDR_ANY);
		saddr.in.sin_port = htons(portNumber);
		len = sizeof(struct sockaddr_in);
		*/
	}
	if (bind(fd, &saddr.sa, len) < 0)
	{
		elog(DEBUG, "sockServerPort: bind failed: %m");
		return STATUS_ERROR;
	}

	if (family == AF_UNIX)
	{
		/* Arrange to unlink the socket file at exit */
		on_proc_exit(sockUnlink, 0);
		
		/* TODO: set ownership/permissions on unix socket */
	}

	listen(fd, SOMAXCONN);

	/*
	 * MS: I took this code from Dillon's version.  It makes the listening
	 * port non-blocking.  That is not necessary (and may tickle kernel
	 * bugs).
	 *
	 * fcntl(fd, F_SETFD, 1); fcntl(fd, F_SETFL, FNDELAY);
	 */

	*fdP = fd;
	if (family == AF_UNIX)
		chmod(saddr.un.sun_path, 0777);
	return STATUS_OK;
}

/* --------------------------------------------------------------------- */
/* sockServerAccept: accepts a pending client connection
 */
int
sockServerAccept(int server_fd, bufsockptr bsock)
{

	int			len,
				flag = 1;
	ACCEPT_TYPE_ARG3	addrlen;
	int			family = 0; 

	/* accept connection (and fill in the client (remote) address) */
	if(bsock == NULL)
		return STATUS_ERROR;
	addrlen = sizeof(bsock->raddr.sa);
	if ((bsock->sock = accept(server_fd,
							 &bsock->raddr.sa,
							 &addrlen)) < 0)
	{
		elog(NOTICE, "bufferedSock: serverAccept: accept: %m");
		bsock->status = CONNECTION_ERROR;
		return STATUS_ERROR;
	}
	family = bsock->raddr.sa.sa_family;
	len = family == AF_INET ?
		sizeof(struct sockaddr_in) : sizeof(struct sockaddr_un);
	/* fill in the server (local) address */
	addrlen = len;
	if (getsockname(bsock->sock, (struct sockaddr *) & bsock->laddr,
					&addrlen) < 0)
	{
		elog(NOTICE, "bufferedSock getsockname: getsockname: %m");
		bsock->status = CONNECTION_ERROR;
		return STATUS_ERROR;
	}
		
	if (family == AF_INET)
	{
		struct protoent *pe;
		int			on = 1;

		pe = getprotobyname("TCP");
		if (pe == NULL)
		{
			elog(NOTICE, "bufferedSock: getprotobyname failed");
			bsock->status = CONNECTION_ERROR;
			return STATUS_ERROR;
		}
				
		if (setsockopt(bsock->sock, pe->p_proto, TCP_NODELAY,
					   &on, sizeof(on)) < 0)
		{
			elog(NOTICE, "bufferedSock: setsockopt failed");
			bsock->status = CONNECTION_ERROR;
			return STATUS_ERROR;
		}
	}

	/* reset to non-blocking */
	//fcntl(bsock->sock, F_SETFL, 1);
	if(ioctl(bsock->sock, FIONBIO, &flag)<0)
		return STATUS_ERROR;
	
	bsock->status = NOT_CONNECTED;
	return STATUS_OK;
}

/* --------------------------------------------------------------------- */
/* sockClientConnect: connects to a server 
 */
int
sockClientConnect(const char *hostName, short portName, bufsockptr bsock)
{
	ACCEPT_TYPE_ARG3	len;
	int				err;
	struct hostent 	*hp;
	extern int		errno;
	/*char 			**p;*/
	int				family;
	char			host[MAX_GROUP_NAME];
	
	/* set up the server (remote) address */
	MemSet((char *) &bsock->raddr, 0, sizeof(bsock->raddr));
	
	if ( hostName == NULL )
	{
		if ( gethostname(host, MAX_GROUP_NAME) != 0 )
			return STATUS_ERROR;
		hostName = host;
	}		
  	#ifdef GCMGR_DEBUG
	elog(DEBUG, "hostName = '%s', host = '%s'", hostName, host);
	#endif
	
	/*---------------------------------------------*/
	family = ((hostName != NULL) ? AF_INET : AF_UNIX);
	bsock->raddr.sa.sa_family = family;
	
	/*
	if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
					sizeof(one))) == -1)
	{
		elog(DEBUG, "sockServerPort: setsockopt failed: %m");
		return STATUS_ERROR;
	}
	*/
	
	if (hostName)
	{
		if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET)
		{
			elog(NOTICE, "bufferedSock: ClientConnect:gethostbyname failed");
			bsock->status = CONNECTION_ERROR;
			return STATUS_ERROR;
		}
		memmove(&(bsock->raddr.in.sin_addr),
				hp->h_addr,
				hp->h_length);
		bsock->raddr.in.sin_family = AF_INET;
		bsock->raddr.in.sin_port = htons(portName);
		len = sizeof(struct sockaddr_in);
		
				
		/* (mc) -----------remove-----------------
		elog(DEBUG, "testing");
		for (p = hp->h_addr_list; *p != 0; p++) 
		{
			struct in_addr in;
			char **q;
			(void) memcpy(&in.s_addr, *p, sizeof (in.s_addr));
			elog(DEBUG, "%s\t%s", inet_ntoa(in), hp->h_name);
			for (q = hp->h_aliases; *q != 0; q++)
				elog(DEBUG, " %s", *q);
			elog(DEBUG, "");
		}
		---------------------------------*/
	}
	else
	{
		bsock->raddr.un.sun_family = AF_UNIX;
		// DAR HACK fix to match UNIXSOCK_PATH as in pqcomm
		UNIXSOCK_PATH(bsock->raddr.un, portName, UnixSocketDir);
		len = UNIXSOCK_LEN(bsock->raddr.un);
	}
	
	/* connect to the server */
	if ((bsock->sock = socket(bsock->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
	{
		elog(DEBUG, "bufferedSock: ClientConnect: socket call failed");
		bsock->status = CONNECTION_ERROR;
		return STATUS_ERROR;
	}

	err = connect(bsock->sock, &bsock->raddr.sa, len);
	if (err < 0)
	{
		elog(DEBUG, "bufferedSock: ClientConnect: connect failed %d:%d:%s", err,errno,strerror(errno));
		bsock->status = CONNECTION_ERROR;
		return STATUS_ERROR;
	}

	/* fill in the client address */
	if (getsockname(bsock->sock, &bsock->laddr.sa, &len) < 0)
	{
		elog(DEBUG, "bufferedSock: clientConnect: getsockname failed");
		bsock->status = CONNECTION_ERROR;
		return STATUS_ERROR;
	}
	
	bsock->status = CONNECTION_OK;
	return STATUS_OK;
}

/* --------------------------------------------------------------------- */
/* sockClose: closes buffered socket
 */
void
sockClose(bufsockptr bsock)
{
	close(bsock->sock);
	bsock->status = NOT_CONNECTED;
}

/* --------------------------------------------------------------------- */
/* sockUnlink: deletes file associated to AF_UNIX socket type
 */
void
sockUnlink(char path[])
{
	Assert(path[0]);
	unlink(path);
}

/* --------------------------------------------------------------------- */
/* sockResetBuffers: resets the input and output buffer indexes of 
		a socket
 */
void
sockResetBuffers(bool in, bool out, bufsockptr bsock)
{
	if(!bsock)
		return;
	if(in){
		bsock->inStart = 0;
		bsock->inEnd = 0;
		bsock->inCursor = 0;
	}
	if(out)
		bsock->outCount = 0;
}

/* --------------------------------------------------------------------- */
/* sockIsConnected: tells if socket has active connection
 */
int
sockIsConnected(bufsockptr bsock)
{
	if(bsock->status == NOT_CONNECTED)
		return (-1);
	else if(bsock->status == CONNECTION_ERROR)
		return 0;
	else
		return 1;
}

/* --------------------------------------------------------------------- */
/* sockNumLeft: gives the number of unread bytes in the 'incoming' buffer
 */
int
sockNumLeft(bufsockptr bsock)
{
	int nleft = 0;
	
	if(!bsock)
		return -1;
	
	nleft = bsock->inEnd - bsock->inCursor;
	if(nleft <= 0)
	{
		return 0;
	}
	return nleft;
}

/* --------------------------------------------------------------------- */
/* sockPeekHeader: reads the first n bytes in the 'incoming' buffer
			without marking them as read.
 */
int
sockPeekHeader(int nbytes, bufsockptr bsock, char *header)
{
	if (nbytes < 0 || nbytes > bsock->inEnd - bsock->inCursor)
		return EOF;

	memcpy(header, bsock->inBuffer + bsock->inCursor, nbytes);
	return 0;
}

/* --------------------------------------------------------------------- */
/* sockTransfer: transfers data from one buffered socket to another without
		unnecessary copying 
 */
int
sockTransfer(bufsockptr dest, bufsockptr src, int len)
{
  	#ifdef GCMGR_DEBUG
	elog(DEBUG, "sockTransfer: ltxnsock->inStart=%d, inCursor=%d, inEnd=%d, bytes=%d",
				src->inStart, src->inCursor, src->inEnd, len);
	#endif

	if(sockPutnchar(src->inBuffer + src->inCursor, len, dest) == 0)
		src->inCursor += len;
		return 0;
	return 1;
}

/* --------------------------------------------------------------------- */
/* sockIgnoreData: discards the first n bytes in the 'incoming' buffer
 */
int
sockIgnoreData(int nbytes, bufsockptr bsock)
{
	if(bsock->inCursor + nbytes <= bsock->inEnd)
		bsock->inCursor += nbytes;
		return 0;
	return 1;
}

/* --------------------------------------------------------------------- */
/* sockConsume: marks the bytes in the 'incoming' buffer that have been 
	read as consumed
 */
void
sockConsume(bufsockptr bsock)
{
	if(bsock->inStart < bsock->inCursor)
		bsock->inStart = bsock->inCursor;
}

/* --------------------------------------------------------------------- */
/* sockResetCursor: resets the reading cursor of the 'incoming' buffer
 */
void
sockResetCursor(bufsockptr bsock)
{
	if(bsock->inStart < bsock->inCursor)
		bsock->inCursor = bsock->inStart;
}

/* --------------------------------------------------------------------- */
/* sockGetSlen: gives the length of the null-terminated string beginning 
	at the read cursor position
 */
int
sockGetSlen(bufsockptr bsock)
{
	return strlen(bsock->inBuffer + bsock->inCursor);
}

/*
	extern buffer routines
*/

/* --------------------------------------------------------------------- */
/* _checkEbufLen: checks if an external buffer has enough room left to hold
	n bytes, if not, the buffer is reallocated
 */
static void
_checkEbufLen(int bytes, ebufptr extbuf)
{
	char *newbuf = NULL;
	int	 newsize = 0;

	if(!extbuf->buf)
	{
		if(extbuf->buflen != 0)
		{
			extbuf->buf = (char *) malloc(extbuf->buflen * sizeof(char));
		}
		else
		{
			extbuf->buflen = EBUF_DEFAULT_SIZE;
			extbuf->buf = (char *) malloc(extbuf->buflen * sizeof(char));
		}
#ifdef EBUF_DEBUG
		elog(DEBUG, "_checkEbufLen: Allocated ebuf (len = %d, buf_addr = %x)"
								, extbuf->buflen, extbuf->buf);
#endif
	}
	else
	{
		int avail = extbuf->buflen - extbuf->currindex;
		if(avail < bytes)
		{
			newsize = extbuf->buflen;
			
			do{
				newsize *= 2;
			}
			while(bytes < newsize - extbuf->currindex);
			
			newbuf = realloc(extbuf->buf, extbuf->buflen);
			
			if(newbuf)
			{
				extbuf->buf = newbuf;
				extbuf->buflen = newsize;
#ifdef EBUF_DEBUG
				elog(DEBUG, "_checkEbufLen: reallocated ebuf (len = %d, buf_addr = %x)"
											, extbuf->buflen, extbuf->buf);
#endif
			}
			else
			{
				elog(DEBUG, "EBuffer realloc failed");
			}
		}
	}
}

/* --------------------------------------------------------------------- */
/* sockInitE: initializes an external buffer
 */
void
sockInitE(ebufptr extbuf, int buflen)
{
	if(!extbuf)
		return;
	
	extbuf->buf = (char *) malloc(buflen * sizeof(char));
	if(!extbuf->buf)
	{
		elog(DEBUG, "Ebuf allocation failed");
		return;
	}
	extbuf->currindex = 0;
	extbuf->buflen = buflen;
#ifdef EBUF_DEBUG
	elog(DEBUG, "Initialized ebuf (len=%d, addr=%x)", extbuf->buflen, extbuf);
#endif
}

/* --------------------------------------------------------------------- */
/* sockDestroyE: discards an external buffer
 */
void
sockDestroyE(ebufptr extbuf)
{
	if(!extbuf)
		return;
		
	free(extbuf->buf);
	extbuf->buf = NULL;
	extbuf->buflen = 0;
	extbuf->currindex = 0;
#ifdef EBUF_DEBUG
	elog(DEBUG, "destroyed ebuf (addr=%x)", extbuf);
#endif
}

/* --------------------------------------------------------------------- */
/* sockGetCountE: gives the number of data bytes in the buffer
 */
int
sockGetCountE(ebufptr extbuf)
{
	return extbuf->currindex;
}

/* --------------------------------------------------------------------- */
/* sockFlushE: flushes an external buffer into a buffered socket
 */
void
sockFlushE(ebufptr extbuf, bufsockptr bsock, bool flushBeforePipe)
{
	char	*outBufSave;
	int		outCountSave;
	
#ifdef EBUF_DEBUG
	elog(DEBUG, "Flushing ebuf (sock=%d, ebuf_addr=%x, outCount=%d)"
							, bsock->sock, extbuf, extbuf->currindex);
#endif
	if(flushBeforePipe)
		sockFlush(bsock);
		
	outBufSave = bsock->outBuffer;
	outCountSave = bsock->outCount;
	
	bsock->outBuffer = extbuf->buf;
	bsock->outCount = extbuf->currindex;
	sockFlush(bsock);
	
	bsock->outBuffer = outBufSave;
	bsock->outCount = outCountSave;
}

/* --------------------------------------------------------------------- */
/* sockPutIntE: writes an int into an external buffer
 */
void
sockPutIntE(int value, int bytes, ebufptr extbuf)
{
	uint16		tmp2;
	uint32		tmp4;
	
	
	if(    extbuf->buflen < 0
		|| extbuf->currindex < 0 
		|| extbuf->buflen < extbuf->currindex
		|| (bytes != 4 && bytes != 2)
	  )
	{
		elog(DEBUG, "sockPutIntE: bad ebuf");	
		return;
	}
	
	_checkEbufLen(bytes, extbuf);

	switch (bytes)
	{
		case 2:
			tmp2 = htons((uint16) value);
			memcpy(extbuf->buf + extbuf->currindex, (char *) &tmp2, bytes);
			break;
		case 4:
			tmp4 = htonl((uint32) value);
			memcpy(extbuf->buf + extbuf->currindex, (char *) &tmp4, bytes);
			break;
	}
	extbuf->currindex += bytes;
#ifdef EBUF_DEBUG
	elog(DEBUG, "sockPutIntE (ebuf_addr=%x): put %d bytes (index=%d, buflen=%d)"
						, extbuf, bytes, extbuf->currindex, extbuf->buflen);
#endif
	return;
}

/* --------------------------------------------------------------------- */
/* sockPutsE: writes a null-terminated string into an external buffer
 */
void
sockPutsE(const char *s, ebufptr extbuf)
{
	int len = strlen(s) + 1;
	
#ifdef EBUF_DEBUG
	elog(DEBUG, "		sockPutsE: str=%sxxx, len=%d", s, len);
#endif

	if(extbuf->buflen <= 0 || extbuf->currindex < 0 
		|| extbuf->buflen < extbuf->currindex)
	{
		elog(DEBUG, "sockPutsE: bad ebuf");	
		return;
	}
	
	_checkEbufLen(len, extbuf);
	
	strncpy(extbuf->buf + extbuf->currindex, s, len);
	extbuf->currindex += len;
#ifdef EBUF_DEBUG
	elog(DEBUG, "sockPutsE (ebuf_addr=%x) : put %d bytes (index=%d, buflen=%d)"
						, extbuf, len, extbuf->currindex, extbuf->buflen);
#endif
}

/* --------------------------------------------------------------------- */
/* sockPutncharE: writes n characters into an external buffer
 */
void
sockPutncharE(const char *s, int len, ebufptr extbuf)
{
	if(extbuf->buflen <= 0 || extbuf->currindex < 0 
		|| extbuf->buflen < extbuf->currindex)
	{
#ifdef EBUF_DEBUG
		elog(DEBUG, "sockPutncharE: bad ebuf");	
#endif
		return;
	}
	
	_checkEbufLen(len, extbuf);
	
	memcpy(extbuf->buf + extbuf->currindex, s, len);
	extbuf->currindex += len;
#ifdef EBUF_DEBUG
	elog(DEBUG, "sockPutncharE (ebuf_addr=%x) : put %d bytes (index=%d, buflen=%d)"
						, extbuf, len, extbuf->currindex, extbuf->buflen);
#endif
}

