Beef up unit tests for SI95 code
[ric-plt/lib/rmr.git] / src / rmr / si / src / si95 / siterm.c
index 0b74ba0..ce48748 100644 (file)
 
 /*
 **************************************************************************
-*  Mnemonic: SIterm
-*  Abstract: This routine will terminate a session based on the tp_blk
-*            that is passed into the routine. The transport session block
-*            is released and removed from the ginfo list. The session is
-*            terminated by issuing a t_unbind call (if the unbind flag is
-*            on in the tpptr block), and then issuing a t_close.
-*  Parms:    gptr - Pointer to the global information block
-*            tpptr - Pointer to tp block that defines the open fd.
-*  Returns:  Nothing.
-*  Date:     18 January 1995
-*  Author:   E. Scott Daniels
+*  Mnemonic:   SIterm
+*  Abstract:   Manage the transport provider block information relating to
+*                              the need to terminate the session. The block is left in the
+*                              list; it is unsafe to clean the lsit up outside of the SIwait
+*                              thread.  When safe, the SIrm_tpb() function can be called to
+*                              do the rest of the work that was originally done by SIterm.
+*
+*  Date:       18 January 1995
+*  Author:             E. Scott Daniels
 *
 **************************************************************************
 */
-#include "sisetup.h"     //  get the setup stuff 
+#include "sisetup.h"     //  get the setup stuff
 #include "sitransport.h"
 
+
+/*
+       Abort the connection in such a way that there is no resulting time-wait state.
+       This should be used cautiously but is needed for situations like when the Linux
+       connect() system call manages to connect us to ourselves through the even number
+       port bug.
+
+       This needs a real file desc as there may not yet be a transport block when
+       the connection may need to be aborted. For this reason, the function name is
+       lower case indicating that user programmes are discouraged from using this
+       function directly.
+*/
+extern void siabort_conn( int fd ) {
+       struct linger   opt_val;                // value passed as option to set call
+
+       opt_val.l_onoff = 1;                    // MUST set linger on with a zero len timeout
+       opt_val.l_linger = 0;
+
+       setsockopt( fd, SOL_SOCKET, SO_LINGER, &opt_val, sizeof( opt_val ) );           // disable linger to prevent time-wait
+       CLOSE( fd );                    // close will now abort and not result in time-wait (do NOT use shutdown() first!)
+}
+
+/*
+       Close the FD and mark the transport block as unusable/closed.
+       Removal of the block from the list is safe only from the siwait
+       thread.  If the abort flag is set in the transport block, then the
+       connection is aborted (reset).
+*/
 extern void SIterm( struct ginfo_blk* gptr, struct tp_blk *tpptr ) {
 
        if( tpptr != NULL ) {
                if( tpptr->fd >= 0 ) {
-                       CLOSE( tpptr->fd );    
+                       if( tpptr->flags & TPF_ABORT ) {
+                               siabort_conn( tpptr->fd );
+                       } else {
+                               CLOSE( tpptr->fd );
+                       }
+
                        if( tpptr->fd < MAX_FDS ) {
                                gptr->tp_map[tpptr->fd] = NULL;         // drop reference
                        }
                }
 
-               if( tpptr->prev != NULL ) {            //  remove from the list 
-                       tpptr->prev->next = tpptr->next;    //  point previous at the next 
-               } else {
-                       gptr->tplist = tpptr->next;        //  this was head, make next new head 
-               }
+               tpptr->fd = -1;                                                         // prevent future sends etc.
+               tpptr->flags |= TPF_DELETE;                                     // signal block deletion needed when safe
+       }
+}
 
-               if( tpptr->next != NULL ) {
-                       tpptr->next->prev = tpptr->prev;  //  point next one back behind this one 
+/*
+       It is safe to remove the block from the list; if it was in the list
+       in the first place.
+*/
+extern void SIrm_tpb( struct ginfo_blk *gptr, struct tp_blk *tpptr ) {
+
+       if( tpptr != NULL ) {
+               if( tpptr->prev != NULL || tpptr->next != NULL ) {      // in the list
+                       if( tpptr->prev != NULL ) {            //  remove from the list
+                               tpptr->prev->next = tpptr->next;    //  point previous at the next
+                       } else {
+                               gptr->tplist = tpptr->next;        //  this was head, make next new head
+                       }
+
+                       if( tpptr->next != NULL ) {
+                               tpptr->next->prev = tpptr->prev;  //  point next one back behind this one
+                       }
                }
 
-               free( tpptr->addr );             //  release the address bufers 
+               free( tpptr->addr );             //  release the address bufers
                free( tpptr->paddr );
-               free( tpptr );                   //  and release the block 
+               free( tpptr );                   //  and release the block
        }
 }