/*
 * ipc.h IPC abstraction data structures.
 *
 * author  Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>,
 *	Alan Robertson <alanr@unix.sh>
 *
 *
 * Copyright (c) 2002 International Business Machines
 * Copyright (c) 2002  Xiaoxiang Liu <xiliu@ncsa.uiuc.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef _IPC_H_
#define _IPC_H_
#include <glib.h>
#undef MIN
#undef MAX
#include <sys/types.h>
#include <sys/poll.h>

#ifdef IPC_TIME_DEBUG
#include <clplumbing/longclock.h>
#define MAXIPCTIME 3000

#endif

/* constants */
#define DEFAULT_MAX_QLEN 64
#define MAX_MESSAGE_SIZE 4096
#define MAX_MSGPAD 128
/* channel and connection status */
#define IPC_CONNECT		1	/* Connected: can read, write */
#define IPC_WAIT		2	/* Waiting for connection */
#define IPC_DISCONNECT		3	/* Disconnected, can't read or write*/
#define IPC_DISC_PENDING	4	/* Disconnected, can't write but    */
					/* may be more data to read	    */

#define MAXFAILREASON		128

#define IPC_SERVER		1
#define IPC_CLIENT		2
#define IPC_PEER		3

#define IPC_ISRCONN(ch) ((ch)->ch_status == IPC_CONNECT		\
	||	(ch)->ch_status == IPC_DISC_PENDING)

#define IPC_ISWCONN(ch) ((ch)->ch_status == IPC_CONNECT)

/* general return values */
#define IPC_OK 0
#define IPC_FAIL 1
#define IPC_BROKEN 2
#define IPC_INTR 3
#define IPC_TIMEOUT 4

/*
 *	IPC:  Sockets-like Interprocess Communication Abstraction
 *
 *	We have two fundamental abstractions which we maintain.
 *	Everything else is in support of these two abstractions.
 *
 *	These two main abstractions are:
 *
 *	IPC_WaitConnection:
 *	A server-side abstraction for waiting for someone to connect.
 *
 *	IPC_Channel:
 *	An abstraction for an active communications channel.
 *
 *	All the operations on these two abstractions are carried out
 *	via function tables (channel->ops).  Below we refer to the
 *	function pointers in these tables as member functions.
 *
 *  On the server side, everything starts up with a call to
 *	ipc_wait_conn_constructor(), which returns an IPC_WaitConnection.
 *
 *	Once the server has the IPC_WaitConnection object in hand,
 *	it can give the result of the get_select_fd() member function
 *	to poll or select to inform you when someone tries to connect.
 *
 *	Once select tells you someone is trying to connect, you then
 *	use the accept_connection() member function to accept
 *	the connection.  accept_connection() returns an IPC_Channel.
 *
 *	With that, the server can talk to the client, and away they
 *	go ;-)
 *
 *  On the client side, everything starts up with a call to
 *	ipc_channel_constructor() which we use to talk to the server.
 *	The client is much easier ;-)
 */


typedef struct IPC_WAIT_CONNECTION	IPC_WaitConnection;
typedef struct IPC_CHANNEL		IPC_Channel;

typedef struct IPC_MESSAGE		IPC_Message;
typedef struct IPC_QUEUE		IPC_Queue;
typedef struct IPC_AUTH			IPC_Auth;

typedef struct IPC_OPS			IPC_Ops;
typedef struct IPC_WAIT_OPS		IPC_WaitOps;



/* wait connection structure. */
struct IPC_WAIT_CONNECTION{
	int		ch_status;	/* wait conn. status.*/
	void *		ch_private;	/* wait conn. private data. */
	IPC_WaitOps	*ops;		/* wait conn. function table .*/
};


typedef void(*flow_callback_t)(IPC_Channel*, void*);

/* channel structure.*/
struct IPC_CHANNEL{
	int		ch_status;	/* identify the status of channel.*/
	int		refcount;	/* reference count */
	pid_t		farside_pid;	/* far side pid */
	void*		ch_private;	/* channel private data. */
					/* (may contain conn. info.) */
	IPC_Ops*	ops;		/* IPC_Channel function table.*/

	/* number of bytes needed
	 * at the begginging of <ipcmessage>->msg_body
	 * it's for msg head needed to tranmit in wire	 
	 */
	unsigned int	msgpad;
	
	/* the number of bytes remainng to send for the first message in send queue
	   0 means nothing has been sent thus all bytes needs to be send
	   n != 0 means there are still n bytes needs to be sent
	*/
	unsigned int	bytes_remaining;


	/* is the send blocking or nonblocking*/
	gboolean	should_send_block;
	
	/* if send would block, should an error be returned or not */
	gboolean	should_block_fail;
	
/*  There are two queues in channel. One is for sending and the other
 *  is for receiving. 
 *  Those two queues are channel's internal queues. They should not be 
 *  accessed directly.
 */
	/* private: */
	IPC_Queue*	send_queue; 
	IPC_Queue*	recv_queue; 

	/* buffer pool for receive in this channel*/
	struct ipc_bufpool* pool;

	/* the follwing is for send flow control*/
	int		high_flow_mark;
	int		low_flow_mark;
	void*		high_flow_userdata;
	void*		low_flow_userdata;
	flow_callback_t	high_flow_callback;
	flow_callback_t	low_flow_callback;
	
	int		conntype;
	
	char		failreason[MAXFAILREASON];
};

struct IPC_QUEUE{
	size_t		current_qlen;	/* Current qlen */
	size_t		max_qlen;	/* Max allowed qlen */
	GList*		queue;		/* List of messages */
};

/* authentication information : set of gids and uids */
struct IPC_AUTH {
	GHashTable * uid;	/* hash table for user id */
	GHashTable * gid;	/* hash table for group id */
};


/* Message structure. */
struct IPC_MESSAGE{
	size_t	msg_len;
	void*	msg_buf;
	void*	msg_body;
/* 
 * IPC_MESSAGE::msg_done 
 *   the callback function pointer which can be called after this 
 *   message is sent, received or otherwise processed.
 *
 * Parameter:
 * msg: the back pointer to the message which contains this
 *	function pointer.
 * 
 */
	void (* msg_done)(IPC_Message * msg);
	void* msg_private;	/* the message private data.	*/
				/* Belongs to message creator	*/
				/* May be used by callback function. */
	IPC_Channel * msg_ch;	/* Channel the */
				/* message is from/in */

};

struct IPC_WAIT_OPS{
/*
 * IPC_WAIT_OPS::destroy
 *   destroy the wait connection and free the memory space used by
 *	this wait connection.
 * 
 * Parameters:
 *   wait_conn (IN):  the pointer to the wait connection.
 *
 */ 
	void (* destroy)(IPC_WaitConnection *wait_conn);
/*
 * IPC_WAIT_OPS::get_select_fd
 *   provide a fd which user can listen on for a new coming connection.
 *
 * Parameters: 
 *   wait_conn (IN) : the pointer to the wait connection which
 *	we're supposed to return the file descriptor for
 *	(the file descriptor can be used with poll too ;-))
 *
 * Return values:
 *   integer >= 0 :  the select_fd.
 *       -1       :  can't get the select fd.
 *
 */
	int (* get_select_fd)(IPC_WaitConnection *wait_conn);
/*
 * IPC_WAIT_OPS::accept_connection
 *   accept and create a new connection and verify the authentication.
 *
 * Parameters:
 *   wait_conn (IN) : the waiting connection which will accept
 *	create the new connection.
 *   auth_info (IN) : the authentication information which will be
 *	verified for the new connection.
 *
 * Return values:
 *   the pointer to the new IPC channel; NULL if the creation or
 *	authentication fails.
 *
 */
	IPC_Channel * (* accept_connection)
		(IPC_WaitConnection * wait_conn, IPC_Auth *auth_info);
};

/* Standard IPC channel operations */

struct IPC_OPS{
/*
 * IPC_OPS::destroy
 *   brief destroy the channel object.
 *
 * Parameters:
 *   ch  (IN) : the pointer to the channel which will be destroyed.
 *
 */
	void  (*destroy) (IPC_Channel * ch);
/*
 * IPC_OPS::initiate_connection
 *   used by service user side to set up a connection.
 *
 * Parameters:
 *   ch (IN) : the pointer to channel used to initiate the connection. 
 *
 * Return values:
 *   IPC_OK  : the channel set up the connection successfully.
 *   IPC_FAIL     : the connection initiation fails.
 *
 */
	int (* initiate_connection) (IPC_Channel * ch);
/*
 * IPC_OPS::verify_auth
 *   used by either side to verify the identity of peer on connection.
 *
 * Parameters
 *   ch (IN) : the pointer to the channel.
 *
 * Return values:
 *   IPC_OK   : the peer is trust.
 *   IPC_FAIL : verifying authentication fails.
 */
	int (* verify_auth) (IPC_Channel * ch, IPC_Auth* info);
/*
 * IPC_OPS::assert_auth
 *   service user asserts to be certain qualified service user.
 *
 * Parameters:
 *   ch    (IN):  the active channel.
 *   auth  (IN):  the hash table which contains the asserting information.
 *
 * Return values:
 *   IPC_OK :  assert the authentication successfully.
 *   IPC_FAIL    : assertion fails.
 *
 * NOTE:  This operation is a bit obscure.  It isn't needed with
 *	UNIX domain sockets at all.  The intent is that some kinds
 *	of IPC (like FIFOs), do not have an intrinsic method to
 *	authenticate themselves except through file permissions.
 *	The idea is that you must tell it how to chown/grp your
 *	FIFO so that the other side and see that if you can write
 *	this, you can ONLY be the user/group they expect you to be.
 *	But, I think the parameters may be wrong for this ;-)
 */
	int (* assert_auth) (IPC_Channel * ch, GHashTable * auth);
/*
 * IPC_OPS::send
 *   send the message through the sending connection.
 *
 * Parameters:
 *   ch  (IN) : the channel which contains the connection.
 *   msg (IN) : pointer to the sending message. User must
 *	allocate the message space.
 *
 * Return values:
 *   IPC_OK : the message was either sent out successfully or
 *	appended to the send_queue.
 *   IPC_FAIL    : the send operation failed.
 *   IPC_BROKEN  : the channel is broken.
 *
*/    
	int (* send) (IPC_Channel * ch, IPC_Message* msg);

/*
 * IPC_OPS::recv
 *   receive the message through receving queue.
 *
 * Parameters:
 *   ch  (IN) : the channel which contains the connection.
 *   msg (OUT): the IPC_MESSAGE** pointer which contains the pointer
 *		to the received message or NULL if there is no
 *		message available.
 *
 * Return values:
 *   IPC_OK	: receive operation is completed successfully.
 *   IPC_FAIL	: operation failed.
 *   IPC_BROKEN	: the channel is broken (disconnected)
 *
 * Note: 
 *   return value IPC_OK doesn't mean the message is already 
 *   sent out to (or received by) the peer. It may be pending
 *   in the send_queue.  In order to make sure the message is no
 *   longer needed, please specify the msg_done function in the
 *   message structure and once this function is called, the
 *   message is no longer needed.
 *
 *   is_sending_blocked() is another way to check if there is a message 
 *   pending in the send_queue.
 *
 */
	int (* recv) (IPC_Channel * ch, IPC_Message** msg);

/*
 * IPC_OPS::waitin
 *   Wait for input to become available
 *
 * Parameters:
 *   ch  (IN) : the channel which contains the connection.
 *
 * Side effects:
 *	If output becomes unblocked while waiting, it will automatically
 *	be resumed without comment.
 *
 * Return values:
 *   IPC_OK	: a message is pending or output has become unblocked.
 *   IPC_FAIL	: operation failed.
 *   IPC_BROKEN	: the channel is broken (disconnected)
 *   IPC_INTR	: waiting was interrupted by a signal
 */
	int (* waitin) (IPC_Channel * ch);
/*
 * IPC_OPS::waitout
 *   Wait for output to finish
 *
 * Parameters:
 *   ch  (IN) : the channel which contains the connection.
 *
 * Side effects:
 *	If input becomes available while waiting, it will automatically
 *	be read into the channel queue without comment.
 *
 * Return values:
 *   IPC_OK	: output no longer blocked
 *   IPC_FAIL	: operation failed.
 *   IPC_BROKEN	: the channel is broken (disconnected)
 *   IPC_INTR	: waiting was interrupted by a signal
 */
	int (* waitout) (IPC_Channel * ch);

/*
 * IPC_OPS::is_message_pending
 *   check to see if there is any messages ready to read, or hangup has
 *   occurred.
 *
 * Parameters:
 *   ch (IN) : the pointer to the channel.
 *
 * Return values:
 *   TRUE : there are messages ready to read, or hangup.
 *   FALSE: there are no messages ready to be read.
 */
	gboolean (* is_message_pending) (IPC_Channel  * ch);

/*
 * IPC_OPS::is_sending_blocked
 *   check the send_queue to see if there are any messages blocked. 
 *
 * Parameters:
 *   ch (IN) : the pointer to the channel.
 *
 * Return values:
 *   TRUE : there are messages blocked (waiting) in the send_queue.
 *   FALSE: there are no message blocked (waiting) in the send_queue.
 *
 *  See also:
 *	get_send_select_fd()
 */  
	gboolean (* is_sending_blocked) (IPC_Channel  *ch);

/*
 * IPC_OPS::resume_io
 *   Resume all possible IO operations through the IPC transport
 *
 * Parameters:
 *   the pointer to the channel.
 *
 * Return values:
 *   IPC_OK : resume all the possible I/O operation successfully.
 *   IPC_FAIL   : the operation fails.
 *   IPC_BROKEN : the channel is broken.
 *
 */
	int (* resume_io) (IPC_Channel  *ch);
/*
 * IPC_OPS::get_send_select_fd()
 *   return a file descriptor which can be given to select/poll. This fd
 *   is used by the IPC code for sending.  It is intended that this be
 *   ONLY used with select, poll, or similar mechanisms, not for direct I/O.
 *   Note that due to select(2) and poll(2) semantics, you must check
 *   is_sending_blocked() to see whether you should include this FD in
 *   your poll for writability, or you will loop very fast in your
 *   select/poll loop ;-)
 *
 * Parameters:
 *   ch (IN) : the pointer to the channel.
 *
 * Return values:
 *   integer >= 0 : the send fd for selection.
 *      -1         : there is no send fd.
 *
 *  See also:
 *	is_sending_blocked()
 */
	int   (* get_send_select_fd) (IPC_Channel * ch);
/*
 * IPC_OPS::get_recv_select_fd
 *   return a file descriptor which can be given to select. This fd
 *   is for receiving.  It is intended that this be ONLY used with select,
 *   poll, or similar mechanisms, NOT for direct I/O.
 *
 * Parameters:
 *   ch (IN) : the pointer to the channel.
 *
 * Return values:
 *   integer >= 0 : the recv fd for selection.
 *       -1        : there is no recv fd.
 *
 *	NOTE:  This file descriptor is often the same as the send
 *	file descriptor.
 */
	int   (* get_recv_select_fd) (IPC_Channel * ch);
/*
 * IPC_OPS::set_send_qlen
 *   allow user to set the maximum send_queue length.
 *
 * Parameters
 *   ch    (IN) : the pointer to the channel.
 *   q_len (IN) : the max length for the send_queue.
 *
 * Return values:
 *   IPC_OK : set the send queue length successfully.
 *   IPC_FAIL    : there is no send queue. (This isn't supposed
 *		 	to happen).
 *                It means something bad happened.
 *
 */
	int  (* set_send_qlen) (IPC_Channel * ch, int q_len);
/*
 * IPC_OPS::set_recv_qlen
 *   allow user to set the maximum recv_queue length.
 *
 * Parameters:
 *   ch    (IN) : the pointer to the channel.
 *   q_len (IN) : the max length for the recv_queue.
 *
 * Return values:
 *   IPC_OK : set the recv queue length successfully.
 *   IPC_FAIL    : there is no recv queue.
 *
 */
	int  (* set_recv_qlen) (IPC_Channel * ch, int q_len);


/*
 * IPC_OPS: set callback for high/low flow mark
 * ch	(IN) : the pointer to the channel
 * callback (IN) : the callback function
 * user_data(IN) : a pointer to user_data
 *		   callback will be called with channel and
 *		   this user_data as parameters
 *
 * Return values:
 *	void
 *
 */
	
	
	void (* set_high_flow_callback) (IPC_Channel* ch , 	
					 flow_callback_t callback,
					 void* user_data);
	
	void (* set_low_flow_callback) (IPC_Channel* ch , 	
					 flow_callback_t callback,
					 void* user_data);
	
/*
 * IPC_OPS::new_ipcmsg
 * ch	(IN) : the pointer to the channel
 * data (IN) : data to be copied to the message body
 * len	(IN) : data len
 * private (IN): the pointer value to set as in the message
 *
 * Return values:
 *	the pointer to a new created message will be
 *	returned if success or NULL if failure
 *
 */
	
	IPC_Message*	(*new_ipcmsg)(IPC_Channel* ch, const void* data, 
				      int len, void* private);
	
	
/*
 * IPC_OPS::nget_chan_status
 * ch	(IN) : the pointer to the channel
 *
 * Return value:
 *	channel status.
 *
 */
	int	(*get_chan_status)(IPC_Channel* ch);

	
/*
 * These two functions returns true if the corresponding queue 
 * is full, otherwise it returns false
 */
	
	gboolean (*is_sendq_full)(struct IPC_CHANNEL * ch);
	gboolean (*is_recvq_full)(struct IPC_CHANNEL * ch);


	/* Get the connection type for the channel
	 * it can be IPC_SERVER, IPC_CLIENT, IPC_PEER
	 */
	
	int (*get_conntype)(struct IPC_CHANNEL* ch);

	int (*disconnect)(struct IPC_CHANNEL* ch);
		
};


/*
 * ipc_wait_conn_constructor:
 *    the common constructor for ipc waiting connection. 
 *    Use ch_type to identify the connection type. Usually it's only
 *    needed by server side.
 *
 * Parameters:
 *    ch_type   (IN) : the type of the waiting connection to create.
 *    ch_attrs  (IN) : the hash table which contains the attributes
 *			needed by this waiting connection in name/value
 *			pair format.
 *
 *			For example, the only attribute needed by UNIX
 *			domain sockets is path name.
 *
 * Return values:
 *    the pointer to a new waiting connection or NULL if the connection
 *			can't be created.
 * Note:
 *    current implementation only supports unix domain socket 
 *    whose type is IPC_DOMAIN_SOCKET 
 *
 */
extern IPC_WaitConnection * ipc_wait_conn_constructor(const char * ch_type
,	GHashTable* ch_attrs);

/*
 * ipc_channel_constructor:
 *   brief the common constructor for ipc channel. 
 *   Use ch_type to identify the channel type.
 *   Usually this function is only called by client side.
 *
 * Parameters:
 *   ch_type  (IN): the type of the channel you want to create.
 *   ch_attrs (IN): the hash table which contains the attributes needed
 *		by this channel.
 *                  For example, the only attribute needed by UNIX domain
 *			socket is path name.
 *
 * Return values:
 *   the pointer to the new channel whose status is IPC_DISCONNECT
 *	or NULL if the channel can't be created.
 *
 * Note:
 *   current implementation only support unix domain socket 
 *   whose type is IPC_DOMAIN_SOCKET 
 *
 */
extern IPC_Channel  * ipc_channel_constructor(const char * ch_type
,	GHashTable* ch_attrs);

/*
 * ipc_channel_pair:
 *   Construct a pair of connected IPC channels in a fashion analogous
 *	to pipe(2) or socketpair(2).
 *
 * Parameters:
 *   channels: an array of two IPC_Channel pointers for return result
 */
int ipc_channel_pair(IPC_Channel* channels[2]);

/*
 * ipc_set_auth:
 *   A helper function used to convert array of uid and gid into
 *	an authentication structure (IPC_Auth)
 *
 * Parameters:
 *   a_uid    (IN): the array of a set of user ids.
 *   a_gid    (IN): the array of a set of group ids.
 *   num_uid  (IN): the number of user ids.
 *   num_gid  (IN): the number of group ids.
 *
 * Return values:
 *   the pointer to the authentication structure which contains the 
 *   set of uid and the set of gid. Or NULL if this structure can't
 *	be created.
 *
 */


IPC_Auth*  ipc_str_to_auth(const char * uidlist, int, const char * gidlist, int);

extern IPC_Auth * ipc_set_auth(uid_t * a_uid, gid_t * a_gid
,	int num_uid, int num_gid);

/* Destroys an object constructed by ipc_set_auth or ipc_str_to_auth() */
extern void ipc_destroy_auth(IPC_Auth * auth);

extern void ipc_set_pollfunc(int (*)(struct pollfd*, unsigned int, int));


#ifdef IPC_TIME_DEBUG

enum MSGPOS_IN_IPC{
	MSGPOS_ENQUEUE,
	MSGPOS_SEND,
	MSGPOS_RECV,
	MSGPOS_DEQUEUE
};

#endif


struct SOCKET_MSG_HEAD{
	int msg_len;
	unsigned int magic;
#ifdef IPC_TIME_DEBUG
	longclock_t enqueue_time;
	longclock_t send_time;
	longclock_t recv_time;
	longclock_t dequeue_time;
#endif

};


/* MAXMSG is the maximum final message size on the wire. */
#define	MAXMSG		(256*1024)
/* MAXUNCOMPRESSED is the maximum, raw data size prior to compression. */
/* 1:8 compression ratio is to be expected on data such as xml */
#define	MAXUNCOMPRESSED	(2048*1024)
#define HEADMAGIC	0xabcd
#define POOL_SIZE (4*1024)
struct ipc_bufpool{
	
	int refcount;
	char* currpos;
	char* consumepos;
	char* startpos;
	char* endpos;
	int size;
};

struct ipc_bufpool* ipc_bufpool_new(int);

void	ipc_bufpool_del(struct ipc_bufpool* pool);

int	ipc_bufpool_spaceleft(struct ipc_bufpool* pool);

int	ipc_bufpool_update(struct ipc_bufpool* pool,
			   struct IPC_CHANNEL * ch,
			   int msg_len,
			   IPC_Queue* rqueue);

gboolean	ipc_bufpool_full(struct ipc_bufpool* pool,
				 struct IPC_CHANNEL* ch,
				 int*);
int		ipc_bufpool_partial_copy(struct ipc_bufpool* dstpool,
					 struct ipc_bufpool* srcpool);

void	ipc_bufpool_ref(struct ipc_bufpool* pool);

void	ipc_bufpool_unref(struct ipc_bufpool* pool);

void	set_ipc_time_debug_flag(gboolean flag);

#define	IPC_PATH_ATTR		"path"		/* pathname attribute */
#define	IPC_DOMAIN_SOCKET	"uds"		/* Unix domain socket */
#define IPC_MODE_ATTR           "sockmode"      /* socket mode attribute */

#ifdef IPC_DOMAIN_SOCKET
#	define	IPC_ANYTYPE		IPC_DOMAIN_SOCKET
#else
#	error "No IPC types defined(!)"
#endif

#endif
