home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tsrvr.zip / TSERVER.C < prev    next >
C/C++ Source or Header  |  1995-10-27  |  35KB  |  997 lines

  1. /**************************************************************************
  2. **  tserver.c - Client/Server Demonstration Server                       **
  3. **  Version 0.000                                                        **
  4. **  1995-05-07 Michael J. Barillier                                      **
  5. **                                                                       **
  6. **  Disclaimer of warranties:  The following code is provided "AS IS",   **
  7. **  without warranty of any kind.  The author shall not be held liable   **
  8. **  for any damages arising from the use of this sample code, even if    **
  9. **  advised of the possiblilty of such damages.                          **
  10. **                                                                       **
  11. **         File:  csdemo\tserver.c                                       **
  12. **  Description:  Implements a sample server.  This server uses named    **
  13. **                pipes for communication with clients, and responds to  **
  14. **                two commands:                                          **
  15. **                    time - Returns 'OK HH:MM:SS'                       **
  16. **                    shutdown - Returns 'OK'                            **
  17. **                All interactions are standard ASCII text, \n-          **
  18. **                terminated.  Error codes are:                          **
  19. **                    EBADCMD - Invalid/unrecognized command.            **
  20. **                    EOFLOW - Command buffer overflow.                  **
  21. **      Created:  1995-05-07                                             **
  22. **  Last update:                                                         **
  23. **        Notes:  IBM C Set++ compatible.  See makefile for compilation  **
  24. **                instructions.                                          **
  25. **************************************************************************/
  26.  
  27. #define INCL_DOSERRORS
  28. #define INCL_DOSFILEMGR
  29. #define INCL_DOSNMPIPES
  30. #define INCL_DOSPROCESS
  31. #define INCL_DOSSEMAPHORES
  32.  
  33. #include <ctype.h>
  34. #include <signal.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <time.h>
  39. #include <os2.h>
  40.  
  41. /*  Configuration #defines.  */
  42.  
  43. #define MAXCMDLENGTH 16    /*  ... Not including parameters.  */
  44. #define MAXCMDBUFFERLENGTH 256    /*  Longer input lines are ignored.  */
  45. #define PIPEBUFFERSIZE ( 2 * MAXCMDBUFFERLENGTH )
  46. #define CLIENTPOLLTIMEOUT 250    /*  ... In milliseconds.  */
  47. #define CONNECTPOLLTIMEOUT 250
  48.  
  49. /*  Interthread error codes.  */
  50.  
  51. enum errorcode { none = 0, pipedisconnected, unknown };
  52.  
  53. /*  Interthread message identifiers.  */
  54.  
  55. enum messageid {
  56.     breakhit, closed, connected, ctclosed, cterror, executing, shutdownreq
  57.     };
  58.  
  59. /*  Inter-thread communication uses message structures placed in a       **
  60. **  message queue ( see structure below ).  This structure contains a    **
  61. **  message ID, a pointer to the next message in the list, and any       **
  62. **  parameter information needed within the program.                     */
  63.  
  64. struct message {
  65.     enum messageid id;
  66.     struct message * next;
  67.     union {
  68.         struct {
  69.             HPIPE hpipe;
  70.             } closeddata;
  71.         struct {
  72.             HPIPE hpipe;
  73.             } connecteddata;
  74.         struct {
  75.             enum errorcode ec;
  76.             HPIPE hpipe;
  77.             } cterrordata;
  78.         struct {
  79.             char cmd[MAXCMDLENGTH+1];
  80.             HPIPE hpipe;
  81.             } executingdata;
  82.         } data;
  83.     };
  84.  
  85. /*  The message queue structure.  Actually a linked-list header with     **
  86. **  semaphores to serialize access.                                      */
  87.  
  88. struct messagequeue {
  89.     HMTX access;
  90.     struct message * q;
  91.     struct message ** qtail;
  92.     HEV available;    /*  Posted when a message is enqueued.  */
  93.     };
  94.  
  95. /*  Parameter block sent to the connect thread.  */
  96.  
  97. struct connectthreadparameters {
  98.     HEV initialized;    /*  Posted when thread is up and running.  */
  99.     unsigned char maxpipes;    /*  Max pipes to open for clients.  */
  100.     char * pipename;
  101.     HEV shutdown;    /*  Posted to notify thread to shut down.  */
  102.     struct messagequeue * msgq;    /*  ... For incoming messages.  */
  103.     HEV terminated;    /*  Posted when thread has cleaned up.  */
  104.     };
  105.  
  106. /*  Parameter block sent to the client handler thread.  Parameters are   **
  107. **  similar to those in connectthreadparameters.                         */
  108.  
  109. struct clienthandlerthreadparameters {
  110.     HEV initialized;
  111.     struct messagequeue * inmsgq;    /*  Incoming messages.  */
  112.     struct messagequeue * outmsgq;    /*  Outgoing messages.  */
  113.     HEV terminated;
  114.     };
  115.  
  116. void clienthandlerthread( void * );
  117. void connectthread( void * );
  118.  
  119. static enum errorcode apiret2ec( APIRET );
  120. static char * strip( char * );
  121.  
  122. /**************************************************************************
  123. **  main                                                                 **
  124. **                                                                       **
  125. **  Description:  Application entry point.  Primarily dispatches         **
  126. **                messages to the other threads.                         **
  127. **   Parameters:  (none)                                                 **
  128. **      Returns:  int - Error level returned to OS/2.                    **
  129. **      Created:  1995-05-07 mjb                                         **
  130. **      Updates:                                                         **
  131. **        Notes:                                                         **
  132. **************************************************************************/
  133.  
  134. /*  Standard file handles.  */
  135.  
  136. #define HFILE_STDIN 0
  137. #define HFILE_STDOUT 1
  138. #define HFILE_STDERR 2
  139.  
  140. static void sigbreak( int );
  141. static char * timestamp( char * );
  142.  
  143. /*  The main application message queue must be made global so that a     **
  144. **  breakhit message can be enqueued by sigbreak.                        */
  145.  
  146. static struct messagequeue mainmsgq;
  147.  
  148. int main( void ) {
  149.  
  150.     unsigned long action;
  151.     char buffer[32];
  152.     struct messagequeue chtmsgq;
  153.     struct clienthandlerthreadparameters chtp;
  154.     unsigned long count;
  155.     struct connectthreadparameters ctp;
  156.     HFILE hfcon;
  157.     HFILE hfstdout;
  158.     struct message * msg;
  159.     APIRET rc;
  160.     int running;
  161.  
  162.     /*  Reopen file 1 in case the user has redirected output.  */
  163.  
  164.     DosOpen( "CON", &hfcon, &action, 0, FILE_NORMAL,
  165.             OPEN_ACTION_FAIL_IF_NEW|OPEN_ACTION_OPEN_IF_EXISTS,
  166.             OPEN_FLAGS_FAIL_ON_ERROR|OPEN_FLAGS_SEQUENTIAL|
  167.                 OPEN_SHARE_DENYNONE|OPEN_ACCESS_WRITEONLY,
  168.             NULL );
  169.     hfstdout = HFILE_STDOUT;
  170.     DosDupHandle(hfcon,&hfstdout);
  171.  
  172.     /*  Unbuffer stdout.  */
  173.  
  174.     setbuf(stdout,NULL);
  175.  
  176.     /*  Header stuff.  */
  177.  
  178.     printf("tserver v%d.%03d Client/Server Demonstration Server\n",
  179.             TSERVER_VERSION,TSERVER_PATCHLEVEL);
  180.  
  181.     /*  Install break handlers to catch ^C and Ctrl-Break.  */
  182.  
  183.     signal(SIGINT,sigbreak);
  184.     signal(SIGBREAK,sigbreak);
  185.  
  186.     /*  Begin application initialization ...  */
  187.  
  188.     printf("Server is initializing - please wait ... ");
  189.  
  190.     /*  Initialize mainmsgq.  */
  191.  
  192.     DosCreateMutexSem(NULL,&mainmsgq.access,0,FALSE);
  193.     mainmsgq.q = NULL;
  194.     mainmsgq.qtail = &mainmsgq.q;
  195.     DosCreateEventSem(NULL,&mainmsgq.available,0,FALSE);
  196.  
  197.     /*  Start the connect thread.  */
  198.  
  199.     DosCreateEventSem(NULL,&ctp.initialized,0,FALSE);
  200.     ctp.maxpipes = ( unsigned char ) NP_UNLIMITED_INSTANCES;
  201.     ctp.pipename = "\\pipe\\time";
  202.     DosCreateEventSem(NULL,&ctp.shutdown,0,FALSE);
  203.     ctp.msgq = &mainmsgq;
  204.     DosCreateEventSem(NULL,&ctp.terminated,0,FALSE);
  205.  
  206.     _beginthread( connectthread, NULL, 8192, ( void * ) &ctp );
  207.     DosWaitEventSem(ctp.initialized,SEM_INDEFINITE_WAIT);
  208.  
  209.     DosCloseEventSem(ctp.initialized);
  210.  
  211.     /*  Start the client handler thread.  */
  212.  
  213.     DosCreateMutexSem(NULL,&chtmsgq.access,0,FALSE);
  214.     chtmsgq.q = NULL;
  215.     chtmsgq.qtail = &chtmsgq.q;
  216.     DosCreateEventSem(NULL,&chtmsgq.available,0,FALSE);
  217.  
  218.     DosCreateEventSem(NULL,&chtp.initialized,0,FALSE);
  219.     chtp.inmsgq = &chtmsgq;
  220.     chtp.outmsgq = &mainmsgq;
  221.     DosCreateEventSem(NULL,&chtp.terminated,0,FALSE);
  222.  
  223.     _beginthread( clienthandlerthread, NULL, 8192, ( void * ) &chtp );
  224.     DosWaitEventSem(chtp.initialized,SEM_INDEFINITE_WAIT);
  225.  
  226.     DosCloseEventSem(chtp.initialized);
  227.  
  228.     printf("completed\n\n");
  229.  
  230.     /*  Initialization completed.  */
  231.  
  232.     printf( "%s Server is running.\n", timestamp(buffer) );
  233.  
  234.     running = !0;
  235.  
  236.     while ( running ) {
  237.  
  238.         printf( "\r%s ", timestamp(buffer) );
  239.  
  240.         /*  Wait one second for a message to hit the queue.  If one      **
  241.         **  arrives, handle it, otherwise just update the timer.         */
  242.  
  243.         rc = DosWaitEventSem(mainmsgq.available,1000);
  244.  
  245.         if ( rc != ERROR_TIMEOUT )
  246.  
  247.             /*  Loop until message queue is empty ( break below ).  */
  248.  
  249.             while ( !0 ) {
  250.  
  251.                 DosRequestMutexSem(mainmsgq.access,SEM_INDEFINITE_WAIT);
  252.  
  253.                 msg = mainmsgq.q;
  254.                 if ( msg != NULL ) {
  255.                     mainmsgq.q = msg->next;
  256.                     /*  If last message dequeued, reset tail pointer.  */
  257.                     if ( mainmsgq.q == NULL )
  258.                         mainmsgq.qtail = &mainmsgq.q;
  259.                     }
  260.  
  261.                 DosResetEventSem(mainmsgq.available,&count);
  262.  
  263.                 DosReleaseMutexSem(mainmsgq.access);
  264.  
  265.                 /*  Break out of inner loop if last message pulled.  */
  266.  
  267.                 if ( msg == NULL )
  268.                     break;
  269.  
  270.                 /*  Update the timer ...  */
  271.  
  272.                 printf( "\r%s ", timestamp(buffer) );
  273.  
  274.                 switch ( msg->id ) {
  275.  
  276.                     case closed:
  277.                         /*  Client disconnected.  */
  278.                         printf("Closed pipe %lu.\n",
  279.                                 msg->data.closeddata.hpipe);
  280.                         break;
  281.  
  282.                     case connected:
  283.                         /*  Client connected.  Display message, then     **
  284.                         **  pass the pipe handle to the client handler.  */
  285.                         printf("Connected pipe %lu.\n",
  286.                                 msg->data.connecteddata.hpipe);
  287.                         DosRequestMutexSem(chtmsgq.access,
  288.                                 SEM_INDEFINITE_WAIT);
  289.                         *chtmsgq.qtail =
  290.                                 malloc( sizeof( struct message ) );
  291.                         (*chtmsgq.qtail)->id = connected;
  292.                         (*chtmsgq.qtail)->data.connecteddata.hpipe =
  293.                                 msg->data.connecteddata.hpipe;
  294.                         (*chtmsgq.qtail)->next = NULL;
  295.                         DosPostEventSem(chtmsgq.available);
  296.                         DosReleaseMutexSem(chtmsgq.access);
  297.                         break;
  298.  
  299.                     case ctclosed:
  300.                         /*  Connect thread has shut down prematurely.    **
  301.                         **  This will occur if there is an error in      **
  302.                         **  that thread.                                 */
  303.                         printf(
  304.                                 "Connect thread died - NO NEW CLIENTS MAY "
  305.                                     "CONNECT.\n");
  306.                         break;
  307.  
  308.                     case cterror:
  309.                         /*  Display error message from the connect       **
  310.                         **  thread.                                      */
  311.                         if ( msg->data.cterrordata.ec == pipedisconnected )
  312.                             printf("Disconnected from pipe %lu.\n",
  313.                                     msg->data.cterrordata.hpipe);
  314.                           else
  315.                             printf("Error %d in connect thread.\n",
  316.                                     msg->data.cterrordata.ec);
  317.                         break;
  318.  
  319.                     case executing:
  320.                         /*  Log informative message.  */
  321.                         printf("Pipe %d executing: %s.\n",
  322.                                 msg->data.executingdata.hpipe,
  323.                                 msg->data.executingdata.cmd);
  324.                         break;
  325.  
  326.                     case shutdownreq:
  327.                         printf("Shutting down.\n");
  328.                         running = 0;
  329.                         break;
  330.  
  331.                     case breakhit:
  332.                         printf("**** break ****\n");
  333.                         running = 0;
  334.                         break;
  335.  
  336.                     }
  337.  
  338.                 free(msg);
  339.  
  340.                 }
  341.  
  342.         }
  343.  
  344.     /*  Begin termination ...  */
  345.  
  346.     printf("\nServer is shutting down - please wait ... ");
  347.  
  348.     /*  Post shutdown message to client handler thread.  */
  349.  
  350.     DosRequestMutexSem(chtmsgq.access,SEM_INDEFINITE_WAIT);
  351.     *chtmsgq.qtail = malloc( sizeof( struct message ) );
  352.     (*chtmsgq.qtail)->id = shutdownreq;
  353.     (*chtmsgq.qtail)->next = NULL;
  354.     DosPostEventSem(chtmsgq.available);
  355.     DosReleaseMutexSem(chtmsgq.access);
  356.  
  357.     DosWaitEventSem(chtp.terminated,SEM_INDEFINITE_WAIT);
  358.  
  359.     DosCloseEventSem(chtp.terminated);
  360.  
  361.     /*  Clear out any messages remaining in the queue.  */
  362.  
  363.     while ( chtmsgq.q != NULL ) {
  364.         msg = chtmsgq.q;
  365.         chtmsgq.q = msg->next;
  366.         free(msg);
  367.         }
  368.  
  369.     DosCloseEventSem(chtmsgq.available);
  370.     DosCloseMutexSem(chtmsgq.access);
  371.  
  372.     /*  Post shutdown semaphore in connect thread.  */
  373.  
  374.     DosPostEventSem(ctp.shutdown);
  375.     DosWaitEventSem(ctp.terminated,SEM_INDEFINITE_WAIT);
  376.  
  377.     DosCloseEventSem(ctp.shutdown);
  378.     DosCloseEventSem(ctp.terminated);
  379.  
  380.     /*  All threads shut down.  Clear out the main message queue.  */
  381.  
  382.     while ( mainmsgq.q != NULL ) {
  383.         msg = mainmsgq.q;
  384.         mainmsgq.q = msg->next;
  385.         free(msg);
  386.         }
  387.  
  388.     DosCloseEventSem(mainmsgq.available);
  389.     DosCloseMutexSem(mainmsgq.access);
  390.  
  391.     printf("completed\n");
  392.  
  393.     /*  Outta here ...  */
  394.  
  395.     return 0;
  396.  
  397.     }
  398.  
  399. static void sigbreak( int sig ) {
  400.  
  401.     /*  Post a breakhit message if ^C or Ctrl-Break hit.  */
  402.  
  403.     DosRequestMutexSem(mainmsgq.access,SEM_INDEFINITE_WAIT);
  404.  
  405.     *mainmsgq.qtail = malloc( sizeof( struct message ) );
  406.     (*mainmsgq.qtail)->id = breakhit;
  407.     (*mainmsgq.qtail)->next = NULL;
  408.  
  409.     DosPostEventSem(mainmsgq.available);
  410.  
  411.     DosReleaseMutexSem(mainmsgq.access);
  412.  
  413.     /*  Re-install the handler, which is unloaded when called.  */
  414.  
  415.     signal( sig, (_SigFunc) sigbreak );
  416.  
  417.     }
  418.  
  419. /*  Function to format a timestamp.  Note:  buffer must be at least 20   **
  420. **  bytes long.                                                          */
  421.  
  422. static char * timestamp( char * buffer ) {
  423.  
  424.     struct tm * tm;
  425.     time_t tt;
  426.  
  427.     time(&tt);
  428.     tm = localtime(&tt);
  429.  
  430.     sprintf( buffer, "%04d-%02d-%02d-%02d.%02d.%02d", 1900+tm->tm_year,
  431.             1+tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
  432.             tm->tm_sec );
  433.  
  434.     return buffer;
  435.  
  436.     }
  437.  
  438. /**************************************************************************
  439. **  clienthandlerthread                                                  **
  440. **                                                                       **
  441. **  Description:  Thread executed by the application to process input    **
  442. **                from the clients.  A list of clientinfo structures is  **
  443. **                scanned, and, if input is available in the pipe, the   **
  444. **                command buffer is updated.  Once a \n has been read,   **
  445. **                the buffer processed as a command, and a result        **
  446. **                string formatted and sent to the client.               **
  447. **   Parameters:  parameters:void * - A pointer to a parameter block     **
  448. **                    from the _beginthread call.  Expected to point to  **
  449. **                    a clienthandlerthreadparameters structure.         **
  450. **      Returns:  (none)                                                 **
  451. **      Created:  1995-05-07 mjb                                         **
  452. **      Updates:                                                         **
  453. **        Notes:  The processing is implemented as ( sort of ) a         **
  454. **                finite state machine.  This is really a mess           **
  455. **                without gotos.                                         **
  456. **************************************************************************/
  457.  
  458. /*  Information relating to a particular client ( and its associated     **
  459. **  pipe ) is stored in a clientinfo structure.  This includes a buffer  **
  460. **  used to hold the incoming command as data is available.              */
  461.  
  462. struct clientinfo {
  463.     HPIPE hpipe;
  464.     char cmdbuffer[MAXCMDBUFFERLENGTH+1];
  465.     unsigned short cmdbufferlength;
  466.     int overflowed;
  467.     struct clientinfo * next;
  468.     };
  469.  
  470. /*  clientpolltimeout determines how often the clients are checked for   **
  471. **  input.  If this is set too low, this thread will eat up CPU time.    */
  472.  
  473. static const unsigned long clientpolltimeout = CLIENTPOLLTIMEOUT;
  474.  
  475. void clienthandlerthread( void * parameters ) {
  476.  
  477.     char buffer[32];
  478.     struct clienthandlerthreadparameters * chtp;
  479.     struct clientinfo * ci;
  480.     struct clientinfo * cilist;
  481.     unsigned long count;
  482.     int dataread;
  483.     unsigned short i;
  484.     struct message * msg;
  485.     struct messagequeue * inmsgq;
  486.     struct messagequeue * outmsgq;
  487.     struct clientinfo ** pci;
  488.     APIRET rc;
  489.     int running;
  490.     HEV terminated;
  491.     struct tm * tm;
  492.     time_t tt;
  493.  
  494.     /*  Pull info from the parameter block.  When done, signal the       **
  495.     **  function which called _beginthread, allowing it to free up the   **
  496.     **  parameter block, if necessary.                                   */
  497.  
  498.     chtp = ( struct clienthandlerthreadparameters * ) parameters;
  499.  
  500.     inmsgq = chtp->inmsgq;
  501.     outmsgq = chtp->outmsgq;
  502.     terminated = chtp->terminated;
  503.  
  504.     DosPostEventSem(chtp->initialized);
  505.  
  506.     /*  Initialize the client info list.  */
  507.  
  508.     cilist = NULL;
  509.  
  510.     running = !0;
  511.  
  512.     /*  Finite state machine begins ...  */
  513.  
  514.     waitq:    /*  Wait for input in the queue.  */
  515.  
  516.     rc = DosWaitEventSem(inmsgq->available,clientpolltimeout);
  517.     if ( rc == ERROR_TIMEOUT )
  518.         goto checkclients;
  519.  
  520.     /*  Fall through otherwise ...  */
  521.  
  522.     processq:    /*  Process input in the queue.  */
  523.  
  524.     /*  Loops until all messages processed.  The running switch is set   **
  525.     **  if a shutdownreq message is pulled from the queue.               */
  526.  
  527.     while ( running ) {
  528.  
  529.         DosRequestMutexSem(inmsgq->access,SEM_INDEFINITE_WAIT);
  530.  
  531.         msg = inmsgq->q;
  532.         if ( msg != NULL ) {
  533.             inmsgq->q = msg->next;
  534.             if ( inmsgq->q == NULL )
  535.                 inmsgq->qtail = &inmsgq->q;
  536.             }
  537.  
  538.         DosResetEventSem(inmsgq->available,&count);
  539.  
  540.         DosReleaseMutexSem(inmsgq->access);
  541.  
  542.         /*  Last message dequeued.  */
  543.  
  544.         if ( msg == NULL )
  545.             break;
  546.  
  547.         switch ( msg->id ) {
  548.  
  549.             case connected:
  550.                 /*  A new client has connected.  Add a clientinfo to     **
  551.                 **  the head of the list ( easier than adding it to the  **
  552.                 **  tail ).                                              */
  553.                 ci = malloc( sizeof( struct clientinfo ) );
  554.                 ci->hpipe = msg->data.connecteddata.hpipe;
  555.                 ci->cmdbufferlength = 0;
  556.                 ci->overflowed = 0;
  557.                 ci->next = cilist;
  558.                 cilist = ci;
  559.                 break;
  560.  
  561.             case shutdownreq:
  562.                 /*  Need to shut down in preparation for program exit.  */
  563.                 running = 0;
  564.                 break;
  565.  
  566.             }
  567.  
  568.         free(msg);
  569.  
  570.         }
  571.  
  572.     if ( !running )
  573.         goto completed;
  574.  
  575.     /*  Otherwise fall through ...  */
  576.  
  577.     checkclients:    /*  Check for incoming text in pipes.  */
  578.  
  579.     dataread = 0;
  580.  
  581.     pci = &cilist;
  582.  
  583.     while ( *pci != NULL ) {
  584.  
  585.         rc =
  586.                 DosRead( (*pci)->hpipe,
  587.                     &(*pci)->cmdbuffer[(*pci)->cmdbufferlength],
  588.                     sizeof( (*pci)->cmdbuffer ) - (*pci)->cmdbufferlength,
  589.                     &count );
  590.  
  591.         if (
  592.                 ( rc != NO_ERROR || count == 0 ) &&
  593.                     rc != ERROR_NO_DATA ) {
  594.             /*  Some error has occurred, probably the client has closed  **
  595.             **  his end of the pipe.  Close this end, notify the         **
  596.             **  application that the pipe/client has closed, and pull    **
  597.             **  the clientinfo structure from the list.                  */
  598.             ci = *pci;
  599.             *pci = (*pci)->next;
  600.             DosClose(ci->hpipe);
  601.             DosRequestMutexSem(outmsgq->access,SEM_INDEFINITE_WAIT);
  602.             *outmsgq->qtail = malloc( sizeof( struct message ) );
  603.             (*outmsgq->qtail)->id = closed;
  604.             (*outmsgq->qtail)->data.closeddata.hpipe = ci->hpipe;
  605.             (*outmsgq->qtail)->next = NULL;
  606.             DosPostEventSem(outmsgq->available);
  607.             DosReleaseMutexSem(outmsgq->access);
  608.             free(ci);
  609.             continue;
  610.             }
  611.  
  612.         if ( rc == NO_ERROR ) {
  613.  
  614.             (*pci)->cmdbufferlength += ( unsigned short ) count;
  615.  
  616.             dataread = !0;
  617.  
  618.             checkfornl:
  619.  
  620.             for (
  621.                     i = 0;
  622.                         i < (*pci)->cmdbufferlength &&
  623.                             (*pci)->cmdbuffer[i] != '\n';
  624.                         ++i )
  625.                 ;
  626.  
  627.             if ( i < (*pci)->cmdbufferlength ) {
  628.  
  629.                 /*  A \n has been found.  Check for a valid command.  */
  630.  
  631.                 if ( !(*pci)->overflowed ) {
  632.                     (*pci)->cmdbuffer[i] = '\0';
  633.                     strip( (*pci)->cmdbuffer );
  634.                     if ( stricmp( (*pci)->cmdbuffer, "time" ) == 0 ) {
  635.                         DosRequestMutexSem(outmsgq->access,
  636.                                 SEM_INDEFINITE_WAIT);
  637.                         *outmsgq->qtail =
  638.                                 malloc( sizeof( struct message ) );
  639.                         (*outmsgq->qtail)->id = executing;
  640.                         (*outmsgq->qtail)->data.executingdata.hpipe =
  641.                                 (*pci)->hpipe;
  642.                         strcpy( (*outmsgq->qtail)->data.executingdata.cmd,
  643.                                 "time" );
  644.                         (*outmsgq->qtail)->next = NULL;
  645.                         DosPostEventSem(outmsgq->available);
  646.                         DosReleaseMutexSem(outmsgq->access);
  647.                         time(&tt);
  648.                         tm = localtime(&tt);
  649.                         sprintf( buffer, "OK %02d:%02d:%02d\n",
  650.                                 tm->tm_hour, tm->tm_min, tm->tm_sec );
  651.                         }
  652.                       else if (
  653.                             stricmp( (*pci)->cmdbuffer, "shutdown" ) ==
  654.                                 0 ) {
  655.                         DosRequestMutexSem(outmsgq->access,
  656.                                 SEM_INDEFINITE_WAIT);
  657.                         *outmsgq->qtail =
  658.                                 malloc( sizeof( struct message ) );
  659.                         (*outmsgq->qtail)->id = executing;
  660.                         (*outmsgq->qtail)->data.executingdata.hpipe =
  661.                                 (*pci)->hpipe;
  662.                         strcpy( (*outmsgq->qtail)->data.executingdata.cmd,
  663.                                 "shutdown" );
  664.                         (*outmsgq->qtail)->next =
  665.                                 malloc( sizeof( struct message ) );
  666.                         (*outmsgq->qtail)->next->id = shutdownreq;
  667.                         (*outmsgq->qtail)->next->next = NULL;
  668.                         DosPostEventSem(outmsgq->available);
  669.                         DosReleaseMutexSem(outmsgq->access);
  670.                         strcpy(buffer,"OK\n");
  671.                         }
  672.                       else
  673.                         strcpy(buffer,"EBADCMD\n");
  674.                     DosWrite( (*pci)->hpipe, buffer, strlen(buffer),
  675.                             &count );
  676.                     }
  677.                   else {
  678.                     /*  \n found, but the overflow flag had been set     **
  679.                     **  earlier.  Notify client that the command was     **
  680.                     **  too long.                                        */
  681.                     strcpy(buffer,"EOFLOW\n");
  682.                     DosWrite( (*pci)->hpipe, buffer, strlen(buffer),
  683.                             &count );
  684.                     (*pci)->overflowed = 0;    /*  Clear flag.  */
  685.                     }
  686.  
  687.                 /*  Shift buffer contents.  There may be more text       **
  688.                 **  following the \n.  If so, go back and check for      **
  689.                 **  another command.                                     */
  690.  
  691.                 memmove( (*pci)->cmdbuffer, &(*pci)->cmdbuffer[i+1],
  692.                         (*pci)->cmdbufferlength - i - 1 );
  693.                 (*pci)->cmdbufferlength -= i + 1;
  694.  
  695.                 if ( (*pci)->cmdbufferlength != 0 )
  696.                     goto checkfornl;
  697.  
  698.                 }
  699.  
  700.               else
  701.  
  702.                 /*  \n not found.  Check if the buffer is full.  If so,  **
  703.                 **  set the overflow flag and clear out the buffer.      */
  704.  
  705.                 if (
  706.                         (*pci)->cmdbufferlength ==
  707.                             sizeof( (*pci)->cmdbuffer ) ) {
  708.                     (*pci)->cmdbufferlength = 0;
  709.                     (*pci)->overflowed = !0;
  710.                     }
  711.  
  712.             }
  713.  
  714.         /*  Move the pointer to the next clientinfo in the list.  */
  715.  
  716.         pci = &(*pci)->next;
  717.  
  718.         }
  719.  
  720.     /*  If no data was read ( all clients are quiet ), go to waitq.      **
  721.     **  Otherwise, go to checkq, which only checks if messages are       **
  722.     **  available and does not wait.                                     */
  723.  
  724.     if ( !dataread )
  725.         goto waitq;
  726.  
  727.     checkq:    /*  Check queue, but don't wait.  */
  728.  
  729.     rc = DosWaitEventSem(inmsgq->available,SEM_IMMEDIATE_RETURN);
  730.     if ( rc == ERROR_TIMEOUT )
  731.         goto checkclients;
  732.  
  733.     /*  Messages available.  Processes those.  */
  734.  
  735.     goto processq;
  736.  
  737.     completed:    /*  Thread has completed and needs to clean up.  */
  738.  
  739.     /*  Close all clients.  */
  740.  
  741.     while ( cilist != NULL ) {
  742.         ci = cilist;
  743.         cilist = ci->next;
  744.         DosClose(ci->hpipe);
  745.         free(ci);
  746.         }
  747.  
  748.     /*  Signal owner that the thread has completed.  */
  749.  
  750.     DosEnterCritSec();
  751.     DosPostEventSem(terminated);
  752.  
  753.     }
  754.  
  755. /**************************************************************************
  756. **  connectthread                                                        **
  757. **                                                                       **
  758. **  Description:  Creates the named pipe through which clients access    **
  759. **                the server and waits for connections.                  **
  760. **   Parameters:  parameters:void * - Parameter block passed by the      **
  761. **                    caller of _beginthread.  Assumed to point to a     **
  762. **                    connectthreadparameters structure.                 **
  763. **      Returns:  (none)                                                 **
  764. **      Created:  1995-05-07 mjb                                         **
  765. **      Updates:                                                         **
  766. **        Notes:  Like clienthandlerthread, this function is loosely     **
  767. **                implemented as a finite state machine.                 **
  768. **************************************************************************/
  769.  
  770. /*  Sizes of buffers created by OS/2 for pipes.  */
  771.  
  772. static const unsigned long inbuffersize = PIPEBUFFERSIZE;
  773. static const unsigned long outbuffersize = PIPEBUFFERSIZE;
  774.  
  775. /*  Configuration parameter to determine how often the pipe is checked   **
  776. **  for a new connection.                                                */
  777.  
  778. static const unsigned long connectpolltimeout = CONNECTPOLLTIMEOUT;
  779.  
  780. void connectthread( void * parameters ) {
  781.  
  782.     struct connectthreadparameters * ctp;
  783.     unsigned long curmax;
  784.     HPIPE hpipe;
  785.     struct messagequeue * msgq;
  786.     unsigned char maxpipes;
  787.     char pipename[CCHMAXPATH];
  788.     APIRET rc;
  789.     long req;
  790.     HEV shutdown;
  791.     HEV terminated;
  792.  
  793.     /*  Grab data from parameter block, and signal when done.  */
  794.  
  795.     ctp = ( struct connectthreadparameters * ) parameters;
  796.  
  797.     maxpipes = ctp->maxpipes;
  798.     strcpy(pipename,ctp->pipename);
  799.     shutdown = ctp->shutdown;
  800.     msgq = ctp->msgq;
  801.     terminated = ctp->terminated;
  802.  
  803.     DosPostEventSem(ctp->initialized);
  804.  
  805.     /*  Clear the pipe handle ( this is checked for a non-NULLHANDLE     **
  806.     **  value on thread exit ).                                          */
  807.  
  808.     hpipe = NULLHANDLE;
  809.  
  810.     create:    /*  Create a new instance of the pipe.  */
  811.  
  812.     rc =
  813.             DosCreateNPipe( pipename, &hpipe,
  814.                 NP_NOINHERIT|NP_ACCESS_DUPLEX,
  815.                 NP_NOWAIT|NP_TYPE_BYTE|NP_READMODE_BYTE|maxpipes,
  816.                 inbuffersize, outbuffersize, 0 );
  817.  
  818.     if (
  819.             rc != NO_ERROR && rc != ERROR_PIPE_BUSY &&
  820.                 rc != ERROR_TOO_MANY_OPEN_FILES ) {
  821.         /*  Some unexpected error has occurred.  Notify the              **
  822.         **  application.                                                 */
  823.         DosRequestMutexSem(msgq->access,SEM_INDEFINITE_WAIT);
  824.         *msgq->qtail = malloc( sizeof( struct message ) );
  825.         (*msgq->qtail)->id = cterror;
  826.         (*msgq->qtail)->data.cterrordata.ec = apiret2ec(rc);
  827.         (*msgq->qtail)->next = NULL;
  828.         DosPostEventSem(msgq->available);
  829.         DosReleaseMutexSem(msgq->access);
  830.         goto completed;
  831.         }
  832.  
  833.     if ( rc == ERROR_PIPE_BUSY )
  834.         /*  Max pipes created.  */
  835.         goto busy;
  836.  
  837.     if ( rc == ERROR_TOO_MANY_OPEN_FILES ) {
  838.         req = 1;
  839.         rc = DosSetRelMaxFH(&req,&curmax);
  840.         if ( rc  == NO_ERROR )
  841.             goto create;
  842.         /*  Can trigger a 'max connections reached' message here ...  */
  843.         goto busy;
  844.         }
  845.  
  846.     connect:    /*  Connect pipe ( allows client to connect ).  */
  847.  
  848.     rc = DosConnectNPipe(hpipe);
  849.  
  850.     if (
  851.             rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED &&
  852.                 rc != ERROR_BROKEN_PIPE ) {
  853.         /*  Unexpected error ...  */
  854.         DosRequestMutexSem(msgq->access,SEM_INDEFINITE_WAIT);
  855.         *msgq->qtail = malloc( sizeof( struct message ) );
  856.         (*msgq->qtail)->id = cterror;
  857.         (*msgq->qtail)->data.cterrordata.ec = apiret2ec(rc);
  858.         (*msgq->qtail)->next = NULL;
  859.         DosPostEventSem(msgq->available);
  860.         DosReleaseMutexSem(msgq->access);
  861.         DosClose(hpipe);
  862.         hpipe = NULLHANDLE;
  863.         goto completed;
  864.         }
  865.  
  866.     if ( rc == ERROR_PIPE_NOT_CONNECTED )
  867.         /*  No client connected, so wait a while ...  */
  868.         goto waiting;
  869.  
  870.     if ( rc == ERROR_BROKEN_PIPE ) {
  871.         /*  Client connected, but probably immediately closed.  */
  872.         DosClose(hpipe);
  873.         hpipe = NULLHANDLE;
  874.         goto create;
  875.         }
  876.  
  877.     /*  Send handle of the newly connected pipe to the application.  */
  878.  
  879.     DosRequestMutexSem(msgq->access,SEM_INDEFINITE_WAIT);
  880.     *msgq->qtail = malloc( sizeof( struct message ) );
  881.     (*msgq->qtail)->id = connected;
  882.     (*msgq->qtail)->data.connecteddata.hpipe = hpipe;
  883.     (*msgq->qtail)->next = NULL;
  884.     DosPostEventSem(msgq->available);
  885.     DosReleaseMutexSem(msgq->access);
  886.  
  887.     /*  Create a new instance of the pipe for the next client.  */
  888.  
  889.     goto create;
  890.  
  891.     waiting:    /*  Wait to see if the application is ready to exit.  */
  892.  
  893.     rc = DosWaitEventSem(shutdown,connectpolltimeout);
  894.     if ( rc != ERROR_TIMEOUT )
  895.         goto completed;
  896.  
  897.     /*  Not shutting down, so wait for a client to connect.  */
  898.  
  899.     goto connect;
  900.  
  901.     busy:    /*  Pipe is busy.  */
  902.  
  903.     rc = DosWaitEventSem(shutdown,connectpolltimeout);
  904.     if ( rc != ERROR_TIMEOUT )
  905.         goto completed;
  906.  
  907.     /*  Not shutting down, but in this case, unlike waiting above, go    **
  908.     **  and create a new pipe.                                           */
  909.  
  910.     goto create;
  911.  
  912.     completed:    /*  Processing completed.  */
  913.  
  914.     /*  Close pipe if no client had connected.  */
  915.  
  916.     if ( hpipe != NULLHANDLE )
  917.         DosClose(hpipe);
  918.  
  919.     /*  Notify application that the thread will not accept more          **
  920.     **  connections.                                                     */
  921.  
  922.     DosRequestMutexSem(msgq->access,SEM_INDEFINITE_WAIT);
  923.     *msgq->qtail = malloc( sizeof( struct message ) );
  924.     (*msgq->qtail)->id = ctclosed;
  925.     (*msgq->qtail)->next = NULL;
  926.     DosPostEventSem(msgq->available);
  927.     DosReleaseMutexSem(msgq->access);
  928.  
  929.     /*  Shutdown processing completed.  Signal owner.  */
  930.  
  931.     DosEnterCritSec();
  932.     DosPostEventSem(terminated);
  933.  
  934.     }
  935.  
  936. /**************************************************************************
  937. **  miscellaneous                                                        **
  938. **                                                                       **
  939. **  Description:  Miscellaneous functions.                               **
  940. **      Created:  1995-05-07 mjb                                         **
  941. **      Updates:                                                         **
  942. **        Notes:                                                         **
  943. **************************************************************************/
  944.  
  945. /*  Useful macros.  */
  946.  
  947. #define ESIZE(x) sizeof((x)[0])
  948. #define NELEMENTS(x) (sizeof(x)/ESIZE(x))
  949.  
  950. /*  Mapping of an OS/2 error code to an internal error code.  */
  951.  
  952. static struct {
  953.     APIRET rc;
  954.     int ec;
  955.     } apiretecmap[] = {
  956.     { ERROR_BROKEN_PIPE, pipedisconnected },
  957.     {          NO_ERROR,             none }
  958.     };
  959.  
  960. /*  Function to perform APIRET -> errorcode mapping.  */
  961.  
  962. static enum errorcode apiret2ec( APIRET rc ) {
  963.  
  964.     int i;
  965.  
  966.     for (
  967.             i = 0;
  968.                 i < NELEMENTS(apiretecmap) && apiretecmap[i].rc != rc;
  969.                 ++i )
  970.         ;
  971.  
  972.     return
  973.             ( i < NELEMENTS(apiretecmap) ) ?
  974.                 apiretecmap[i].ec : unknown;
  975.  
  976.     }
  977.  
  978. /*  Function to strip leading and trailing spaces from a string.  */
  979.  
  980. static char * strip( char * buffer ) {
  981.  
  982.     int i,j;
  983.  
  984.     for ( i = 0; buffer[i] != '\0' && isspace(buffer[i]); ++i )
  985.         ;
  986.     for ( j = 0; buffer[i] != '\0'; ++i, ++j )
  987.         buffer[j] = buffer[i];
  988.     buffer[j] = '\0';
  989.  
  990.     for ( i = strlen(buffer) - 1; i >= 0 && isspace(buffer[i]); --i )
  991.         ;
  992.     buffer[i+1] = '\0';
  993.  
  994.     return buffer;
  995.  
  996.     }
  997.