home *** CD-ROM | disk | FTP | other *** search
-
- /* */
- /* */
- /* Copyright 1987, 1988, 1989 Netwise, Inc. */
- /* All Rights Reserved */
- /* This software contains information which is proprietary to and a trade */
- /* secret of Netwise, Inc. It is not to be used, reproduced, or disclosed */
- /* except as authorized in your license agreement. */
- /* */
- /* Restricted Rights Legend */
- /* Use, duplication, or disclosure by the Government is subject to */
- /* restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in */
- /* Technical Data and Computer Software clause at 252.227-7013, or the */
- /* equivalent Government clause for other agencies. */
- /* Contractor: Netwise, Inc., Boulder, CO 80301 USA */
- /* */
- /* */
-
- #include <stdio.h>
- #include <stdarg.h>
- #include <rpchdr.h>
- #include <nwnet.h>
- #include <signal.h>
-
- #ifdef __TURBOC__
- #pragma warn -par
- #endif
-
- /*
- * Error severity codes (passed to eprt).
- */
- #define NONFATAL 0
- #define FATAL 1
-
- #ifdef SERV_DEF
- #include "serv_def.h" /* get macro definitions from this file */
- #endif
-
- /*
- * Main routine for servers to handle multiple clients using one-shot
- * or persistent connections.
- */
-
- /* Macro: Dispatcher (Required)
- *
- * The 'Dispatcher' macro is the name of the dispatcher procedure
- * for your RPC server. The name of the dispatcher procedure depends on
- * the interface name given in the RPC specification file, or on the
- * name of your RPC specification file if no interface name was given.
- */
- /*
- #define Dispatcher dispatcher_proc_name
- */
-
- /* Macro: Server_Name (Required)
- *
- * The 'Server_Name' macro must contain the name of your server as it
- * should be registered in your environment. The name clients use to
- * locate the server will be based on this.
- */
- /*
- #define Server_Name "server_name"
- */
-
- /* Macro: Server_Init (Optional)
- *
- * The macro 'Server_Init' may contain code to be executed when the
- * server is started. This may be useful for opening files, or other
- * operations that only need to be done once before any remote procedure
- * calls are serviced. If 'Server_Init' is not defined, no such
- * initialization is performed.
- *
- * Command line arguments can be accessed by using argc and argv.
- */
- /*
- #define Server_Init server_init_routine();
- */
-
- /* Macro: Server_Term (Optional)
- *
- * The macro 'Server_Term' may contain the name of a termination
- * procedure to be called when the server encounters an error. If
- * present, this routine, will be called with the code of the most
- * recent error, or zero if the server is terminating normally. If
- * not defined, the server just calls exit with an appropriate code.
- */
- /*
- #define Server_Term term_routine_name
- */
-
- /* Macro: Poll_Loop (Optional)
- *
- * The macro 'Poll_Loop' may contain code to be executed from within
- * the server's polling loop. This code is placed at the end of the
- * polling loop and can be used to insert delays or to poll for
- * additional events.
- */
- /*
- #define Poll_Loop poll_loop_routine();
- */
-
- /* Macro: Server_Loop (Optional)
- *
- * The macro 'Server_Loop' may contain code to be executed from
- * within the server's primary loop. This code is executed prior to
- * servicing the next client ( connection establishment or RPC request ).
- */
- /*
- #define Server_Loop server_loop_routine();
- */
-
- /* Macro: Dispatcher_Loop (Optional)
- *
- * The macro 'Dispatcher_Loop' may contain code to be executed from
- * within the server's primary loop. This code is executed prior to
- * servicing the next remote procedure and can be used to allocate
- * resources in preparation for this remote procedure.
- */
- /*
- #define Dispatcher_Loop dispatcher_loop_routine();
- */
-
- /* Macro: DEBUG (Optional)
- *
- * The following define controls the inclusion of optional debugging code
- * within the server. If defined, various debugging messages will be
- * printed to 'stderr' as the server executes.
- */
- /*
- #define DEBUG
- */
-
- /* Macro: DPRT (Optional)
- *
- * The DPRT macro may be defined if you want to use your own "dprt" routine.
- * The default routine in this file prints a debug message to stderr. In
- * certain environments it may be desirable to generate this output through
- * some other means. If the default "dprt" is used, it is only enabled if
- * DEBUG is defined.
- */
- /*
- #define DPRT
- */
-
- /* Macro: EPRT (Optional)
- *
- * The EPRT macro may be defined to replace the default error handler with
- * your own. The default routine prints a message to stderr.
- */
- /*
- #define EPRT
- */
-
- /* Variable: shut_down (Optional)
- *
- * Set this variable from within a remote procedure to tell the main
- * routine to initiate a shutdown of the server. If shut_down is set
- * to 2, the server is shut down immediately. If a more graceful
- * shutdown is desired, set shut_down to 1. This causes the server
- * to stop accepting new connections, and to shut down when all the
- * current client connections terminate.
- */
- int shut_down = 0;
-
- /* Variable: _scp_v2r1_ (Required)
- *
- * This variable is used to insure that compatible pieces are linked
- * together. It is not referenced during execution.
- */
- int _scp_v2r1_ = 0;
-
- /*
- * Exit codes used by servers
- */
- #define EXIT_OK 0 /* normal termination */
- #define EXIT_ERR 1 /* an error occurred */
-
- /*
- * Macros and variables for the multi-client model.
- */
-
- /* Macro: MAX_CLIENTS (Optional)
- *
- * The 'MAX_CLIENTS' macro tells how many clients may be connected at a
- * time. If MAX_CLIENTS is not defined elsewhere, a default value of 5
- * is used.
- */
- #ifndef MAX_CLIENTS
- #define MAX_CLIENTS 5
- #endif
-
- /* Variable: maxclients (Optional)
- *
- * The variable 'maxclients' is used to make the maximum number of clients
- * available to remote procedures. It is not used within this file.
- */
- int maxclients = MAX_CLIENTS;
-
- /* Variable: _client_id_ (Optional)
- *
- * This variable will be set by the main routine to the value of the
- * connection_id for the "current" client, whenever a remote procedure
- * is called. The remote procedure code can use this information to
- * store internal state data structures on a per-client basis. Some
- * example routines for managing such a state table can be found at
- * the end of this file.
- */
- int _client_id_;
-
- /*
- * The following variables are used within this file to maintain a
- * list of currently active client connections.
- */
- connection_id clients[MAX_CLIENTS+1]; /* connection id's of the clients */
- int nclients; /* current number of clients */
-
- /*
- * Macro to return the next client.
- */
- #define NEXTC(c) (((c) < nclients) ? ((c)+1) : 1)
-
- /*ARGSUSED*/
- main(argc, argv)
- int argc;
- char *argv[];
- {
- void finish(), dprt(), drop_dead(), close_client();
- int eprt(), is_connected();
- struct nl_info info;
- network_addr adx;
- struct dscp_t dscp;
- int erc;
- int i, lastc = 0;
- int got_event;
- int ch;
-
- nclients = 0;
-
- if ( (erc = nl_init(&info)) != 0 ) {
- if (eprt("nl_init", FATAL, erc, &dscp, __LINE__))
- finish(erc);
- }
-
- if ( (erc = nl_open(&clients[0])) != 0 ) {
- if (eprt("nl_open", FATAL, erc, &dscp, __LINE__))
- finish(erc);
- }
-
- adx.len = info.addr_len;
- if ( (adx.value = _allo_(adx.len)) == NULL ) {
- eprt("malloc", FATAL, -1, &dscp, __LINE__);
- finish(-1);
- }
-
- if ( (erc = nl_getaddr(adx.value, &adx.len)) != 0 ) {
- if (eprt("nl_getaddr", FATAL, erc, &dscp, __LINE__))
- finish(erc);
- }
-
- #ifndef Server_Interrupt_Control
- /*
- * The default server interrupt code for DOS sets up the program
- * to ignore the control-C interrupt. This prevents uncontrolled
- * program termination. The server can then be directed to perform an
- * orderly shutdown by pressing the ESC key.
- * If this default server interrupt code is not desired,
- * Server_Interrupt_Control should be defined.
- */
- printf("Press ESC key to shut down the server:\n"); \
- signal(SIGINT, SIG_IGN);
- #endif
-
- #ifdef Server_Init
- Server_Init /* server-specific initialization code */
- #endif
-
- dprt("Registering");
- erc = nl_register(Server_Name, adx.value, &adx.len, clients[0]);
- if ( erc != 0 ) {
- if (eprt("nl_register", FATAL, erc, &dscp, __LINE__))
- finish(erc);
- }
-
- for (; (shut_down==0) || (shut_down==1 && nclients!=0) ;drop_dead()) {
-
- int mask[MAX_CLIENTS+1];
-
- #ifdef Server_Loop
- Server_Loop
- #endif
- /*
- * Wait for an event.
- */
- dprt("Waiting for an event");
-
- for (got_event = 0; !got_event ;) {
- struct nl_stats stat;
-
- for (i=0; i <= nclients ;i++) {
- if ((erc = nl_status(clients[i], &stat)) != 0) {
- if (eprt("nl_status",FATAL,erc,&dscp,__LINE__))
- finish(erc);
- }
- if (stat.read_ready == 1) {
- mask[i] = 1;
- got_event = 1;
- } else
- mask[i] = 0;
- }
-
- #ifndef Server_Interrupt_Control
- /*
- * The server can be directed to perform an orderly
- * shutdown by pressing the ESC key.
- * If this default server interrupt code is not desired,
- * Server_Interrupt_Control should be defined.
- */
- #define ESCKEY '\033'
- if (kbhit() && (ch=getch()) == ESCKEY) {
- dprt("Server shutting down\n");
- finish(erc);
- }
- #endif
-
- #ifdef Poll_Loop
- Poll_Loop
- #endif
- }
-
- if (mask[0] == 1) { /* new client? */
- connection_id new_id;
-
- if ( (erc = nl_open(&new_id)) != 0 ) {
- if (eprt("nl_open", FATAL, erc, &dscp,__LINE__))
- finish(erc);
- }
-
- erc = nl_accept(clients[0], new_id, NULL, NULL);
- if ( erc != 0 ) {
- if (eprt("nl_accept", FATAL, erc, &dscp,__LINE__))
- finish(erc);
- }
- /*
- * If there's room, and we're not trying to shut
- * down, accept the new client. Otherwise, abort
- * the connection. Another alternative would be
- * to ignore new connections until there is room
- * in the 'clients' table. In that case, clients
- * could block indefinitely waiting for service.
- */
- if (nclients < MAX_CLIENTS && shut_down == 0) {
- clients[++nclients] = clients[0];
- clients[0] = new_id;
- mask[nclients] = 0;
- dprt("Adding a new client (%d)", nclients);
- } else {
- nl_close(clients[0]);
- clients[0] = new_id;
- dprt("Refusing service to a new client");
- }
- }
-
- i = NEXTC(lastc);
- do {
- if (mask[i] == 1)
- break;
- i = NEXTC(i);
- } while (i != NEXTC(lastc));
-
- if (mask[i] != 1) /* we didn't find anyone to service */
- continue;
-
- dprt("Servicing client %d", i);
- lastc = i;
- _client_id_ = clients[i];
-
- #ifdef Dispatcher_Loop
- Dispatcher_Loop
- #endif
- dscp.cflags = dscp.iflags = 0L;
- erc = Dispatcher(clients[i], &dscp);
-
- if (erc != 0) {
- if (eprt("dispatcher", NONFATAL, erc, &dscp, __LINE__))
- finish(erc);
- }
-
- if ( dscp.cflags & DSCP_ABORT ) {
- close_client(clients[i], DSCP_ABORT );
- dprt("Connection terminated (%d)", i);
- } else if ( dscp.cflags & DSCP_CLOSE ) {
- close_client(clients[i], DSCP_CLOSE );
- dprt("Connection terminated (%d)", i);
- }
-
- if (shut_down == 2) {
- dprt("Server shutting down forcefully");
- finish(0);
- }
- }
-
- dprt("Server shutting down gracefully");
- finish(0);
- }
-
- /*
- * is_connected(id) - return true if the given id is 'connected'
- */
- int
- is_connected(id)
- connection_id id;
- {
- struct nl_stats stat;
-
- if ( nl_status(id, &stat) != 0 )
- return 0;
-
- if (stat.errored)
- return 0;
-
- return stat.connected;
- }
-
- /*
- * drop_dead() - check for dead clients and remove any that are found
- */
- void
- drop_dead()
- {
- int i;
-
- for (i=1; i <= nclients ;i++)
- if (!is_connected(clients[i])) {
- close_client(clients[i], DSCP_CLOSE );
- dprt("Dropping dead client (%d)", i);
- }
- }
-
- /*
- * close_client(conn, abort|close) - remove the client with connection_id 'conn'
- */
- void
- close_client(conn, flag)
- connection_id conn;
- unsigned flag;
- {
- int i, erc;
-
- for (i=0; i <= nclients ; i++) {
- if (clients[i] == conn)
- break;
- }
- if (i <= nclients) {
- if ( flag & DSCP_ABORT )
- erc = nl_abort( clients[i] );
- else /* flag & DSCP_CLOSE */
- erc = nl_close( clients[i] );
- if ( erc != 0 ) {
- if (eprt("nl_close", FATAL, erc, (struct dscp_t *) 0, __LINE__))
- finish(erc);
- }
- for (; i < nclients ;i++)
- clients[i] = clients[i+1];
-
- nclients--;
- }
- }
-
- void
- finish(error)
- int error;
- {
- int i;
-
- #ifdef Server_Term
- Server_Term(error);
- #endif
-
- for (i=0; i <= nclients ;i++)
- nl_close(clients[i]);
- /*
- * Exit here with a reasonable code in case the user didn't
- * didn't define Server_Term or didn't call exit there.
- */
- exit( error ? EXIT_ERR : EXIT_OK );
- }
-
- #ifndef EPRT
- /*ARGSUSED*/
- int
- eprt(func, severity, error, dscp, line)
- char *func;
- int severity;
- int error;
- struct dscp_t *dscp;
- int line;
- {
- /*
- * Errors can be downgraded from FATAL to NONFATAL if the error
- * occurred in a Network Library routine, and the error code we
- * got indicates an "informational" error.
- */
- if (severity == FATAL) {
- if ((strncmp(func, "nl_", 3) == 0) && ((error & 1) == 0))
- severity = NONFATAL;
- }
-
- (void) fprintf(stderr, "%s failed (%d) on line %d -- %s\n",
- func, error, line,
- severity == FATAL ? "fatal" : "non-fatal");
-
- return severity;
- }
- #endif
-
- #ifndef DPRT
- /*VARARGS0*/
- void
- dprt(char *format, ...)
- {
- #ifdef DEBUG
- va_list args;
-
- va_start(args,format);
- (void) vfprintf(stderr, format, args);
- (void) fputc('\n', stderr);
- va_end(args);
- #endif
- }
- #endif
-