+// vim: noet sw=4 ts=4:
+/*
+==================================================================================
+ Copyright (c) 2020 Nokia
+ Copyright (c) 2020 AT&T Intellectual Property.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================================
+*/
+
+/* X
+*****************************************************************************
+*
+* Mnemonic: SIrcv
+* Abstract: This routine allows the user program to receive data on a
+* session without using the callback structure of the library.
+* It is the caller's responsibility to provide a buffer large
+* enough to handle the received data.
+* Parms: gptr - The SIHANDLE that the user received on init call
+* sid - The session id that the user wants to check
+* buf - Pointer to buffer to receive data in
+* abuf - Pointer to buffer to return address of UDP sender in (!null)
+* buflen-Length of the receive buffer
+* delay- Value to pass to poll (time out) -1 == block until data
+* Returns: SI_ERROR - (SIerrno will contain reason) if failure, else the
+* number of bytes read. If the number read is 0 SIerrno will indicate
+* why: time out exceeded, signal received.
+* Date: 26 March 1995
+* Author: E. Scott Daniels
+* Mods: 26 Mar 20001 - Changed to support UDP reads
+*
+******************************************************************************
+*/
+#include "sisetup.h" // get start up stuff
+#include "sitransport.h"
+
+extern int SIrcv( struct ginfo_blk *gptr, int sid, char *buf, int buflen, char *abuf, int delay ) {
+ //extern int sigflags; // signal flags
+ int status = SI_ERROR; // assume the worst to return to caller
+ struct tp_blk *tpptr; // pointer to transport provider info
+ int flags = 0; // receive flags
+ int remainder; // # of bytes remaining after rcv if more
+ fd_set readfds; // special set of read fds for this call
+ fd_set execpfds; // special set of read fds for this call
+ struct timeval *tptr = NULL; // time info for select call
+ struct timeval time;
+ struct sockaddr *uaddr; // pointer to udp address
+ char *acbuf; // pointer to converted address
+ int addrlen;
+
+ gptr->sierr = SI_ERR_HANDLE; // set errno before we fail
+ if( gptr->magicnum != MAGICNUM ) // if not a valid ginfo block
+ return SI_ERROR;
+
+ gptr->sierr = SI_ERR_SESSID; // set errno before we fail
+ for( tpptr = gptr->tplist; tpptr != NULL && tpptr->fd != sid;
+ tpptr = tpptr->next ); // find transport block
+ if( tpptr == NULL )
+ return SI_ERROR; // signal bad block
+
+ uaddr = (struct sockaddr *) malloc( sizeof( struct sockaddr ) );
+ addrlen = sizeof( *uaddr );
+
+ gptr->sierr = SI_ERR_SHUTD; // set errno before we fail
+ if( ! (gptr->flags & GIF_SHUTDOWN) )
+ { // if not in shutdown and no signal flags
+ FD_ZERO( &readfds ); // clear select info
+ FD_SET( tpptr->fd, &readfds ); // set to check read status
+
+ FD_ZERO( &execpfds ); // clear select info
+ FD_SET( tpptr->fd, &execpfds ); // set to check read status
+
+ if( delay >= 0 ) // user asked for a fininte time limit
+ {
+ tptr = &time; // point at the local struct
+ tptr->tv_sec = 0; // setup time for select call
+ tptr->tv_usec = delay;
+ }
+
+ gptr->sierr = SI_ERR_TP;
+ if( (select( tpptr->fd + 1, &readfds, NULL, &execpfds, tptr ) < 0 ) )
+ gptr->flags |= GIF_SHUTDOWN; // we must shut on error or signal
+ else
+ { // poll was successful - see if data ?
+ gptr->sierr = SI_ERR_TIMEOUT;
+ if( FD_ISSET( tpptr->fd, &execpfds ) ) // session error?
+ {
+ SIterm( gptr, tpptr ); // clean up our end of things
+ gptr->sierr = SI_ERR_SESSID; // set errno before we fail
+ }
+ else
+ {
+ if( (FD_ISSET( tpptr->fd, &readfds )) )
+ { // process data if no signal
+ gptr->sierr = SI_ERR_TP;
+ if( tpptr->type == SOCK_DGRAM ) // raw data received
+ {
+ status = RECVFROM( sid, buf, buflen, 0, uaddr, &addrlen );
+ if( abuf )
+ {
+ SIaddress( uaddr, (void **) &acbuf, AC_TODOT ); // address returns pointer to buf now rather than filling
+ strcpy( abuf, acbuf ); // must be back compat with old versions
+ free( acbuf );
+ }
+ if( status < 0 ) // session terminated?
+ SIterm( gptr, tpptr ); // so close our end
+ }
+ else // cooked data received
+ {
+ status = RECV( sid, buf, buflen, 0 ); // read data into user buf
+ if( status < 0 ) // session terminated?
+ SIterm( gptr, tpptr ); // so close our end
+ }
+ } // end event was received
+ else // no event was received
+ status = 0; // status is just ok
+ } // end else - not in shutdown mode after poll
+ } // end else pole was successful
+ } // end if not already signal shutdown
+
+ if( gptr->flags & GIF_SHUTDOWN && gptr->tplist != NULL )
+ { // shutdown received but sessions not cleaned up
+ SIshutdown( gptr );
+ status = SI_ERROR; // indicate failure on return
+ } // end if shut but not clean
+
+ free( uaddr );
+ return status; // send back the status
+} // SIrcv