/*
 * pqpacket.c -- routines for reading and writing data packets
 *	sent/received by POSTGRES clients and servers
 *
 * $Header: /private/postgres/src/lib/libpq/RCS/pqpacket.c,v 1.8 1992/04/21 13:14:18 clarsen Exp $
 *
 * This is the module that understands the lowest-level part
 * of the communication protocol.  All of the trickyness in
 * this module is for making sure that non-blocking IO in
 * the Postmaster works correctly.   Check the notes in PacketRecv
 * on non-blocking I/O.
 *
 * Data Structures:
 *	Port has two important functions. (1) It records the 
 *	sock/addr used in communication. (2) It holds partially
 *	read in messages.  This is especially important when
 *	we haven't seen enough to construct a complete packet 
 *	header.
 *
 * PacketBuf -- None of the clients of this module should know
 *	what goes into a packet hdr (although they know how big
 *	it is).  This routine is in charge of host to net order
 *	conversion for headers.  Data conversion is someone elses
 *	responsibility.
 *
 * Memory Allocation:  is not handled in this module
 *
 * IMPORTANT: these routines are called by backends, clients, and
 *	the Postmaster.
 *
 * NOTE: assume htonl() is sufficient for all header structs.  If
 *	header structs become longs, must switch to htonl().
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <fcntl.h>

#include "tmp/postgres.h"
#include "utils/log.h"
#include "storage/ipci.h"
#include "tmp/pqcomm.h"

extern char PQerrormsg[];

/*
 * PacketRecieve -- receive a packet on a port.
 *
 * RETURNS: connection id of the packet sender, if one
 * is available.
 */
PacketReceive(port, buf, nonBlocking, connIdP)
Port		*port;	/* receive port */
PacketBuf	*buf;	/* MAX_PACKET_SIZE-worth of buffer space */
Boolean		nonBlocking; /* NON_BLOCKING or BLOCKING i/o */
ConnId		*connIdP;    /* sender's connection (seqpack) */
{
  int		status;
  PacketLen	cc;	    	/* character count -- bytes recvd */
  PacketLen	packetLen; 	/* remaining packet chars to read */
  Addr	tmp;	   	/* curr recv buf pointer */
  PacketHdr	*hdr = (PacketHdr *) buf;  /* incoming hdr information */
  int		addrLen = sizeof(struct sockaddr_in);
  int		flag;
  int		decr;

  if (nonBlocking) {
    flag = MSG_PEEK;
    decr = 0;
  } else {
    flag = 0;
    decr = sizeof(PacketHdr);
  }
  /*
   * Assume port->nBytes is zero unless we were interrupted during
   * non-blocking I/O.  This first recvfrom() is to get the hdr
   * information so we know how many bytes to read.  Life would
   * be very complicated if we read too much data (buffering).
   */
  tmp = ((Addr)buf) + port->nBytes;
  if (port->nBytes >= sizeof(PacketHdr)) {
    packetLen = htonl(hdr->len) + sizeof(PacketHdr);
  } else {
    cc = recvfrom(port->sock, tmp, sizeof(PacketHdr), flag, 
		  &(port->addr), &addrLen);
    if ( cc < sizeof(PacketHdr)) {
      /* if cc is negative, the system call failed */
      if (cc < 0) {
	return(STATUS_ERROR);
      }
      /* 
       * cc == 0 means the connection was broken at the 
       * other end.
       */
      else if (! cc) {
	return(STATUS_INVALID);

      } else {
	/*
	 * Worst case.  We didn't even read in enough data to
	 * get the header.  IN UDP (seq-pack) THIS IS IMPOSSIBLE.
	 * Messages are delivered in full packets.  In the stream
	 * protocol, it probably won't happen unless the client
	 * is malicious (so its even harder to test).
	 *
	 * Don't save the number of bytes we've read so far.
	 * Since we only peeked at the incoming message, the
	 * kernel is going to keep it for us.
	 */

	*connIdP = INVALID_ID;
	return(STATUS_NOT_DONE);
      }
    } else {

      /* great. got the header. now get the true length (including
       * header size).
       */
      packetLen = htonl(hdr->len);
      packetLen -= decr;
      tmp += decr - port->nBytes;
    }
  }

  /*
   * Now that we know how big it is, read the packet.  We read
   * the entire packet, since the last call was just a peek.
   */
  while (packetLen) { 
    cc = recvfrom(port->sock, tmp, packetLen, NULL, 
		  &(port->addr), &addrLen);
    if (cc < 0)
      return(STATUS_ERROR);

    /* 
     * cc == 0 means the connection was broken at the 
     * other end.
     */
    else if (! cc) 
      return(STATUS_INVALID);

    tmp += cc;
    packetLen -= cc;


    /* if non-blocking, we're done. */
    if (nonBlocking && packetLen) {
      port->nBytes += cc;
      return(STATUS_NOT_DONE);
    }
  }

  *connIdP = htonl(hdr->connId);
  port->nBytes = 0;
  return(STATUS_OK);
}


/*
 * PacketSend -- send a single-packet message.
 *
 * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
 * SIDE_EFFECTS: may block.
 * NOTES: Non-blocking writes would significantly complicate 
 *	buffer management.  For now, we're not going to do it.
 */
PacketSend(port, conn, buf, type, len, nonBlocking)
Port		*port;
Connection	*conn;
PacketBuf	*buf;
MsgType		type;
PacketLen	len;
Boolean		nonBlocking;
{
  int			status;
  PacketLen		totalLen;
  PacketHdr		*hdr = (PacketHdr *) buf;
  int			addrLen = sizeof (struct sockaddr_in);

  Assert( ! nonBlocking );
  Assert( buf );

  totalLen = len;

  hdr->seqno = htonl(conn->seqno++);
  hdr->connId = htonl(conn->id);
  hdr->type = (MsgType)htonl(type);
  hdr->len = htonl(len);

  len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
	       &(port->addr), addrLen);

  if (len < totalLen) {
      strcpy(PQerrormsg,"PacketSend: couldn't send complete packet\n");
    fprintf(stderr,PQerrormsg);
    return(STATUS_ERROR);
  }

  return(STATUS_OK);
}

/*
 * PacketData -- return components of msg hdr.
 *
 * Clients of this module don't know what the headers look
 * like.  
 *
 * NOTE: seqnoP is an IN/OUT parameter.  The others are out only.
 *
 */
PacketData(buf, dataP, bufSizeP, msgTypeP, seqnoP)
Addr		buf;
Addr 	*dataP;
int  		*bufSizeP;
MsgType		*msgTypeP;
SeqNo		*seqnoP;
{
  PacketHdr	*hdr = (PacketHdr *) buf;

  /* seq pack protocol only */
  if (htonl(hdr->seqno) != *seqnoP)
    *msgTypeP = DUPLICATE_MSG;
  else
    *seqnoP++;

  *msgTypeP = (MsgType)htonl(hdr->type);
  *bufSizeP = htonl(hdr->len);

  /* data part starts right after the header */
  *dataP = (Addr) (hdr+1);
}


/*
 * PacketRetransmit -- resend a buffer.  
 *
 * Assume that the header is already initialized.  We're
 * just going to resend the same message.
 */
PacketRetransmit(buf,port,addr)
Addr		buf;
Port		*port;
Addr		addr;
{
  PacketHdr 	*hdr = (PacketHdr *) buf;
  PacketLen	len,totalLen;
  int		addrLen = sizeof (struct sockaddr_in);

  len = totalLen = htonl(hdr->len);
  len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
	       (struct sockaddr *)addr, addrLen);

  if (len < totalLen) {
    strcpy(PQerrormsg,"PacketWrite: couldn't send complete packet\n");
    fprintf(stderr,PQerrormsg);
    return(STATUS_ERROR);
  }

  return(STATUS_OK);
}


/*
 * PacketBuf -- remove the header from a packet buf for
 * 	a client.
 *
 * NOTE: not sure this is necessary.  Some clients are going
 *	to have to know how big the thing is in order to do
 *	memory allocation.
 */
PacketBufInit(bufP, bufSizeP)
Addr 		*bufP;
PacketLen  	*bufSizeP;
{
  *bufP  += sizeof(PacketHdr);
  *bufSizeP -= sizeof(PacketHdr);
}

