/******************************************************************************* Process: maple.c Multiple Applications Emulator. Author: Alex Measday Purpose: MAPLE is a program that simultaneously emulates one or more TPOCC subsystems and their communications with the TPOCC state manager. "[XQ]" commands sent by the state manager to MAPLE result in an "[ST] 0" message being returned to the state manager. MAPLE also handles spacecraft command dialogs, which are initiated by an "[XQ] /CMD" message. Consequently, MAPLE can be used by TSTOL as a state manager simulator. Invocation: % maple [-debug] [-sink ] [-vperror] ... where: "-debug" turns debug on. "-sink " specifies the name of a server that is to be emulated as a data sink. In this mode, the server receives messages from clients, but does not send any in return. "-vperror" turns VPERROR() message output on. VPERROR() messages are low-level error messages generated by TPOCC library functions; normally, they are disabled. If enabled, the messages are output to STDERR. " ... " specify the names of the servers to be emulated. The server names should be present in the "/etc/services" system file. *******************************************************************************/ /*PDL-------------------------PDL------------------------PDL** CALL OPT_GET() to parse the command line options. DOFOR each server name CALL NET_ANSWER() to create a listening socket for the server name. ENDDO DOFOR a very long time Construct a read mask for the listening sockets and any client sockets. CALL SELECT(2) to wait for input from any clients or potential clients. DOFOR each listening socket IF a new client is requesting a connection on this port THEN CALL XNET_ANSWER() to accept the connection and create an XDR stream for the connection. Add the client to the list of clients. ENDIF ENDDO DOFOR each connected client IF input is waiting to be read from this client THEN DOUNTIL no more input IF the emulated server is in "zero" mode THEN CALL NET_READ() to read whatever input has been sent by the client. ELSE CALL XNET_READ_STRING() to read the next message from the client. CALL XNET_WRITE_STRING() to return a status message to the client. ENDIF ENDDO ELSEIF the connection has been broken THEN CALL XNET_CLOSE() to close the connection. ENDIF ENDDO ENDDO **PDL-------------------------PDL------------------------PDL*/ #ifdef sccs static char sccsid[] = "File: %M% Release: %I% Date: %G%, %U%" ; #endif #include /* System error definitions. */ #include /* Signal definitions. */ #include /* Standard I/O definitions. */ #include /* Standard C Library definitions. */ #include /* C Library string functions. */ #if defined(VMS) # include /* VMS-emulation of UNIX I/O. */ # include "vmstypes.h" /* System type definitions (VMS). */ #elif defined(VXWORKS) # include /* SELECT(2) definitions. */ # include /* Socket library definitions. */ # include /* System type definitions. */ # include /* UNIX-specific definitions. */ #else # include /* UNIX-specific definitions. */ # include /* System type definitions. */ # ifdef sun # define atexit(f) on_exit (f, (char *) NULL) # endif #endif #include "libutilgen.h" /* LIBUTILGEN definitions. */ #include "net_util.h" /* Network utility definitions. */ #include "opt_util.h" /* Option scanning definitions. */ #include "xnet_util.h" /* XDR/Network utility definitions. */ #define MAX_INPUT (4*1024) /******************************************************************************* Server List - is a list of the servers that MAPLE is emulating. *******************************************************************************/ typedef struct server_entry { char *name ; /* Server name. */ int connection ; /* Server socket. */ int zero_mode ; /* 0 = XDR client, 1 = data sink. */ struct server_entry *next ; } server_entry ; static server_entry *server_list ; /******************************************************************************* Client List - is a list of the clients to which MAPLE is connected. *******************************************************************************/ typedef struct client_entry { char *server_name ; /* The client's server. */ int dialog_mode ; /* Is the connection in dialog mode? */ int zero_mode ; /* Is the connection in zero mode? */ XnetStream stream ; /* XNET stream handle for connection. */ struct client_entry *next ; } client_entry ; static client_entry *client_list ; static int debug ; /******************************************************************************* Private Functions *******************************************************************************/ static void exit_handler ( # if __STDC__ || defined(vaxc) void # endif ) ; static char *extract_tag ( # if __STDC__ || defined(vaxc) const char *message # endif ) ; static void interrupt_handler ( # if __STDC__ || defined(vaxc) int sig # endif ) ; static void pipe_handler ( # if __STDC__ || defined(vaxc) int sig # endif ) ; /******************************************************************************* MAPLE's Main Program. *******************************************************************************/ #ifdef VXWORKS int maple ( # if __STDC__ char *command_line) # else command_line) char *command_line ; # endif #else int main ( # if __STDC__ || defined(vaxc) int argc, char *argv[], char *envp[]) # else argc, argv, envp) int argc ; char *argv[] ; char *envp[] ; # endif #endif { /* Local variables. */ char *argument, inbuf[MAX_INPUT], outbuf[MAX_INPUT] ; char *s, *tag, word[16] ; client_entry *client, *prev ; fd_set read_mask, read_mask_save ; int end_of_connection, errflg, length, more_input ; int num_active, option, vperror_save ; server_entry *server ; XnetStream stream ; #ifdef VXWORKS char **argv ; int argc ; /* Parse command string into an ARGC/ARGV array of arguments. */ opt_create_argv ("maple", command_line, &argc, &argv) ; #endif vperror_save = vperror_print ; /* Save state of VPERROR_PRINT flag. */ vperror_print = 1 ; /* Enable VPERROR output during initialization. */ /* Set up a termination handler (called when the program exits), an interrupt handler (called in response to SIGINT and SIGTERM signals), and a broken pipe handler (called when an attempt to write to a broken connection generates a SIGPIPE signal). */ atexit (exit_handler) ; signal (SIGINT, interrupt_handler) ; signal (SIGTERM, interrupt_handler) ; signal (SIGPIPE, pipe_handler) ; /******************************************************************************* Scan the command line options. *******************************************************************************/ client_list = NULL ; server_list = NULL ; debug = 0 ; /* NOTE: Keep "{sink:}" first in list! */ opt_init (argc, argv, 0, "{sink:}{debug}{vperror}", NULL) ; errflg = 0 ; while (option = opt_get (NULL, &argument)) { switch (option) { case 2: /* "-debug" */ debug = 1 ; net_util_debug = 1 ; xnet_util_debug = 1 ; break ; case 3: /* "-vperror" */ vperror_save = 1 ; break ; case 1: /* "-sink " */ case NONOPT: /* "" */ server = (server_entry *) malloc (sizeof (server_entry)) ; if (server == NULL) { vperror ("[MAPLE] Error allocating server entry for \"%s\".\nmalloc: ", argument) ; exit (errno) ; } server->name = str_dupl (argument, -1) ; str_lowcase (server->name, -1) ; server->connection = -1 ; server->zero_mode = (option == 1) ; /* "-sink"? */ server->next = server_list ; server_list = server ; break ; case OPTERR: errflg++ ; break ; default : break ; } } if (errflg || (server_list == NULL)) { fprintf (stderr, "Usage: maple [-debug] [-sink ] [-vperror]\n") ; fprintf (stderr, " [... ]\n") ; exit (EINVAL) ; } /******************************************************************************* For each of the servers to be emulated, create and bind a socket to that server's listening port. *******************************************************************************/ for (server = server_list ; server != NULL ; server = server->next) { if (net_answer (server->name, "-listen", -1.0, &server->connection, NULL)) { vperror ("[MAPLE] Error creating %s's listening socket.\nnet_answer: ", server->name) ; exit (errno) ; } } /******************************************************************************* Field connection requests from new clients and service old clients. *******************************************************************************/ vperror_print = vperror_save ; /* Set error output flag. */ for ( ; ; ) { /* Construct a read mask for the server's listening sockets and any client connections. */ FD_ZERO (&read_mask_save) ; for (server = server_list ; server != NULL ; server = server->next) FD_SET (server->connection, &read_mask_save) ; for (client = client_list ; client != NULL ; client = client->next) FD_SET (xnet_socket (client->stream), &read_mask_save) ; /* Monitor all the sockets for input. */ for ( ; ; ) { read_mask = read_mask_save ; num_active = select (FD_SETSIZE, &read_mask, NULL, NULL, NULL) ; if (num_active>= 0) break ; if (errno == EINTR) continue ; /* SELECT interrupted by signal - try again. */ vperror ("[MAPLE] Error checking network for input.\nselect: ") ; exit (errno) ; } /******************************************************************************* If a new client is requesting a connection, then accept the request and add the client to the list of clients. *******************************************************************************/ for (server = server_list ; server != NULL ; server = server->next) { if (FD_ISSET(server->connection, &read_mask)) { if (xnet_answer (server->name, NULL, -1.0, &server->connection, &stream)) { vperror ("[MAPLE] Error answering connection request for \"%s\".\nxnet_answer: ", server->name) ; exit (errno) ; } if (debug) printf ("[MAPLE] Answered connection request for %s.\n", server->name) ; client = (client_entry *) malloc (sizeof (client_entry)) ; if (client == NULL) { vperror ("[MAPLE] Error allocating entry for client of \"%s\".\nmalloc: ", server->name) ; exit (errno) ; } client->server_name = str_dupl (server->name, -1) ; client->dialog_mode = 0 ; client->zero_mode = server->zero_mode ; client->stream = stream ; client->next = client_list ; client_list = client ; } /* If a client is requesting a connection */ } /* For each server */ /******************************************************************************* Service any existing clients that need to be serviced. *******************************************************************************/ prev = NULL ; client = client_list ; while (client != NULL) { end_of_connection = 0 ; if (FD_ISSET (xnet_socket (client->stream), &read_mask)) do { /* If this client connection is in zero mode (i.e., the server is acting as a data sink), then read and discard the input from the client. */ if (client->zero_mode) { if (net_read (xnet_socket (client->stream), -1.0, - (sizeof inbuf), inbuf, &length)) { end_of_connection = -1 ; } else { if (debug) printf ("(%s) %d:\n", client->server_name, xnet_socket (client->stream)) ; if (debug) hex_dump (stdout, 0, inbuf, length) ; } break ; } /* If this client connection is in command/status mode, then read the next message from the client and return a status message. */ if (xnet_read (client->stream, -1.0, &s, &more_input)) { end_of_connection = 1 ; break ; } strcpy (inbuf, s) ; if (debug) printf ("(%s) %d: \"%s\"\n", client->server_name, xnet_socket (client->stream), inbuf) ; tag = extract_tag (inbuf) ; length = 0 ; /* Skip "[DR]" or "[XQ]" prefix. */ s = getword (inbuf, "[]", &length) ; s = getword (s, "] \t", &length) ; str_lcopy (s, length, word, sizeof word) ; str_upcase (word, -1) ; /* If the client is in dialog mode, then check for an end-of-dialog message. If that message is received, then return a status message to the client; otherwise, prompt the client for the next dialog response. */ if (client->dialog_mode) { if ((strncmp (inbuf, "[DR", 3) == 0) && ((strcmp (word, "AB") == 0) || (strcmp (word, "ABORT") == 0) || (strcmp (word, "END") == 0))) { client->dialog_mode = 0 ; sprintf (outbuf, "[ST%s] 0", tag) ; xnet_write (client->stream, -1.0, "%s", outbuf) ; } else { xnet_write (client->stream, -1.0, "dialog prompt \"Next Dialog Response? \"") ; } } /* If the client is not in dialog mode, then check for a message initiating dialog mode. If that message is found, then return a prompt for the next dialog response; otherwise, simply return a successful status message to the client. */ else { /* "[XQ]" followed by "/CMD" followed by nothing? */ s = getword (s, " \t", &length) ; if ((strncmp (inbuf, "[XQ", 3) == 0) && ((strcmp (word, "/") == 0) || (strcmp (word, "/CMD") == 0) || (strcmp (word, "CMD") == 0)) && (length == 0)) { client->dialog_mode = 1 ; xnet_write (client->stream, -1.0, "dialog prompt \"Next Dialog Response? \"") ; } else { xnet_write (client->stream, -1.0, "[ST%s] 0", tag) ; } } } while (more_input) ; /* If the connection has been broken, then delete the client from the list of clients. */ if (end_of_connection) { if (debug) printf ("[MAPLE] Closing client %d of server \"%s\".\n", xnet_socket (client->stream), client->server_name) ; xnet_close (client->stream) ; if (prev == NULL) client_list = client->next ; else prev->next = client->next ; str_free (&client->server_name, -1) ; free ((char *) client) ; client = NULL ; } prev = client ; client = (client == NULL) ? client_list : client->next ; } /* For each client */ } /* For ever and a day */ } /******************************************************************************* EXTRACT_TAG - extracts the identification tag from an "[XQ]" message. *******************************************************************************/ static char *extract_tag ( # if __STDC__ || defined(vaxc) const char *message) # else message) char *message ; # endif { /* Local variables. */ char *eotag ; int length ; static char tag_string[32] ; if (message[0] != '[') return ("") ; eotag = strchr (message, ']') ; /* "[XQ]" */ if (eotag == NULL) return ("") ; length = (eotag - message) - 3 ; /* Length of tag. */ if (length> 0) { str_lcopy (&message[3], length, tag_string, sizeof tag_string) ; return (tag_string) ; } else { return ("") ; } } /******************************************************************************* Procedure: exit_handler () Purpose: The exit handler is automatically invoked when a process terminates. *******************************************************************************/ static void exit_handler ( # if __STDC__ || defined(vaxc) void) # elif defined(sun) status, arg) int status ; char *arg ; # else ) # endif { /* Local variables. */ client_entry *client ; server_entry *server ; for (server = server_list ; server != NULL ; server = server->next) { shutdown (server->connection, 2) ; /* No more reads or writes allowed. */ close (server->connection) ; } server_list = NULL ; for (client = client_list ; client != NULL ; client = client->next) { xnet_close (client->stream) ; } client_list = NULL ; } /******************************************************************************* Procedure: interrupt_handler () Purpose: Invoked by the system in response to a SIGINT signal: interrupt_handler (sig) where is the signal causing invocation of the handler. *******************************************************************************/ static void interrupt_handler ( # if __STDC__ || defined(vaxc) int sig) # else sig) int sig ; # endif { exit (0) ; } /******************************************************************************* Procedure: pipe_handler () Purpose: Invoked by the system in response to a SIGPIPE signal (caused by attempting to write to a broken socket connection): pipe_handler (sig) where is the signal causing invocation of the handler. *******************************************************************************/ static void pipe_handler ( # if __STDC__ || defined(vaxc) int sig) # else sig) int sig ; # endif { if (vperror_print) printf ("[MAPLE] Broken pipe.\n") ; signal (SIGPIPE, pipe_handler) ; /* Restore signal handler. */ }

AltStyle によって変換されたページ (->オリジナル) /