/*************************************************************************

  Copyright (c) 1997 Novell, Inc.  All Rights Reserved.

  With respect to this file, Novell hereby grants to Developer a 
  royalty-free, non-exclusive license to include this sample code 
  and derivative binaries in its product. Novell grants to Developer 
  worldwide distribution rights to market, distribute or sell this 
  sample code file and derivative binaries as a component of 
  Developer's product(s).  Novell shall have no obligations to 
  Developer or Developer's customers with respect to this code.
  
  DISCLAIMER:
  
  Novell disclaims and excludes any and all express, implied, and 
  statutory warranties, including, without limitation, warranties 
  of good title, warranties against infringement, and the implied 
  warranties of merchantibility and fitness for a particular purpose.  
  Novell does not warrant that the software will satisfy customer's 
  requirements or that the licensed works are without defect or error 
  or that the operation of the software will be uninterrupted.  
  Novell makes no warranties respecting any technical services or 
  support tools provided under the agreement, and disclaims all other 
  warranties, including the implied warranties of merchantability and 
  fitness for a particular purpose. */

/**************************************************************************
   SPXS_MUL.C
***************************************************************************

  NetWare Example TLI server on SPX, uses SAP to advertise.

  SPXS_MUL.C works in conjunction with either SPXC_TLI.C or SPXC_RW.C to 
  demonstrate SPX TLI functions.  This example, representing the server 
  portion of the demonstration, accepts a number of connections at a time.
  This example creates a new thread and sends the contents of LOGFILE to 
  each accepted client.  On exit or unload, a cleanup function is called to
  free all the connections.

  Complete the following steps to run this example:

  1. Make sure there's a file available for the server application
     (SPXS_MUL.NLM) to open and transmit (SERVER\SYS:README.TXT).  This 
     file should contain a test message.

  2. Load SPXS_MUL.NLM on a test server using the server's name and the 
     number of allowable connections as a command-line parameters 
     (ex. LOAD A:SPXS_MUL MY_TEST_SERVER 4).  The program will respond with
     a message similar to the following: "SERVER:  my_test_server is ready ... "  

  3. Load a client example (either SPXC_RW.NLM or SPXC_TLI.NLM) on another 
     test server, using the previous server's name as a command-line 
     parameter (ex. LOAD A:SPXC_RW.NLM MY_TEST_SERVER).  This client example 
     will exit after displaying the contents of README.TXT transmitted from 
     the other server by SPXS_MUL.NLM

  4. Unload SPXS_MUL.NLM, the client examples SPXC_RW.NLM and SPXC_RW.NLM 
     unload automatically.

  NOTES:
 
  1.  QMK386.EXE can be used to generate a Watcom makefile for this 
      example:     QMK386 SPXS_MUL

  2.  CALNLM32.NLM, CLXNLM32.NLM, TLI.NLM and SPXS.NLM must be running on 
      the server for this example to work properly.

*************************************************************************/

#include <stdio.h> 
#include <stdlib.h>     
#include <string.h>     
#include <nwcalls.h>
#include <nwclxcon.h>
#include <nwthread.h>    
#include <nwstring.h>
#include <fcntl.h>    
#include <nlm\tiuser.h>    
#include <nwconn.h>     
#include <nlm\nwsap.h>      
#include <nlm\poll.h>  

/* Global defines */
#define MAX_CIND 10           /* Max connection indication  */
#define TLI_TYPE 0x9000       /* Server type (dynamic area) */
#define LOGFILE  "README.TXT" /* File name                  */

/* Global variables */
int    conn_ind;                 /* # connection indication */ 
struct pollfd pfh;               /* poll structure          */
struct sockaddr_in *sinpt;       /* TCP/IP addr. structure  */
struct t_bind  *bind;            /* bind structure ptr      */
struct t_call  *calls[MAX_CIND]; /* call structure ptr      */
struct t_discon discon;          /* disconnect structure    */
LONG   SAPhandle;                /* SAP handle              */
unsigned short dynamic_socket = 0x0000; /* zero value causes NetWare
                                           to open a socket dynamically 
                                           in the range 0x4000 - 0x7FFF */

/* Function prototypes */
int  AcceptConnection(void);         /* Assign a new TEP, accept*/
void ProcessConnection(void *parm);  /* Process new connection  */
void TerminateNLM(void);             /* Cleanup function        */

main(int argc, char **argv)
{
   int            fh_new;        /* new transport endpoint        */
   IPX_ADDR       *Addr;         /* address structure for SPX     */
   int            i;
   NWCONN_HANDLE  connHandle, startConnHandle;
   NWCCODE        ccode;
   NWINET_ADDR    InetAddr;
   nuint          openState;
   nuint16        buffer;
   
   startConnHandle=0;  
   openState = NWCC_OPEN_UNLICENSED;

   if( argc != 3 )
   {
      printf("Usage: %s <name> <# connect indications>\n", argv[0]);
      exit(1);
   }
   conn_ind = atoi(argv[2]);
   conn_ind = abs(conn_ind);
   if( conn_ind > MAX_CIND )
      conn_ind = MAX_CIND;

   /* NWCallsInit() must be called before any other NetWare SDK functions */
   ccode = NWCallsInit (NULL,NULL);
   if (ccode) 
   {
      printf("NWCallsInit failed: %X\r\n", ccode);
      return;
   }

   /* Register function that will be executed when NLM
      exits or is unloaded */
   if( atexit( TerminateNLM ) != NULL )
   {
      printf("atexit failed");
      exit(1);
   }

   /* Get NetWare to assign us a dynamic socket number */
   ccode = SpxOpenSocket(&dynamic_socket);
   if (ccode)
   {
      printf("SpxOpenSocket: failed  %d\n", ccode);
      return;
   }

   /* Open server's TEP */
   if((pfh.fd = t_open("/dev/nspx", O_RDWR, NULL)) == -1)
   {
      t_error("t_open");
      exit(1);
   }

   /* Allocate space for structures */
   bind = (struct t_bind *) t_alloc(pfh.fd, T_BIND, T_ALL);
   if( bind == NULL )
   {
      t_error("t_alloc");
      exit(1);
   }

   /* Prepare bind structure and then call t_bind */
   bind->qlen = conn_ind;
   bind->addr.len = sizeof( IPX_ADDR );
   Addr = (IPX_ADDR *)bind->addr.buf;

   /* Open a connection and obtain a connection handle */
   /* Note:  A connection is established to a server only for the
             convenience of reading the net address from the 
             Bindery.  This server connection is not necessary
             for the transport functions to work.      */

   ccode = NWCCOpenConnByName(
            /*  start Conn Handle */ startConnHandle,
            /*  Name of Server    */ (pnstr8) argv[1],
            /*  Name format       */ NWCC_NAME_FORMAT_BIND,
            /*  open state        */ openState,
            /*  Novell reserved   */ NWCC_RESERVED,
            /*  Connection Handle */ &connHandle);

   if (ccode)
   {
      printf( "\nNWCCOpenConnByName: failed %04x", ccode );
      exit(1);
   }

   ccode = NWCCGetConnInfo(connHandle, 
                           NWCC_INFO_CONN_NUMBER,
                           sizeof(buffer),
                           &buffer);
   if (ccode)
   {
      printf("NWCCGetConnInfo failed: %X\n",ccode);
      return;
   }

   /* Get internetwork address */
   ccode = NWGetInetAddr(connHandle, buffer, &InetAddr);
   if (ccode)
   {
      printf("NWGetInetAddr failed: %X\n",ccode);
      return;
   }

   /* Close server connection */
   ccode = NWCCCloseConn(connHandle);
   if(ccode)
      printf("\nNWCCCloseConn: failed %04x", ccode);
   
   /* copy internetwork address to t_bind address buffer */
   memcpy(Addr->ipxa_net, InetAddr.networkAddr,
          sizeof(InetAddr.networkAddr));
   memcpy(Addr->ipxa_node, InetAddr.netNodeAddr,
          sizeof(InetAddr.netNodeAddr));

   *(WORD *)Addr->ipxa_socket = IntSwap(dynamic_socket);

   if( t_bind(pfh.fd, bind, bind) == -1 )
   {
      t_error("t_bind");
      exit(1);
   }

   /* Check if this is socket you wanted to be bound to */
   if( *(WORD *)&bind->addr.buf[10] != IntSwap(dynamic_socket) )
   {
      printf("Bound wrong address: %d != %d \n",
             *(WORD *)&bind->addr.buf[10], IntSwap(dynamic_socket) );
      exit(1);
   }
   conn_ind = bind->qlen;     /* negotiated qlen value */

   /* Advertise server on dynamic_socket */
   SAPhandle = AdvertiseService( TLI_TYPE, argv[1],
                                 IntSwap(dynamic_socket) );
   if( SAPhandle == NULL )
   {
      printf("AdvertiseService failed for: %s\n", argv[1]);
      exit(1);
   }
   printf("Server %s is ready. %d connection indication(s).\n",
          argv[1], conn_ind );
   pfh.events = POLLIN;

   for (;;)
   {
      if( poll( &pfh, 1, -1 ) == -1 )
      {
         perror("poll");
         exit(1);
      }

      if( pfh.revents != POLLIN )
      {
         perror("poll returned error event");
         exit(1);
      }

      if( t_look(pfh.fd) == T_LISTEN )
      {
         for( i=0; i<conn_ind; i++ )
         {
            if( calls[i] == NULL )
               break;
         }
         calls[i]=(struct t_call *)t_alloc(pfh.fd,T_CALL,T_ALL);
         if( calls[i] == NULL )
         {
            t_error("t_alloc");
            exit(1);
         }
         if ( t_listen( pfh.fd, calls[i] ) == -1 )
         {
            t_error("t_listen");
            exit(1);
         }
         if((fh_new = AcceptConnection()) != NULL )
            if( BeginThread(ProcessConnection,
                           NULL, NULL, (void *)fh_new) == -1 )
             {
                printf("BeginThread failed\n");
                exit(1);
             }
      }
      else
      { 
         t_error("t_look");
         exit(1);
      }
      ThreadSwitch();
   } /* for */
} /* main */

/* Assign a new TEP, accept and return new TEP on success  */
int AcceptConnection(void)
{
   int i, fh;

   /* Open new TEP */
   if((fh = t_open("/dev/nspx", O_RDWR, NULL)) == -1)
   {
      t_error("t_open");
      exit(1);
   }

   /* Bind it, address is not important here */
   if( t_bind(fh, NULL, NULL) == -1 )
   {
      t_error("t_bind");
      t_close(fh);
      exit(1);
   }

   /* Find which slot has pending accept */
   for( i=0; i<conn_ind; i++ )
   {
      if( calls[i] != NULL ) 
         break;
   }

   /* Accept the call from a client */
   if( t_accept(pfh.fd, fh, calls[i]) == -1 )
   {
      if( t_errno == TLOOK )
      {
         printf("TLOOK[%d]\n", calls[i]->sequence);
         t_close(fh);
         return NULL;
      }
      else
      {
         t_error("t_accept");
         t_close(fh);
         exit(1);
      }
   }

   /* Check if it was disconnect */
   ThreadSwitch();
   if( t_look(fh) == T_DISCONNECT )
   {
      printf("DISCONNECT[%d]\n", calls[i]->sequence);
      if( t_rcvdis(fh, NULL) < 0 )
         t_error("t_rcvdis");

      t_free((char *)calls[i], T_CALL);
      calls[i] = NULL;
      t_close(fh);
      return NULL;
   }

   /* Success, so free calls[i] slot to be reusable by others */
   printf("ACCEPT[%d]\n", calls[i]->sequence);
   t_free((char *)calls[i], T_CALL);
   calls[i] = NULL;
   return fh;
}

void ProcessConnection(void *parm)
{
   FILE *fp;               /* file pointer      */
   char iobuf[132];        /* line buffer       */
   int  fh = (int) parm;

   printf("Client %d\n", fh);
   if( (fp = fopen( LOGFILE, "r" )) == NULL )
   {
      printf("Couldn't open file: %s\r\n", LOGFILE);
      return;
   }

   /* Read lines from a file and send to the client */
   while( fgets( iobuf, sizeof(iobuf), fp ) != NULL )
   {
      delay(100);    /* timeout and thread switch */
      if( t_look(fh) == T_DISCONNECT )
      {
         printf("Client %d received disconnect, EXIT.\n", fh);
         if( t_rcvdis(fh, NULL) < 0 )
            t_error("t_rcvdis");
         fclose(fp);
         t_close(fh);
         return;
      }
      if( t_snd( fh, iobuf, sizeof(iobuf), NULL) == -1 )
      {
         t_error("t_snd");
         fclose(fp);
         t_close(fh);
         return;
      }
   }
   fclose(fp);

   /* Send disconnect to the client */
   if( t_snddis(fh, NULL) == -1 )
   {
      t_error("t_snddis");
      t_close(fh);
      return;
   }

   /* Unbind TEP */
   if( t_unbind(fh) == -1 )
   {
      t_error("t_unbind");
      t_close(fh);
      return;
   }

   t_close(fh);
}

void TerminateNLM(void)
{
   int i;
   NWCCODE                  ccode;

   if( SAPhandle )
      ShutdownAdvertising( SAPhandle );
   if( bind )
      t_free((char *)bind, T_BIND);
   for( i=0; i<conn_ind; i++ )
   {
      if( calls[i] != NULL )
      {
         t_free((char *)calls[i], T_CALL );
         calls[i] = NULL;
      }
   }
   t_unbind( pfh.fd );
   t_close( pfh.fd );

   /* Give the socket back to NetWare. */
   if   (dynamic_socket != 0x0000)
   {
      ccode=SpxCloseSocket(dynamic_socket);
      if (ccode)
      {
          printf("SpxCloseSocket: failed %d\n", ccode);
          exit(1);
      }
   }
} 
