home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 11 / 11.iso / n / n001 / 1.ddi / SCP / MAIN_MC.C next >
Encoding:
C/C++ Source or Header  |  1989-10-17  |  13.8 KB  |  531 lines

  1.  
  2. /*                                                                            */
  3. /*                                                                            */
  4. /*                  Copyright 1987, 1988, 1989 Netwise, Inc.                  */
  5. /*                              All Rights Reserved                           */
  6. /*   This software contains information which is proprietary to and a trade   */
  7. /*   secret of Netwise, Inc. It is not to be used, reproduced, or disclosed   */
  8. /*   except as authorized in your license agreement.                          */
  9. /*                                                                            */
  10. /*                          Restricted Rights Legend                          */
  11. /*   Use, duplication,  or  disclosure  by the  Government  is  subject  to   */
  12. /*   restrictions as set forth in subparagraph (c)(1)(ii) of the Rights  in   */
  13. /*   Technical Data and  Computer Software clause  at 252.227-7013, or  the   */
  14. /*   equivalent Government clause for other agencies.                         */
  15. /*   Contractor: Netwise, Inc., Boulder, CO 80301 USA                         */
  16. /*                                                                            */
  17. /*                                                                            */
  18.  
  19. #include <stdio.h>
  20. #include <stdarg.h>
  21. #include <rpchdr.h>
  22. #include <nwnet.h>
  23. #include <signal.h>
  24.  
  25. #ifdef __TURBOC__
  26. #pragma warn -par
  27. #endif
  28.  
  29. /*
  30.  * Error severity codes (passed to eprt).
  31.  */
  32. #define    NONFATAL    0
  33. #define    FATAL        1
  34.  
  35. #ifdef SERV_DEF
  36. #include "serv_def.h"        /* get macro definitions from this file */
  37. #endif
  38.  
  39. /*
  40.  * Main routine for servers to handle multiple clients using one-shot
  41.  * or persistent connections.
  42.  */
  43.  
  44. /* Macro: Dispatcher  (Required)
  45.  *
  46.  * The 'Dispatcher' macro is the name of the dispatcher procedure
  47.  * for your RPC server. The name of the dispatcher procedure depends on
  48.  * the interface name given in the RPC specification file, or on the
  49.  * name of your RPC specification file if no interface name was given.
  50.  */
  51. /*
  52. #define    Dispatcher    dispatcher_proc_name
  53.  */
  54.  
  55. /* Macro: Server_Name  (Required)
  56.  *
  57.  * The 'Server_Name' macro must contain the name of your server as it
  58.  * should be registered in your environment. The name clients use to
  59.  * locate the server will be based on this.
  60.  */
  61. /*
  62. #define    Server_Name    "server_name"
  63.  */
  64.  
  65. /* Macro: Server_Init  (Optional)
  66.  *
  67.  * The macro 'Server_Init' may contain code to be executed when the
  68.  * server is started. This may be useful for opening files, or other
  69.  * operations that only need to be done once before any remote procedure
  70.  * calls are serviced. If 'Server_Init' is not defined, no such
  71.  * initialization is performed.
  72.  *
  73.  * Command line arguments can be accessed by using argc and argv.
  74.  */
  75. /*
  76. #define Server_Init    server_init_routine();
  77.  */
  78.  
  79. /* Macro: Server_Term  (Optional)
  80.  *
  81.  * The macro 'Server_Term' may contain the name of a termination
  82.  * procedure to be called when the server encounters an error. If
  83.  * present, this routine, will be called with the code of the most
  84.  * recent error, or zero if the server is terminating normally. If
  85.  * not defined, the server just calls exit with an appropriate code.
  86.  */
  87. /*
  88. #define    Server_Term    term_routine_name
  89.  */
  90.  
  91. /* Macro: Poll_Loop  (Optional)
  92.  *
  93.  * The macro 'Poll_Loop' may contain code to be executed from within
  94.  * the server's polling loop.  This code is placed at the end of the
  95.  * polling loop and can be used to insert delays or to poll for
  96.  * additional events.
  97.  */
  98. /*
  99. #define Poll_Loop    poll_loop_routine();
  100.  */
  101.  
  102. /* Macro: Server_Loop  (Optional)
  103.  *
  104.  * The macro 'Server_Loop' may contain code to be executed from
  105.  * within the server's primary loop.  This code is executed prior to
  106.  * servicing the next client ( connection establishment or RPC request ).
  107.  */
  108. /*
  109. #define    Server_Loop        server_loop_routine();
  110.  */
  111.  
  112. /* Macro: Dispatcher_Loop  (Optional)
  113.  *
  114.  * The macro 'Dispatcher_Loop' may contain code to be executed from
  115.  * within the server's primary loop.  This code is executed prior to
  116.  * servicing the next remote procedure and can be used to allocate
  117.  * resources in preparation for this remote procedure.
  118.  */
  119. /*
  120. #define    Dispatcher_Loop        dispatcher_loop_routine();
  121.  */
  122.  
  123. /* Macro: DEBUG  (Optional)
  124.  *
  125.  * The following define controls the inclusion of optional debugging code
  126.  * within the server. If defined, various debugging messages will be
  127.  * printed to 'stderr' as the server executes.
  128.  */
  129. /*
  130. #define    DEBUG
  131.  */
  132.  
  133. /* Macro: DPRT  (Optional)
  134.  *
  135.  * The DPRT macro may be defined if you want to use your own "dprt" routine.
  136.  * The default routine in this file prints a debug message to stderr. In
  137.  * certain environments it may be desirable to generate this output through
  138.  * some other means. If the default "dprt" is used, it is only enabled if
  139.  * DEBUG is defined.
  140.  */
  141. /*
  142. #define    DPRT
  143.  */
  144.  
  145. /* Macro: EPRT  (Optional)
  146.  *
  147.  * The EPRT macro may be defined to replace the default error handler with
  148.  * your own. The default routine prints a message to stderr.
  149.  */
  150. /*
  151. #define    EPRT
  152.  */
  153.  
  154. /* Variable: shut_down  (Optional)
  155.  *
  156.  * Set this variable from within a remote procedure to tell the main
  157.  * routine to initiate a shutdown of the server.  If shut_down is set
  158.  * to 2, the server is shut down immediately. If a more graceful
  159.  * shutdown is desired, set shut_down to 1. This causes the server
  160.  * to stop accepting new connections, and to shut down when all the
  161.  * current client connections terminate.
  162.  */
  163. int shut_down = 0;
  164.  
  165. /* Variable: _scp_v2r1_  (Required)
  166.  *
  167.  * This variable is used to insure that compatible pieces are linked
  168.  * together. It is not referenced during execution.
  169.  */
  170. int _scp_v2r1_ = 0;
  171.  
  172. /*
  173.  * Exit codes used by servers
  174.  */
  175. #define    EXIT_OK        0    /* normal termination */
  176. #define    EXIT_ERR    1    /* an error occurred */
  177.  
  178. /*
  179.  * Macros and variables for the multi-client model.
  180.  */
  181.  
  182. /* Macro: MAX_CLIENTS  (Optional)
  183.  *
  184.  * The 'MAX_CLIENTS' macro tells how many clients may be connected at a
  185.  * time. If MAX_CLIENTS is not defined elsewhere, a default value of 5
  186.  * is used.
  187.  */
  188. #ifndef    MAX_CLIENTS
  189. #define    MAX_CLIENTS 5
  190. #endif
  191.  
  192. /* Variable: maxclients  (Optional)
  193.  *
  194.  * The variable 'maxclients' is used to make the maximum number of clients
  195.  * available to remote procedures. It is not used within this file.
  196.  */
  197. int    maxclients = MAX_CLIENTS;
  198.  
  199. /* Variable: _client_id_  (Optional)
  200.  *
  201.  * This variable will be set by the main routine to the value of the
  202.  * connection_id for the "current" client, whenever a remote procedure
  203.  * is called.  The remote procedure code can use this information to
  204.  * store internal state data structures on a per-client basis.  Some
  205.  * example routines for managing such a state table can be found at
  206.  * the end of this file.
  207.  */
  208. int _client_id_;
  209.  
  210. /*
  211.  * The following variables are used within this file to maintain a
  212.  * list of currently active client connections.
  213.  */
  214. connection_id clients[MAX_CLIENTS+1];    /* connection id's of the clients */
  215. int    nclients;            /* current number of clients */
  216.  
  217. /*
  218.  * Macro to return the next client.
  219.  */
  220. #define    NEXTC(c)    (((c) < nclients) ? ((c)+1) : 1)
  221.  
  222. /*ARGSUSED*/
  223. main(argc, argv)
  224. int    argc;
  225. char    *argv[];
  226. {
  227.     void    finish(), dprt(), drop_dead(), close_client();
  228.     int    eprt(), is_connected();
  229.     struct    nl_info info;
  230.     network_addr    adx;
  231.     struct    dscp_t    dscp;
  232.     int    erc;
  233.     int    i, lastc = 0;
  234.     int    got_event;
  235.     int ch;
  236.  
  237.     nclients = 0;
  238.  
  239.     if ( (erc = nl_init(&info)) != 0 ) {
  240.         if (eprt("nl_init", FATAL, erc, &dscp, __LINE__))
  241.             finish(erc);
  242.     }
  243.  
  244.     if ( (erc = nl_open(&clients[0])) != 0 ) {
  245.         if (eprt("nl_open", FATAL, erc, &dscp, __LINE__))
  246.             finish(erc);
  247.     }
  248.  
  249.     adx.len = info.addr_len;
  250.     if ( (adx.value = _allo_(adx.len)) == NULL ) {
  251.         eprt("malloc", FATAL, -1, &dscp, __LINE__);
  252.         finish(-1);
  253.     }
  254.  
  255.     if ( (erc = nl_getaddr(adx.value, &adx.len)) != 0 ) {
  256.         if (eprt("nl_getaddr", FATAL, erc, &dscp, __LINE__))
  257.             finish(erc);
  258.     }
  259.  
  260. #ifndef    Server_Interrupt_Control
  261.     /*
  262.      * The default server interrupt code for DOS sets up the program
  263.      * to ignore the control-C interrupt. This prevents uncontrolled
  264.      * program termination. The server can then be directed to perform an
  265.      * orderly shutdown by pressing the ESC key.
  266.      * If this default server interrupt code is not desired,
  267.      * Server_Interrupt_Control should be defined.
  268.      */
  269.     printf("Press ESC key to shut down the server:\n"); \
  270.     signal(SIGINT, SIG_IGN);
  271. #endif
  272.  
  273. #ifdef    Server_Init
  274.     Server_Init        /* server-specific initialization code */
  275. #endif
  276.  
  277.     dprt("Registering");
  278.     erc = nl_register(Server_Name, adx.value, &adx.len, clients[0]);
  279.     if ( erc != 0 ) {
  280.         if (eprt("nl_register", FATAL, erc, &dscp, __LINE__))
  281.             finish(erc);
  282.     }
  283.  
  284.     for (; (shut_down==0) || (shut_down==1 && nclients!=0) ;drop_dead()) {
  285.  
  286.         int    mask[MAX_CLIENTS+1];
  287.  
  288. #ifdef    Server_Loop
  289.         Server_Loop
  290. #endif
  291.         /*
  292.          * Wait for an event.
  293.          */
  294.         dprt("Waiting for an event");
  295.  
  296.         for (got_event = 0; !got_event ;) {
  297.             struct    nl_stats stat;
  298.  
  299.             for (i=0; i <= nclients ;i++) {
  300.                 if ((erc = nl_status(clients[i], &stat)) != 0) {
  301.                     if (eprt("nl_status",FATAL,erc,&dscp,__LINE__))
  302.                         finish(erc);
  303.                 }
  304.                 if (stat.read_ready == 1) {
  305.                     mask[i] = 1;
  306.                     got_event = 1;
  307.                 } else
  308.                     mask[i] = 0;
  309.             }
  310.  
  311. #ifndef    Server_Interrupt_Control
  312.             /*
  313.              * The server can be directed to perform an orderly
  314.              * shutdown by pressing the ESC key.
  315.              * If this default server interrupt code is not desired,
  316.              * Server_Interrupt_Control should be defined.
  317.              */
  318. #define ESCKEY    '\033'
  319.             if (kbhit() && (ch=getch()) == ESCKEY) {
  320.                 dprt("Server shutting down\n");
  321.                 finish(erc);
  322.             }
  323. #endif
  324.  
  325. #ifdef    Poll_Loop
  326.             Poll_Loop
  327. #endif
  328.         }
  329.  
  330.         if (mask[0] == 1) {        /* new client? */
  331.             connection_id    new_id;
  332.  
  333.             if ( (erc = nl_open(&new_id)) != 0 ) {
  334.                 if (eprt("nl_open", FATAL, erc, &dscp,__LINE__))
  335.                     finish(erc);
  336.             }
  337.  
  338.             erc = nl_accept(clients[0], new_id, NULL, NULL);
  339.             if ( erc != 0 ) {
  340.                 if (eprt("nl_accept", FATAL, erc, &dscp,__LINE__))
  341.                     finish(erc);
  342.             }
  343.             /*
  344.              * If there's room, and we're not trying to shut
  345.              * down, accept the new client. Otherwise, abort
  346.              * the connection. Another alternative would be
  347.              * to ignore new connections until there is room
  348.              * in the 'clients' table. In that case, clients
  349.              * could block indefinitely waiting for service.
  350.              */
  351.             if (nclients < MAX_CLIENTS && shut_down == 0) {
  352.                 clients[++nclients] = clients[0];
  353.                 clients[0] = new_id;
  354.                 mask[nclients] = 0;
  355.                 dprt("Adding a new client (%d)", nclients);
  356.             } else {
  357.                 nl_close(clients[0]);
  358.                 clients[0] = new_id;
  359.                 dprt("Refusing service to a new client");
  360.             }
  361.         }
  362.  
  363.         i = NEXTC(lastc);
  364.         do {
  365.             if (mask[i] == 1)
  366.                 break;
  367.             i = NEXTC(i);
  368.         } while (i != NEXTC(lastc));
  369.  
  370.         if (mask[i] != 1)    /* we didn't find anyone to service */
  371.             continue;
  372.  
  373.         dprt("Servicing client %d", i);
  374.         lastc = i;
  375.         _client_id_ = clients[i];
  376.  
  377. #ifdef    Dispatcher_Loop
  378.         Dispatcher_Loop
  379. #endif
  380.         dscp.cflags = dscp.iflags = 0L;
  381.         erc = Dispatcher(clients[i], &dscp);
  382.  
  383.         if (erc != 0) {
  384.             if (eprt("dispatcher", NONFATAL, erc, &dscp, __LINE__))
  385.                 finish(erc);
  386.         }
  387.  
  388.         if ( dscp.cflags & DSCP_ABORT ) {
  389.             close_client(clients[i], DSCP_ABORT );
  390.             dprt("Connection terminated (%d)", i);
  391.         } else if ( dscp.cflags & DSCP_CLOSE ) {
  392.             close_client(clients[i], DSCP_CLOSE );
  393.             dprt("Connection terminated (%d)", i);
  394.         }
  395.  
  396.         if (shut_down == 2) {
  397.             dprt("Server shutting down forcefully");
  398.             finish(0);
  399.         }
  400.     }
  401.  
  402.     dprt("Server shutting down gracefully");
  403.     finish(0);
  404. }
  405.  
  406. /*
  407.  * is_connected(id) - return true if the given id is 'connected'
  408.  */
  409. int
  410. is_connected(id)
  411. connection_id id;
  412. {
  413.     struct nl_stats stat;
  414.  
  415.     if ( nl_status(id, &stat) != 0 )
  416.         return 0;
  417.  
  418.     if (stat.errored)
  419.         return 0;
  420.  
  421.     return stat.connected;
  422. }
  423.  
  424. /*
  425.  * drop_dead() - check for dead clients and remove any that are found
  426.  */
  427. void
  428. drop_dead()
  429. {
  430.     int    i;
  431.  
  432.     for (i=1; i <= nclients ;i++)
  433.         if (!is_connected(clients[i])) {
  434.             close_client(clients[i], DSCP_CLOSE );
  435.             dprt("Dropping dead client (%d)", i);
  436.         }
  437. }
  438.  
  439. /*
  440.  * close_client(conn, abort|close) - remove the client with connection_id 'conn'
  441.  */
  442. void
  443. close_client(conn, flag)
  444. connection_id    conn;
  445. unsigned    flag;
  446. {
  447.     int    i, erc;
  448.  
  449.     for (i=0; i <= nclients ; i++) {
  450.         if (clients[i] == conn)
  451.             break;
  452.     }
  453.     if (i <= nclients) {
  454.         if ( flag & DSCP_ABORT )
  455.             erc = nl_abort( clients[i] );
  456.         else /* flag & DSCP_CLOSE */
  457.             erc = nl_close( clients[i] );
  458.         if ( erc != 0 ) {
  459.             if (eprt("nl_close", FATAL, erc, (struct dscp_t *) 0, __LINE__))
  460.                 finish(erc);
  461.         }
  462.         for (; i < nclients ;i++)
  463.             clients[i] = clients[i+1];
  464.  
  465.         nclients--;
  466.     }
  467. }
  468.  
  469. void
  470. finish(error)
  471. int    error;
  472. {
  473.     int i; 
  474.  
  475. #ifdef    Server_Term
  476.     Server_Term(error);
  477. #endif
  478.  
  479.     for (i=0; i <= nclients ;i++)
  480.         nl_close(clients[i]);
  481.     /*
  482.      * Exit here with a reasonable code in case the user didn't
  483.      * didn't define Server_Term or didn't call exit there.
  484.      */
  485.     exit( error ? EXIT_ERR : EXIT_OK );
  486. }
  487.  
  488. #ifndef    EPRT
  489. /*ARGSUSED*/
  490. int
  491. eprt(func, severity, error, dscp, line)
  492. char    *func;
  493. int    severity;
  494. int    error;
  495. struct    dscp_t    *dscp;
  496. int    line;
  497. {
  498.     /*
  499.      * Errors can be downgraded from FATAL to NONFATAL if the error
  500.      * occurred in a Network Library routine, and the error code we
  501.      * got indicates an "informational" error.
  502.      */
  503.     if (severity == FATAL) {
  504.         if ((strncmp(func, "nl_", 3) == 0) && ((error & 1) == 0))
  505.             severity = NONFATAL;
  506.     }
  507.  
  508.     (void) fprintf(stderr, "%s failed (%d) on line %d -- %s\n",
  509.         func, error, line,
  510.         severity == FATAL ? "fatal" : "non-fatal");
  511.  
  512.     return severity;
  513. }
  514. #endif
  515.  
  516. #ifndef    DPRT
  517. /*VARARGS0*/
  518. void
  519. dprt(char *format, ...)
  520. {
  521. #ifdef    DEBUG
  522.     va_list args;
  523.  
  524.     va_start(args,format);
  525.     (void) vfprintf(stderr, format, args);
  526.     (void) fputc('\n', stderr);
  527.     va_end(args);
  528. #endif
  529. }
  530. #endif
  531.