/* 
 * Define queues and messages
 *
 * $Log: sessipc.h,v $
 * Revision 2.0.1.1  2000/02/25 01:13:50  greyham
 * patch42: Add author's regression tests to distribution
 *
 * Revision 1.6  1993/07/09  16:49:21  leisner
 * added inline functions to get/put subvalue
 *
 * Revision 1.5  1993/06/04  18:10:41  leisner
 * *** empty log message ***
 *
 * Revision 1.4  1993/05/27  15:16:43  leisner
 * some changes
 *
 * Revision 1.3  1993/05/20  15:37:45  leisner
 * release
 *
 * Revision 1.2  1993/05/19  13:36:21  leisner
 * Binding queues to sendhandle, making answerback stack part of queue
 *
 * Revision 1.1  1993/05/17  15:31:22  leisner
 * Initial revision
 *
 */


#ifndef __SESSIPC_H__
#define __SESSIPC_H__

#define IPC_SOCKET_BASED

#include <sys/types.h>
#include <sys/time.h>

#ifdef RCSID
static char *ipc_rcsid = "$Id: sessipc.h,v 2.0.1.1 2000/02/25 01:13:50 greyham Exp greyham $";
#endif

#ifndef NULL
#define NULL (void *) 0
#endif

/* for the time being, put it here */
#undef TRUE
#undef FALSE
typedef enum { TRUE=1, FALSE=0 } BOOLEAN;


typedef unsigned int MESSAGE_TYPE;
typedef unsigned int QUEUE_ID; 
typedef unsigned char FUNC_CODE;
typedef unsigned short MESSAGE_NUMBER;
typedef short MESSAGE_SIZE;

/* Message types are defines as follows:
 * types are a 32 bit doubleword, LSB is 0
 * bit 31 - FD being passed
 * bit 30 - fipc message
 * bit 29 - fipc final answer
 * bit 28 - fipc interim answer
 * for any bits 28-31 set:
 * bit 0-15 -- message number
 * bit 16-23 -- function ID
 * Otherwise, its up to a user
 */
#define IPC_FD         (1 << 31)
#define FIPC_MESS  (1 << 30)
#define FIPC_FINAL_ANSWER (1 << 29)
#define FIPC_INTERIM_ANSWER (1 << 28)
#define IPC_FUNC_CODE_OFFSET 16

#define IPC_MAX_MESSAGE 512

#define QLIST void

/* Doug McIlroy likes this?? */
enum { LAST_PREDEFINED_QUEUE = 10000 };

typedef struct { 
	/* when an fd is passed */
	int fd;
#define MAGIC_MESSAGE 0xae374256
#ifdef MAGIC_MESSAGE
	int magic_message;
#endif
	/* can be anything the sender sets, but could be the senders
	 * QUEUE_ID
         */
	QUEUE_ID source_queue_name; 
	MESSAGE_TYPE  type; 
	/* I don't understand this */
	int subtype;	
	/* how large the message is */
	MESSAGE_SIZE size; 
} EACH_MESSAGE; 

#define NULL_QUEUEID 0
 
/* this is used to align the message (double? ) */
typedef long ALIGN_TYPE;

typedef struct {
	EACH_MESSAGE each_message;
	union {
		char message_data[IPC_MAX_MESSAGE];
		ALIGN_TYPE aligner;
	} m_data;
} MESSAGE;


typedef struct queued_message {
	/* links to prev and next message, NULL indicates end of list.
	 * It is sorted in chronological order of message arrival
	 */
	struct queued_message *prev;	
	struct queued_message *next;
	MESSAGE *message;
} QUEUED_MESSAGE;

/* one of the following is used for each FIPC with an answer sent */
typedef struct answer_block {
        /* fipc being used */
        FUNC_CODE current_fipc; 
        /* message number for a particular fipc */
        MESSAGE_NUMBER message_number;
        /* where the answers go -- nominally a structure */
        void *answer_back;
        /* answer_block->next = NULL is end of list
         * answer_block->next = NULL Is start of list 
         */ 
        struct answer_block *next;
        struct answer_block *prev;
} ANSWER_BLOCK;

/* Functional IPC needs to have this */
/* one of the following is used for each FIPC registered */
typedef struct fipc {
        /* number of fipc (same for all processes) */
        FUNC_CODE fipc_number;
        /* function used to transmit message if non-null --
         * this is only useful on the side which receives
         * the message and processes it
         */
        void  (*server_func)(MESSAGE *);
        /* function used for an answer if non-null  --
         * this is only useful on the side which sends the message and
         * waits for an answer
         */
        int (*answer_func)(MESSAGE *, ANSWER_BLOCK *);
        /* next fipc, NULL terminates list */
        struct fipc *next_fipc;
} FIPC;



/* use this for mutexes */
typedef volatile short IPC_MUTEX_VARIABLE;

/* each queue contains initialization information, and pointers to the
 * head and tail of the queue 
 */
typedef struct {   
#define MAGIC_QUEUE	0xabcdef
	/* an allocated queue gets a magic when debugging */
#ifdef MAGIC_QUEUE
	int magic_queue;
#endif
	/* identity assigned to this queue */
	QUEUE_ID queue_number;
	/* function to indicate message arrival */
	void (*message_arrival)(MESSAGE *pmess);

#ifdef IPC_SOCKET_BASED	
	/* unix domain fd */
	int unix_domain_fd;
	/* TRUE if this socket blocks for input */
	BOOLEAN blocking;
#endif	
	/* list of queue messages, head is front, tail is rear, NULL means empty */
	QUEUED_MESSAGE *head;
	QUEUED_MESSAGE *tail;
	MESSAGE *message_heap;	/* points to array of sizeof(MESSAGE) * number
				 * of message preallocated */
	MESSAGE *message_stack;  /* a stack of messages */
	/* a stack of available QUEUE_MESSAGE nodes, NULL indicates no
	 * more nodes are available.
	 */
	QUEUED_MESSAGE *queue_stack;	
	/* amount of time to block for when reading queue  */
	struct timeval blocking_time;
	/* block for specified time or if NULL block forever.
	 * If not NULL, points to blocking_time
	 */ 
	struct timeval *pblock;	
	/* pending answer blocks on this queue */
	ANSWER_BLOCK *answer_list;	
	/* stack of available answers for this queue */
	ANSWER_BLOCK *answer_stack;
	/* counter for send handles bound to this queue.
  	 * If > 0, can't close queue 
	 * Increment in create_handle, decremented in destroy_handle
 	 * if queue is bound
	 */
	int	num_send_handles;
} IPC_QUEUE;

#ifdef MAGIC_QUEUE
#define TEST_QUEUE(x)	  assert(x->magic_queue == MAGIC_QUEUE) 
#else
#define TEST_QUEUE(x)
#endif


typedef struct {
#ifdef IPC_SOCKET_BASED 
	/* socket fd to use */
	 int fd;
#endif
	/* if it blocks, this is TRUE */
	BOOLEAN blocking;
	/* where to answer messages (may be NULL_QUEUEID) */
	QUEUE_ID sender_queue;
	/* this is only for FIPC answers on the requeuster. 
	 * NULL if no binding */
	IPC_QUEUE *pqueue;
	MESSAGE_NUMBER mess_number;	
	/* this can be used to test the integrity */
#define MAGIC_SEND 0x886644ff
#ifdef MAGIC_SEND
	int magic_send;
#endif
} SEND_HANDLE;


#define Q_LIST void

#if !defined(MAKE_PROTOS) && !defined(__C2MAN__)
#include "ipcprotos.h"
#endif

#ifdef MAGIC_SEND
#define TEST_SEND_HANDLE(x)	(assert(x->magic_send == MAGIC_SEND))
#else
#define TEST_SEND_HANDLE(x)
#endif

/* a static inline function is as fast as a macro
 * This technology is preferable to macros, since the compiler will type check,
 * and should be as efficient as inline code.
 * This allows the actual structures to be opaque to the user 
 */

#ifdef __GNUC__
/* this continues to work with -ansi */
#define INLINE __inline__
#else
#define INLINE
#endif

/* Messages involving FIPC or file desriptor passing have 
 * message numbers
 */
static INLINE MESSAGE_NUMBER get_message_number(MESSAGE *message)
{
	return message->each_message.type & 0xffff;
}

static INLINE void set_message_number(MESSAGE *message, MESSAGE_NUMBER number)
{
	message->each_message.type |= (message->each_message.type & 0xffff0000) | number;
}


/* get a function code, given a message */
static INLINE FUNC_CODE get_function_code(MESSAGE *pmess)
{
	return (pmess->each_message.type >> 16) & 0xff;
}

/* set the function code */
static INLINE void set_function_code(MESSAGE *pmess, FUNC_CODE code)
{
	pmess->each_message.type &= 0xff00ffff;
	pmess->each_message.type |= (code << 16); 

}

static INLINE MESSAGE_TYPE get_message_type(MESSAGE *pmess)
{
	return pmess->each_message.type;	
}

static INLINE void set_message_type(MESSAGE *pmess, MESSAGE_TYPE type)
{
	pmess->each_message.type = type;	
}


/* get a pointer to the data of the current message */
static INLINE void *message_data(MESSAGE *pmess)
{
	return  &(pmess->m_data.message_data);
}

/* get the size of the current message */
static INLINE MESSAGE_SIZE get_message_size(MESSAGE *pmess)
{
	return pmess->each_message.size;
}

/* set the size of the current message */
static INLINE void set_message_size(MESSAGE *pmess, MESSAGE_SIZE size)
{
	pmess->each_message.size = size;
}

/* get the source of the current message.
 * 
 * Warning: this is only useful when reading messages
 */
static INLINE QUEUE_ID get_message_source(MESSAGE *pmess)
{
	return pmess->each_message.source_queue_name;
}

/* set the source of the current message.
 *
 * Warning: this is only useful when writing messages
 */ 
static INLINE void set_message_source(MESSAGE *pmess, QUEUE_ID source)
{
	pmess->each_message.source_queue_name = source;
}

/* Determine whether a message is functinal IPC 
 * Returns: TRUE if it is, FALSE if not
 */
static INLINE BOOLEAN is_message_fipc(MESSAGE *pmess)
{
	return (pmess->each_message.type & FIPC_MESS) ?
		TRUE : FALSE;
}

/* return TRUE if either a final or an interim answer */
static INLINE BOOLEAN is_message_answer(MESSAGE *pmess)
{
	return (pmess->each_message.type & (FIPC_FINAL_ANSWER |
					    FIPC_INTERIM_ANSWER)) ?
			TRUE : FALSE;	
}

/* is this message involving a file descriptor pass.
 * Return TRUE if it is, FALSE if not.
 */
static INLINE BOOLEAN is_message_fd_pass(MESSAGE *pmess)
{
	return (pmess->each_message.type & IPC_FD) ? TRUE : FALSE;

}

/* get the file descriptor associated with the message.
 * 
 * WARNING: is_message_fd_pass() must return TRUE before 
 * 	    this can be useful.
 */
static INLINE int get_message_fd(MESSAGE *pmess)
{
	return pmess->each_message.fd;
}



/* Get the next message on the queue.
 * Return the message or NULL if it doesn't exist
 */
static INLINE MESSAGE *get_next_message(IPC_QUEUE *pqueue)
{
	return ipc_get_message(pqueue, NULL);
}

/* on a previously created send handle, 
 * set the return address.
 * This is nominally the receive queue of the process, but may be
 * anything else, include the NULL_QUEUEID
 */
static INLINE void set_send_answer(SEND_HANDLE *phandle, QUEUE_ID id)
{
	phandle->sender_queue = id;
}

/* test to see if there is anything present in the queue.
 * Return TRUE if there are pending messages, FALSE if there
 * are not.
 */
static INLINE BOOLEAN ipc_queue_status(IPC_QUEUE *pqueue)
{
	if(pqueue->head)
		return TRUE;
	else	return ipc_test_implementation(pqueue);
}

#ifndef __C2MAN__
/* used internally by the library  -- don't generate a man page */
static INLINE MESSAGE_NUMBER get_next_mess_number(SEND_HANDLE *psend)
{
        if(!++(psend->mess_number) )
                psend->mess_number = 1;

        return psend->mess_number;
}
#endif


/* used to get message subtype */
static INLINE int ipc_get_message_subtype(MESSAGE *mess)
{
	return mess->each_message.subtype;
}

/* set an ipc message subtype field */
static INLINE void ipc_set_message_subtype(MESSAGE *mess, int i)
{
	mess->each_message.subtype = i;
}

/* get the id currently being used in a queue.
 * This is normally useful when the systems assigns a QUEUE_ID
 */
static INLINE QUEUE_ID get_queue_id(IPC_QUEUE *pqueue)
{
	return pqueue->queue_number;
}


#ifndef EMBEDDED
static void nonembedded_perror(const char *string)
{
	perror(string);
}
#else

static void nonembedded_perror(const char *string)
{
}
#endif
#endif

#define COMPILED_AT  "$I" "d: " __FILE__ " compiled at " __DATE__ " " __TIME__ " $" 
