/* CATS based Transport Service for RPC ts_cats.c ** ==================================== ** ** This module provides a transport service for the RPC system developed ** by CERN/DD Online group in 1986-90. ** ** This particular module uses the CATS calling sequence to access an ISO-style ** transport provider. ** See the RPC User Manual for details of address syntax. ** ** See also the RPC internals manual, which describes how modules ** similar to this one work, also giving flow charts and message formats. ** ** Outstanding: ** ** - Use proper TS types as per cats.h ** - Async server for unsolicited callback in cats ** ** History: ** Written for DECnet by R.Jones. tested. ** Adapted to CATS by I. Martinez, tested. ** Converted to C by TBL, not tested. ** 30 Apr 90 Code extracted from general ts.c module. ** C Code is UNTESTED at this stage, Pascal code worked. ** ** Compilation switches: ** ** AST System must support asynchronous routines. Mandatory. ** CATS Picks up relevant include file structures. Mandatory. */ #define CATS #define AST /* Module parameters: ** ----------------- ** ** These may be undefined and redefined by syspec.h */ # define WILDCARD '*' /* Wildcard used in addressing */ #include #include "syspec.h" #define TS /* Require visibility of TS interface */ #define TS_INTERNALS /* Require visibility of TS internals */ #include "rpcrts.h" /* Define all general RPC data structures */ #include "rpc_code.h" /* Coding convention macros */ /* ** Definitions for pointers */ #define NIL_MESSAGE (rpc_message *)0 extern char *malloc(); extern void free(); extern char *strncpy(); /* External Routines ** ----------------- */ extern void rpc_new(); /* Allocate rpc_message */ extern void rpc_dispose(); /* Deallocate rpc_message */ extern void rpc_get_string(); /* Get Pascal or C string parameter */ extern void cats_register_errors(); #ifdef AST /* Always used by CATS !!!!!! */ extern void sys_setast(); /* En/Disable ASTs */ rpc_status ts_when_receive(); /* forward (ASTs only) */ #endif /* Module-global variables ** ----------------------- */ int always_zero; BOOLEAN dummy_BOOLEAN; rpc_long global_efn; void panic() { perror(" Please report internal error in TSCATS module.\n"); abort(); } void new_socket(s) socket_type *s; { (*s) = (struct socket_struct *)malloc((unsigned)(sizeof(*(*s)))); } /****************************************************************************** * * Routines to process Return Codes */ void check_error(er_code, status, explanation) rpc_status er_code; rpc_status *status; char *explanation; { *status = er_code; if (!(BOOLEAN)(*status & 1)) { CTRACE(tfp, "RPC/TS/CATS: Error %1d (dec) occured in multiclient handling: %s\n", er_code, explanation); } } /* Check CATS return Code ** ====================== ** ** This one checks against an expected status. It writes the return ** code into a status variable too. */ PRIVATE BOOLEAN cats_error(error_code, status, expected, expected_text) rpc_long error_code; rpc_status *status; rpc_status expected; char *expected_text; { char* errmess; *status = error_code; if (rpc_trace) { switch ((int)*status) { case 138642209: errmess = "Good return: normal: success (TS_OK)";break ; case 138643027: errmess = "Connect request indication"; break ; case 138643011: errmess = "Connect confirm indication"; break ; case 138643067: errmess = "Connection response completed"; break ; case 138644610: errmess = "Disconnect indication"; break ; case 138643019: errmess = "Disconnect confirm"; break ; case 138643043: errmess = "Data received indication"; break ; case 138643035: errmess = "Data send completed"; break ; case 138643059: errmess = "Expedited data indication"; break ; case 138643051: errmess = "Expedited data send completed"; break ; case 138643808: errmess = "Data overrun. User buffer too small";break ; case 138644634: errmess = "Bad connection identifier"; break; case 138644618: errmess = "Procedure called out of sequence"; break ; case 138645412: errmess = "Failed to allocate virtual memory"; break ; default: errmess = NULL; } sys_setast(0); if (errmess) UTRACE(tfp, "RPC/TS/CATS Error: %s, when\n \"%s\" expected.\n", errmess, expected_text); else UTRACE(tfp, "RPC/TS/CATS: Non CATS error %lx hex (%ld decimal)\n", *status, *status); sys_setast(1); } return (*status != expected); } /* Socket handling ** =============== */ #ifdef SUPRESSED /* Make Socket make_socket() ** ----------- */ socket_type make_socket(status) rpc_status *status; { socket_type socket; *status = RPC_S_NORMAL; new_socket(&socket); if (valid_sockets == NULL) { new_socket(&valid_sockets); valid_sockets->soc_next = NULL; } socket->soc_next = valid_sockets->soc_next; valid_sockets->soc_next = socket; return socket; } #endif rpc_status close_connection(); /* forward */ /* Delete Socket delete_socket() ** ------------- */ PRIVATE rpc_status delete_socket(socket) socket_type socket; { rpc_status status; socket_type next_soc; BOOLEAN found; register struct socket_struct *soc = socket; if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: delete_socket called for chan:\n"); sys_setast(1); } if (soc->mdp.soc_cats.soc_master != NULL) { next_soc = soc->mdp.soc_cats.soc_master; found = FALSE; while ((next_soc != NULL) && (!found)) if (next_soc->mdp.soc_cats.soc_slaves == socket) found = TRUE; else next_soc = next_soc->mdp.soc_cats.soc_slaves; if (found) { next_soc->mdp.soc_cats.soc_slaves = socket->mdp.soc_cats.soc_slaves; if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: dispose of slave socket:\n"); sys_setast(1); } if (soc->mdp.soc_cats.soc_message != NULL) rpc_dispose(soc->mdp.soc_cats.soc_message); free(socket); } else check_error(RPC_S_NOSUCHSOCKET, &status, "delete_socket call with bad owned socket"); } else { while ((soc->mdp.soc_cats.soc_slaves != NULL) && (status == RPC_S_NORMAL)) status = close_connection(soc->mdp.soc_cats.soc_slaves); if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: dispose of slave:%.40s\n", soc->mdp.soc_cats.soc_tsuf.body); sys_setast(1); } if (soc->mdp.soc_cats.soc_message != NULL) rpc_dispose(soc->mdp.soc_cats.soc_message); free(socket); } return status; } void cats_read_chan(); /* forward */ /* Search Channel List search_chan_list() ** ------------------- * * This routine searches the list of attached slave sockets to find * one with a message waiting. If it fibnds it, it swaps it with the * 'message' passed to it. * */ BOOLEAN search_chan_list(socket, message, status) socket_type *socket; rpc_message_pointer *message; rpc_status *status; { BOOLEAN found; socket_type next_owned_chan; rpc_message_pointer temp_ptr; *status = RPC_S_NORMAL; found = FALSE; next_owned_chan = (*socket)->mdp.soc_cats.soc_slaves; while ((next_owned_chan != NULL) && (!found)) if (next_owned_chan->mdp.soc_cats.soc_holding_message) found = TRUE; else next_owned_chan = next_owned_chan->mdp.soc_cats.soc_slaves; if (found) { temp_ptr = (*message); (*message) = next_owned_chan->mdp.soc_cats.soc_message; next_owned_chan->mdp.soc_cats.soc_message = temp_ptr; next_owned_chan->mdp.soc_cats.soc_message->m_socket = next_owned_chan; next_owned_chan->mdp.soc_cats.soc_holding_message = FALSE; cats_read_chan(&next_owned_chan, status); } return found; } /* Display Event Flags display_efn() ** * */ PRIVATE void display_efn(efn) rpc_long efn; { /* This was only put in as a trace option */ } /* Close a Socket close_connection() ** -------------- ** ** On entry, ** soc may be a master or slave socket. ** ** On exit, ** Any connection associated with the socket is disconnected. ** Any event flag is released. ** ** Why no recursion to slave sockets? - TBL */ #ifdef __STDC__ PRIVATE rpc_status close_connection(socket_type soc) #else PRIVATE rpc_status close_connection(soc) socket_type soc; #endif { rpc_status status; if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: close_connection called for conn :%1d\n", soc->mdp.soc_cats.soc_conn_id); UTRACE(tfp, " Freeing EF :%1d\n", soc->soc_efn); sys_setast(1); } if (soc->mdp.soc_cats.soc_role == master) (void)lib_free_ef(&soc->soc_efn); (void)cats_error(tsdsrw(&soc->mdp.soc_cats.soc_conn_id, &soc->mdp.soc_cats.soc_systat), &status, soc->mdp.soc_cats.soc_connected ? ts_dscf : ts_dsid, "disconnect confirm completion"); soc->mdp.soc_cats.soc_connected = FALSE; return status; } /******************************************************************************* * * Completion Routine for Asynchrounous Reads on slave socket */ void cats_read_completion(ci, st, event, le, sy, socket) rpc_long *ci; rpc_long *st; rpc_long *event; TS_length *le; TS_systat *sy; socket_type *socket; { rpc_status status; rpc_message_pointer full_msg; { register struct socket_struct *soc = (*socket); if (rpc_trace) { UTRACE(tfp, "CATS message received on chan :%1d completion status :%1d\n", (*ci), (*st)); hex_message(soc->mdp.soc_cats.soc_message, (*le)); } if (((*st) == ts_dtaid) || (*st == ts_dtaovr)) { /* soc->mdp.soc_cats.soc_async_read_queued = FALSE; */ if (soc->mdp.soc_cats.soc_master-> mdp.soc_cats.soc_async_read_queued) { register socket_type own = soc->mdp.soc_cats.soc_master; full_msg = soc->mdp.soc_cats.soc_message; soc->mdp.soc_cats.soc_message = soc->mdp.soc_cats.soc_master->mdp.soc_cats.soc_message; soc->mdp.soc_cats.soc_message->m_socket = (*socket); own->mdp.soc_cats.soc_message = NULL; own->mdp.soc_cats.soc_async_read_queued = no_read; /* Call user AST: */ (*(soc->mdp.soc_cats.soc_master->soc_user_ast))(full_msg); cats_read_chan(socket, &status); /* Requeue the read */ } else { soc->mdp.soc_cats.soc_holding_message = TRUE; soc->mdp.soc_cats.soc_master->mdp.soc_cats.soc_retlen = (*le); CTRACE(tfp, "set event flag %1d\n", soc->mdp.soc_cats.soc_master->soc_efn); check_error(sys_setef( soc->mdp.soc_cats.soc_master->soc_efn), &status, "from cats set event flag"); } } else if ((*st) == ts_dsid) { soc->mdp.soc_cats.soc_connected = FALSE; status = close_connection(soc); delete_socket(&status, *socket); } else if (rpc_trace) { UTRACE(tfp, "RPC/TS/CATS: Unexpected event on conn:%1d status:%1d length:%1d soc_systat[1]:%1d soc_systat[2]:%1d\n", (*ci), (*st), (*le), (*sy)[0], (*sy)[1]); panic(); } } } /******************************************************************************* * * Procedure: Queue an asynchronous read on a socket. * * If there is no message, one is allocated. */ void cats_read_chan(socket, status) socket_type *socket; rpc_status *status; { TS_length buffer_length; { register struct socket_struct *soc = (*socket); if (soc->mdp.soc_cats.soc_holding_message) panic(); else { if (soc->mdp.soc_cats.soc_message == NULL) rpc_new(&soc->mdp.soc_cats.soc_message, RPC_BUFFER_SIZE); soc->mdp.soc_cats.soc_message->m_socket = (*socket); if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: queue read on chan:%1d\n", soc->mdp.soc_cats.soc_conn_id); sys_setast(1); } buffer_length = RPC_BUFFER_SIZE; if (soc->mdp.soc_cats.soc_connected) (void)cats_error( tsrcv( &soc->mdp.soc_cats.soc_conn_id, &soc->mdp.soc_cats.soc_message->body.rpc_b, &buffer_length, &soc->mdp.soc_cats.soc_systat, cats_read_completion, &soc->mdp.soc_cats.soc_self), status, ts_ok, " TSRCV initiation: read queued completed"); else { if (rpc_trace) { sys_setast(0); UTRACE(tfp, "RPC/TS/CATS: Error, channel is not active. Releasing ...\n"); sys_setast(1); } status = close_connection(*socket); delete_socket(status, *socket); } } } } /******************************************************************************* * * Completion routine for TSCRS - Handle Connection Confimation */ void accept_ast(conn_id, status, event, len, systat, master_socket) rpc_long *conn_id; rpc_long *status; rpc_long *event; TS_length *len; TS_systat *systat; socket_type *master_socket; { rpc_message_pointer full_msg; socket_type new_soc; rpc_status my_status; if (*status & 1) { new_socket(&new_soc); { register struct socket_struct *soc = new_soc; *new_soc = **master_socket; soc->mdp.soc_cats.soc_master = (*master_socket); soc->mdp.soc_cats.soc_conn_id = (*conn_id); soc->mdp.soc_cats.soc_self = new_soc; soc->mdp.soc_cats.soc_role = slave; soc->soc_next = NULL; soc->soc_user_ast = 0; soc->soc_efn = 0; soc->mdp.soc_cats.soc_slaves = NULL; soc->mdp.soc_cats.soc_holding_message = FALSE; soc->mdp.soc_cats.soc_message = NULL; soc->mdp.soc_cats.soc_connected = FALSE; soc->mdp.soc_cats.soc_connected = TRUE; soc->mdp.soc_cats.soc_slaves = (*master_socket)->mdp.soc_cats.soc_slaves; (*master_socket)->mdp.soc_cats.soc_slaves = new_soc; if (rpc_trace) { UTRACE(tfp, "TSCATS: New cats connection:\n"); UTRACE(tfp, " soc_conn_id: %1d\n", new_soc->mdp.soc_cats.soc_conn_id); } cats_read_chan(&new_soc, &my_status); } } else if (rpc_trace) { UTRACE(tfp, "TSCATS ACCEPT_AST: accept failed. \n"); UTRACE(tfp, " conn_id: %1d\n", (*conn_id)); } } /******************************************************************************* * * Completion routine for TSLST: Handle an incoming connection */ void listen_ast(ci, st, event, length, systat, master_socket) TS_id *ci; TS_status *st; TS_status *event; TS_length *length; TS_systat *systat; socket_type *master_socket; { rpc_status status; (*master_socket)->mdp.soc_cats.soc_conn_id = *ci; CTRACE(tfp, "CATS- request on master conn: %1d connection status: %1d\n", (*master_socket)->mdp.soc_cats.soc_conn_id, (*st)); if ((*event) == ts_cnid) (void)cats_error(tscrs(&(*ci), &(*master_socket)->mdp.soc_cats.soc_systat, accept_ast, &(*master_socket)), &status, ts_cnrsok, "connection response initiation"); } /******************************************************************************* * * LISTEN: Establish a TSAP and a connection request handler */ void listen_master(socket, status) socket_type *socket; rpc_status *status; { register struct socket_struct *soc = (*socket); (void)cats_error( tslst(soc->mdp.soc_cats.soc_tsuf.body, &soc->mdp.soc_cats.soc_tsuf.length, &soc->mdp.soc_cats.soc_systat, listen_ast, &soc->mdp.soc_cats.soc_self), status, ts_ok, "Initiation of listen (tslst) on master socket"); } /******************************************************************************* * * Append C string to rpc_name */ void append_string_to_name(dest, source) rpc_name dest; char * source; { register int i, j; for(i=RPC_NAME_LENGTH; i && (dest[i-1]==' '); i--); /* Find last non-blank*/ for (j=0; (imdp.soc_cats.soc_tsuf.body, RPC_NAME_LENGTH); for (i = socket->mdp.soc_cats.soc_tsuf.length; isoc_medium].name); *status = RPC_S_NORMAL; } void /******************************************************************************* * * Send a message to partner task */ PRIVATE rpc_status cats_write(mes) rpc_message_pointer mes; /******************************************************************************* * * Dummy completion routine */ void dummy_ast(ci, st, event, le, sy, socket) rpc_long *ci; rpc_long *st; rpc_long *event; TS_length *le; TS_systat *sy; socket_type *socket; { } /******************************************************************************* * * Receive a message on a given socket */ void rpc_cats_get_message(status, socket, message, rx_size, timeout) rpc_status *status; socket_type *socket; rpc_message_pointer *message; TS_length rx_size; TS_timeout timeout; { rpc_long conn_xx; rpc_long event_mask, system_mask; rpc_status event; event_mask = 16; system_mask = 0; if (rpc_trace) { sys_setast(0); UTRACE(tfp, "Waiting for cats message...\n"); FLUSH_TRACE sys_setast(1); } { register struct socket_struct *soc = (*socket); register rpc_message *mes = (*message); if (soc->mdp.soc_cats.soc_role != master) { (void)cats_error( tsrcvw(&soc->mdp.soc_cats.soc_conn_id, &(*message)->body.rpc_b, &rx_size, &soc->mdp.soc_cats.soc_retlen, &soc->mdp.soc_cats.soc_systat), status, ts_dtaid, "Synchronous read TSrcvW completion"); if (soc->mdp.soc_cats.soc_role == transient) status = close_connection(*socket); } else /* master */ if (soc->mdp.soc_cats.soc_async_read_queued) check_error(RPC_S_ALREADY_READING, status, "cats read already queued"); else { check_error(sys_clref(soc->soc_efn), status, "cats clear event flag"); if (!search_chan_list(socket, message, status)) { if (rpc_trace) { sys_setast(0); UTRACE(tfp, "Cleared, & now waiting on event flag %1d by server %.40s\n", soc->soc_efn, soc->mdp.soc_cats.soc_tsuf.body); sys_setast(1); } check_error(sys_waitfr(soc->soc_efn), status, "cats wait on event flag"); if (!search_chan_list(socket, message, status)) if (rpc_trace) { sys_setast(0); UTRACE(tfp, "cats flag set but no msg received\n"); sys_setast(1); } /* soc->mdp.soc_cats.soc_reading = no_read; old */ } } if (rpc_trace) { sys_setast(0); UTRACE(tfp, "cats message received"); hex_message((*message), soc->mdp.soc_cats.soc_retlen); sys_setast(1); } } } /******************************************************************************* * * Completion routine for TSRCV: Handle incoming message (simple socket) */ void rpc_cats_ast_service(ci, st, event, le, sy, socket) TS_id *ci; TS_status *st; TS_status *event; TS_length *le; TS_systat *sy; socket_type *socket; { register rpc_message_pointer pmessage; pmessage = (*socket)->mdp.soc_cats.soc_message; if (!(BOOLEAN)(((*st)) & 1)) CTRACE(tfp, "RPC/TS/CATS: Bad async I/O :%10d (dec)\n", (*st)); else { if (rpc_trace) { UTRACE(tfp, "Asynchonous message received\n"); hex_message(pmessage, (*le)); } (*(pmessage->m_socket->soc_user_ast))(pmessage); } } /* Parse address string and setup socket (CATS) ** ------------------------------------- ** ** On entry, ** socket is already valid ** s is the entire service name */ rpc_status ts_open_CATS(socket, tsap) socket_type socket; char * tsap; { int i, l; register int j; rpc_status status; rpc_long st; int ind, tl; device_name_type d; char * sys_dollar_net = "SYS$NET."; { register struct socket_struct *soc = socket; soc->soc_efn = 0; /* Determine socket role: ** ** On omitted TSAP, or 'SYS$NET' means the process is a passive one started ** by a daemon. */ i = 1; switch ((int)(tsap[0])) { case 0 : soc->mdp.soc_cats.soc_role = passive; break; case '*': soc->mdp.soc_cats.soc_role = master; i++; break ; case '#': soc->mdp.soc_cats.soc_role = transient; i++; break ; default: soc->mdp.soc_cats.soc_role = passive; for (j = 0; j<7 ; j++) if (tsap[j] != sys_dollar_net[j]) soc->mdp.soc_cats.soc_role = single; } /*end switch*/ /* Extract Transport Suffix (object name): */ if (strlen(tsap)>device_length) status = RPC_SYNTAX_4; /*?*/ else { struct device_struct *tsf = &socket->mdp.soc_cats.soc_tsuf; strcpy(tsf->body, tsap); tsf->length = strlen(tsap); } /* Perform the remaining initialisation: */ if (GOOD(status)) { TS_length tsapl; TS_opts zero = 0; soc->mdp.soc_cats.soc_connected = TRUE; soc->mdp.soc_cats.soc_self = socket; soc->mdp.soc_cats.soc_master = NULL; soc->mdp.soc_cats.soc_slaves = NULL; tsapl = soc->mdp.soc_cats.soc_tsuf.length; switch ((int)(soc->mdp.soc_cats.soc_role)) { case passive: status = RPC_S_NOT_IMPLEMENTED; break ; case single: (void)cats_error( tscnrw(&soc->mdp.soc_cats.soc_conn_id, soc->mdp.soc_cats.soc_tsuf.body, &soc->mdp.soc_cats.soc_tsuf.length, &zero, &soc->mdp.soc_cats.soc_systat), &status, ts_cncf, "Connect confirm completion"); break ; case transient: soc->mdp.soc_cats.soc_connected = FALSE; break ; case master: (void) lib_get_ef(&soc->soc_efn); soc->mdp.soc_cats.soc_async_read_queued = FALSE; listen_master(&socket, &status); if (rpc_trace) { sys_setast(0); UTRACE(tfp, "new cats server initialised:\n"); UTRACE(tfp, " device name: %.40s\n", soc->mdp.soc_cats.soc_tsuf.body); UTRACE(tfp, " connection: %1d\n", soc->mdp.soc_cats.soc_conn_id); UTRACE(tfp, " lib_GET_EF : %1d\n", soc->soc_efn); sys_setast(1); } break ; } /*switch */ if (GOOD(status)) soc->mdp.soc_cats.soc_connected = FALSE; } /* if parse ok */ } /* with soc */ return status; } /* Return RPC address of this socket cats_my_address() ** --------------------------------- ** ** This routine returns a string indicating the RPC address of the socket. ** ** On entry, ** soc should be a valid socket pointer ** addrstr points to a buffer to contain a string. ** addrlen indicates the length of the string buffer ** ** On exit, ** *addrstr has the contact address for an external node wishing to contact ** this socket written into it, zero terminated, without the ** protocol on the end. */ #ifdef __STDC__ PRIVATE rpc_status cats_my_address( socket_type soc, chr * addrstr, int addrlen) #else PRIVATE rpc_status cats_my_address(soc, addrstr, addrlen) socket_type soc; char * addrstr; int addrlen; #endif { @@@ } /* Return RPC address of peer cats_peer_address() ** -------------------------- ** ** On entry, ** soc should be a valid socket pointer ** address points to a buffer to contain an rpc_name. ** ** On exit, ** *address has the contact address for the peer node. ** blank filled. ** 26 useful characters in the address. */ #ifdef __STDC__ PRIVATE rpc_status cats_peer_address( socket_type soc, char * addrstr, int addrlen) #else PRIVATE rpc_status cats_peer_address(soc, addrstr, addrlen) socket_type soc; char * addrstr; int addrlen; #endif { @@@ } /* Send a message over the given socket cats_write() ** ------------------------------------ ** ** On entry, ** pmessage points to a pointer to the message, with fields: ** m_next (undefined) ** m_socket valid ** m_index Length of message to be sent in bytes, ** exclusing any bytes needed for the protocol. ** m_status (undefined) ** ** On exit, ** ** The pointer to the message may have been changed. In this case, ** it will point to a message with the m_socket and m_status fields ** set, but without the data. This routine is therefore DESTRUCTIVE. ** ** returns: Status of operation. */ #ifdef __STDC__ PRIVATE rpc_status cats_write(rpc_message_pointer *ppmessage) #else PRIVATE rpc_status cats_write(ppmessage) rpc_message_pointer *ppmessage; #endif { rpc_status status; TS_length tsapl; register rpc_message * mes = *ppmessage; TS_opts zero = 0; TS_length tx_size = (TS_length)mes->m_index; register socket_type soc = mes->m_socket; status = check_socket(soc); if (BAD(status)) return status; if (!soc->mdp.soc_cats.soc_connected) soc->mdp.soc_cats.soc_connected = (BOOLEAN) (!cats_error( tscnrw(&soc->mdp.soc_cats.soc_conn_id, soc->mdp.soc_cats.soc_tsuf.body, &soc->mdp.soc_cats.soc_tsuf.length, &zero, &soc->mdp.soc_cats.soc_systat), &status, ts_cncf, "Connect confirm for fast client")); if (soc->mdp.soc_cats.soc_connected) { if (rpc_trace) { sys_setast(0); UTRACE(tfp, "Cats message to be txd (soc_conn_id = %1d)\n", soc->mdp.soc_cats.soc_conn_id); sys_setast(1); hex_message(mes, m_index); } (void)cats_error( tssndw( &soc->mdp.soc_cats.soc_conn_id, &mes->body, &tx_size, &soc->mdp.soc_cats.soc_systat), &status, ts_rdyid, "TSSNDW completion"); if (rpc_trace) { sys_setast(0); UTRACE(tfp, "Cats message transmitted.\n"); sys_setast(1); } } else if (rpc_trace) { sys_setast(0); UTRACE(tfp, "CATS- Unable to connect to remote before sending in conn:%1d\n", soc->mdp.soc_cats.soc_conn_id); sys_setast(1); } mes->m_status = status; return status; } /* Receive message cats_read() ** --------------- ** ** This procedure waits for a message to arrive on a socket and returns the ** message. A suitable message buffer is passed to the routine, but a ** different one may be returned. If the socket passed is the master socket ** for mutiple connections, then the message recieved will carry the socket ** number of the slave socket on which the message actually arrived. ** ** On entry: ** ppmessage is pointer to pointer to buffer, fields: ** m_socket The socket to be used - possibly a master socket ** m_index don't care ** m_status don't care ** m_next don't care ** ** timeout -1 for infinite, 0 for poll, else in units of 10ms ** ** On exit, ** returns The status of the operation. ** *pmessage may have been modified to point to another message ** from the pool. Fields: ** m_socket The (possible slave) socket the msg came on. ** m_status The status of the operation (= return value). ** m_index The number of bytes recieved if good status ** m_next junk. ** */ #ifdef __STDC__ PRIVATE rpc_status cats_read( rpc_message_pointer *ppmessage, int timeout) #else PRIVATE rpc_status cats_read(ppmessage, timeout) rpc_message_pointer *ppmessage; int timeout; #endif { @@@ return status; } /* Asynchronous Receive ** -------------------- ** ** On entry, ** pmessage is the address of a suitable message buffer ** action is the address of the routine to be called ** user_1 is not used at the moment. ** ** On exit, ** the parameters are stored only. Exits immediately. ** ** On arrival of the next message: ** The routine *action() is called, and passed the message as a parameter. ** If the buffer *pmessage is not used, it will be reused elsewhere and ** a different buffer passed to *action(). */ #ifdef __STDC__ PRIVATE rpc_status cats_aread( rpc_message_pointer pmessage, rpc_pointer action, rpc_integer user_1) #else PRIVATE rpc_status cats_aread(pmessage, action, user_1) rpc_message_pointer pmessage; rpc_pointer action; rpc_integer user_1; #endif { rpc_status status; socket_type socket = pmessage->m_socket; TS_length rx_size = RPC_BUFFER_SIZE; socket->soc_user_ast = action; { register struct socket_struct *soc = socket; if (soc->mdp.soc_cats.soc_master != NULL) socket = soc->mdp.soc_cats.soc_master; if (socket->mdp.soc_cats.soc_role != master) { soc->mdp.soc_cats.soc_message = pmessage; (void)cats_error( tsrcv(&soc->mdp.soc_cats.soc_conn_id, &pmessage->body.rpc_b, &rx_size, &soc->mdp.soc_cats.soc_systat, rpc_cats_ast_service, &socket), &status, ts_ok, "Asynch receive initiated (queued)"); } else { if (socket->mdp.soc_cats.soc_async_read_queued) status = RPC_S_ALREADY_READING; else if (search_chan_list(&socket, &pmessage, &status)) (*(socket->soc_user_ast))(pmessage); else { socket->mdp.soc_cats.soc_async_read_queued = TRUE; socket->mdp.soc_cats.soc_message = pmessage; } } } return status; } /* cats_aread */ /* Open a transport connection cats_open() ** --------------------------- ** ** On entry, ** soc points to a recently created socket, fields as follows: ** ** soc_last_call_sent NULL ** soc_last_reply_sent NULL ** soc_next-call_tid 0 ** soc_protocol valid pointer to protocol structure ** ** and otherwise uninitialised. ** ** tsap points to a zero-terminated string representing the address ** (with no .cats on it) */ #ifdef __STDC__ PRIVATE rpc_status cats_open(socket_type soc, char * tsap) #else PRIVATE rpc_status cats_open(soc, tsap) socket_type soc; char *tsap; #endif { @@@ } /* Close a Transport Connection cats_close() ** ---------------------------- ** ** On entry, ** soc Must be a socket opened by cats_open(). ** ** On exit, ** returns a status of the close operation. */ #ifdef __STDC__ PRIVATE rpc_status cats_close(socket_type soc) #else PRIVATE rpc_status cats_close(soc) socket_type soc; #endif { return delete_socket(soc); } /* cats_close */ /* Data structure defining this medium: ** ----------------------------------- */ PRIVATE rpc_protocol cats_protocol = { 0, /* Link (not yet used) */ "cats", /* Name of medium used in address strings */ 0, /* No, not reliable */ cats_open, cats_close, cats_write, cats_read, cats_aread, cats_my_address, cats_peer_address, }; /* Register this protocol with the RPC system ** ------------------------------------------ */ #ifdef __STDC__ rpc_status use_cats(void) #else rpc_status use_cats() #endif { valid_sockets = NULL; always_zero = 0; cats_register_errors(); rpc_use(cats_protocol); }