#define ident "1A13" #ifdef __DECC #pragma module SerPortServer ident #endif /* ** Updates: ** 1A01 26-Apr-1999 DM. Initial version. ** 1A04 14-Jan-2000 DM. Increase # RS-232-C ports to 20. ** 1A08 3-Apr-2000 DM. Add fortify.h ** 1A10 7-Apr-2000 DM. Change time-out units to 0.2 secs. ** 1A13 13-Sep-2000 DM. Set up time-out each time select is called to be ** Linux compatible. ** ** +--------------------------------------------------------------+ ** | Paul Scherrer Institute | ** | SINQ Project | ** | | ** | This software may be used freely by non-profit organizations.| ** | It may be copied provided that the name of P.S.I. and of the | ** | author is included. Neither P.S.I. nor the author assume any | ** | responsibility for the use of this software outside of P.S.I.| ** +--------------------------------------------------------------+ ** ** Link_options - Here is the Linker Option File **!$ if p1 .eqs. "DEBUG" then dbg1 := /debug **!$ if p1 .eqs. "DEBUG" then dbg2 := _dbg **!$ link 'dbg1'/exe=SerPortServer'dbg2'.exe sys$input/options **! SerPortServer **! mad_lib:sinq_dbg/lib **! sys$share:decw$xmlibshr12/share **! sys$share:decw$xtlibshrr5/share **! sys$share:decw$xlibshr/share **!$ purge/nolog SerPortServer'dbg2'.exe **!$ set prot=w:re SerPortServer'dbg2'.exe **!$ write sys$output "Exec file is ''f$environment ("default")'SerPortServer''DBG2'.EXE **!$ exit **!$! **!$! To build on LNSA09 ... **!$! $ import tasmad dev **!$! $ build_cc_select :== decc **!$! $ define/job deltat_c_tlb sinq_c_tlb **!$! $ bui tas_src:[tests]SerPortServer debug **!$! ** Link_options_end ** ** Building on Alpha Digital Unix: ** ** setenv TAS_BASE ~maden/tasmad ** source $TAS_BASE/tasmad.setup ** rcp -p "lnsa09:tas_src:[utils]SerPortServer.c" \ ** $TAS_SRC/utils/SerPortServer.c ** cc -std -g -o $TAS_BIN/SerPortServer \ ** -I$TAS_INC \ ** $TAS_SRC/utils/SerPortServer.c \ ** -L$TAS_LIB -lsinq -lXm -lXt -lX11 ** ** Resources and Options File: decw$user_defaults:SinQ_rc.dat ** --------------------- or $HOME/SinQ_rc ** ** Resource Option Default Description ** -------- ------ ------- ----------- ** - -name SerPortServer Name to use when looking up the ** resources. The default is the ** image file name. ** *serPortTsName -ts none Name or IP address of Terminal ** Server. ** *serPortServPort -port 4000 TCP/IP port service number. ** *serPortTrace -trace Turn on tracing. ** *serPortTraceSize -tsize 0x40000 (= 256k) Trace buffer size. ** *serPortPeriod -period 60 Period for writing trace time-stamps ** ** A value given via -name will be converted to lowercase before being used. **--------------------------------------------------------------------------- ** Module Name . . . . . . . . : [...MOTIF]SerPortServer.C ** ** Author . . . . . . . . . . : D. Maden ** Date of creation . . . . . . : Apr 1999 ** ** Purpose ** ======= ** SerPortServer is a TCP/IP server program which should accept TCP/IP ** connections from EL734, EL737 or other clients obeying the AsynSrv ** protocol and then transmit requests to an RS232C device connected to ** a Lantronix ETSnnP terminal server. It should provide the same ** functions as the SerPortServer program on the Mac. ** Use: ** === ** 1) Set up the name of the Terminal Server and the TCP/IP port number on ** which this TCP/IP server will listen for connections in the resources ** file (see above). These can also be supplied as an option on the ** command line. ** ** 2) Run the program ** ** $ spawn/nowait/out=nl: run SerPortServer ** ** or, to specify the options on the command line, use a ** DCL foreign command: ** ** $ SerPortServer :== $SerPortServer ** $ SerPortServer -ts -port ** **==================================================================== */ #include #include #include #include #include #include #include #include #ifdef __VMS #include #include #else #include #ifdef FORTIFY #include #endif #endif #include #include #include /* **-------------------------------------------------------------------------- ** Define global structures and constants. */ #include #define RS__MAX_CLIENTS 8 /* Up to 8 clients will be supported */ #define RS__MAX_ASYNCH 20 /* Asynch "ports" 0 - 19 will be allowed */ #define IN 1 #define OUT 2 #define ERROR 3 #define OPEN 4 #define CLOSE 5 #define TMO 6 #define FLUSH 7 #define NIL '\0' /*------------------------------------------------------------- ** Prototypes of Routines in this File */ void chanFlush ( int chan); void ctrlC_Handler ( int sigint); int handleUserRequest ( struct RS__MsgStruct *msg, int msg_size, struct RS__RespStruct *rply, int rply_len); int lookForTerm ( int *len, char *term, int nterm, char *terms, char *buff, char *buff_end); int open_RS232C_Chan ( int chan, struct RS__RespStruct *rply); int setupSocket ( int port); char *setupTime ( char *buff, int buff_size); int setupXrmDatabase ( XrmDatabase *db, char *name[], int *argc, char *argv[]); void traceAdd ( int n_txt, char *txt); void traceDo ( char prefix, int indx, int in_or_out, int n_txt, char *txt); void traceWrite (); /*------------------------------------------------------------- ** Global Variables */ static int Inet_port; /* TCP/IP Port number for listening */ static int Cnct_skt; /* Base socket for "accept" */ static int Client_skts[RS__MAX_CLIENTS]; /* Client sockets */ static int Client_port[RS__MAX_CLIENTS]; /* Client ports */ static char Client_host[RS__MAX_CLIENTS][32]; /* Client hosts */ static int Skt_mask = 0; /* Mask for "select" */ static int Max_skt = 0; /* Max socket to check by "select" */ static char Ts_name[32]; /* Name of Terminal Server */ static int Ts_skts[RS__MAX_ASYNCH]; /* Sockets for the TS channels */ static int Ts_ports[RS__MAX_ASYNCH]; /* Ports of the TS channels */ static int Ts_mask[RS__MAX_ASYNCH]; /* Masks for "select" */ static char Ts_buff[RS__MAX_ASYNCH][512]; /* Buffers for each channel */ static char *Ts_nxt[RS__MAX_ASYNCH]; /* Pointer to next char in buff */ static char *Ts_last[RS__MAX_ASYNCH]; /* Pointer to last char in buff */ static int Max_ts_skt = 0; /* Max TS socket to check by "select" */ static int Trace; /* Trace transactions if True */ static int Tr_buf_size = 0x40000; /* Size of trace buffer */ static unsigned char *Tr_buf = NULL; /* Buffer to hold trace info. */ static int Tr_buf_len = 0; /* Length of allo trace buff */ static unsigned char *Tr_buf_nxt; /* Next free byte in trace bf */ static int Tr_buf_free; /* # free bytes in trace buff */ static int Tr_buf_shuff; /* Trace buffer shuffle value */ static time_t Tr_timer; /* Trace time-stamp */ static int Tr_period = 60; /* Trace time-stamp period */ static int Ctrl_C_has_happened; /* Set to True when hit */ static int Ctrl_C_number; /* counter to decide when to exit */ extern int C_gbl_status; /* Return status from C_... routines */ /* **---------------------------------------------------------------------------*/ void chanFlush ( /* ========= ** Flush out any pending input */ int chan) { /* The channel to flush */ int i, my_rd_msk, status; char buff[80]; struct timeval tmo, zero_tmo = {0, 0}; /* ** First flush the type-ahead buffer */ if ((Trace) && (Ts_nxt[chan] != Ts_last[chan])) { traceDo ('C', chan, FLUSH, (Ts_last[chan] - Ts_nxt[chan]), Ts_nxt[chan]); } Ts_nxt[chan] = Ts_last[chan] = Ts_buff[chan]; /* ** Then flush the socket */ my_rd_msk = Ts_mask[chan]; tmo = zero_tmo; status = select (Ts_skts[chan]+1, (fd_set *) &my_rd_msk, NULL, NULL, &tmo); while (status > 0) { status = recv (Ts_skts[chan], buff, sizeof (buff), 0); if ((Trace) && (status > 0)) traceDo ('C', chan, FLUSH, status, buff); my_rd_msk = Ts_mask[chan]; tmo = zero_tmo; status = select (Ts_skts[chan]+1, (fd_set *) &my_rd_msk, NULL, NULL, &tmo); } } /* **-------------------------------------------------------------------------- ** ctrlC_Handler: Signal handler to detect on keyboard. */ void ctrlC_Handler (int sigint) { /* ============= */ Ctrl_C_has_happened = True; } /* **--------------------------------------------------------------------------- ** handleUserRequest - respond to user */ int handleUserRequest ( /* ================= ** Handle a user request. */ struct RS__MsgStruct *msg, /* The user's request */ int msg_size, /* The request size */ struct RS__RespStruct *rply, /* Buffer for forming reply */ int rply_len) { /* Size of reply buffer in ** (to allow for variable ** length buffers in ** RS__RespStruct. */ /* Return value is number of bytes which have been stored in rply, ** including the 4 header bytes which give the length of the remainder ** of rply, rounded up to a multiple of 4. The 4 header bytes in ** rply will have been set to the ASCII coded value of (return_val - 4). ** ** If an error is detected, rply->n_rply is "-001" and ** rply->u.sub_status may take the values: ** "-00000000001" if Non-decimal ASCII msg->n_cmnds. ** "-00000000002" if Illegal msg->n_cmnds. ** "-00000000003" if runaway command list. ** "-00000000004" if Non-decimal ASCII command length. ** "-00000000005" if illegal command length. ** "-00000000006" if bad status from send. ** "-00000000007" if bad recv length. ** "-00000000008" if Bad msg->serial_port. ** "-00000000009" if Error opening connection to serial port - the ** appropriate environment variable is not defined. ** "-00000000010" if Error opening connection to serial port - the ** environment variable is badly defined. ** "000000005001" if Protocol Level mismatch. */ int status, i, j, chan, io_chan, hdr_size, pcol_code; int ncmnds, nterm, rply_size, remaining, bytes_got; int my_rd_msk, c_len, term_fnd, max_bytes, tmo_detected; int zero = 0; char terms[4], my_term, *fmt_in, *fmt_out, *nxt_pntr; struct timeval tmo, tmo_set; char *nxt_cmnd_ptr; char *nxt_rply_ptr; char buff[16]; /* Set up a null error reply */ strcpy (rply->msg_size, "0024"); strcpy (rply->msg_id, msg->msg_id); strcpy (rply->s_pcol_lvl, RS__PROTOCOL_ID_V01B); strcpy (rply->n_rply, "-001"); strcpy (rply->u.sub_status, "000000000000"); /*---------------------------------------------- ** Check protocol level of request. If all zero, give error ** response silently (AsynSrv_Open is probably trying to ** establish our level). Otherwise, it must be recognised. */ if (memcmp (msg->c_pcol_lvl, &zero, 4) == 0) { memcpy (rply->n_rply, "0000", 4); /* Protocol level is null. Return ** "bad protocol level" error. */ memcpy (rply->u.sub_status, "000000005001", 12); return 28; }else if (memcmp (msg->c_pcol_lvl, RS__PROTOCOL_ID_V01B, 4) == 0) { pcol_code = RS__PROTOCOL_CODE_V01B; fmt_in = "%4d"; fmt_out = "%4.4d"; hdr_size = 4; }else if (memcmp (msg->c_pcol_lvl, RS__PROTOCOL_ID, 4) == 0) { pcol_code = RS__PROTOCOL_CODE; fmt_in = "%2d"; fmt_out = "%2.2d"; hdr_size = 2; }else { printf ("handleUserRequest -- bad protocol level: \"%.4s\"\n", msg->c_pcol_lvl); memcpy (rply->u.sub_status, "000000005001", 12); return 28; } nxt_cmnd_ptr = (char *) &msg->cmnds; nxt_rply_ptr = (char *) &rply->u.rplys; remaining = rply_len; /*---------------------------------------------- ** Protocol level seems OK. Continue decyphering the message */ if ((sscanf (msg->serial_port, "%4d", &chan) != 1) || (chan < 0) || (chan >= RS__MAX_ASYNCH)) { printf ("handleUserRequest: Bad msg->serial_port.\n"); memcpy (rply->u.sub_status, "-00000000008", 12); return 28; } if (sscanf (msg->tmo, "%4d", &tmo_set.tv_usec) != 1) tmo_set.tv_usec = 50; if (tmo_set.tv_usec < 0) tmo_set.tv_usec = 0; tmo_set.tv_sec = tmo_set.tv_usec/5; /* Cvt to secs and microsecs from .. */ tmo_set.tv_usec = (tmo_set.tv_usec - /* .. 0.2 secs */ 5 * tmo_set.tv_sec) * 100000; switch (msg->terms[0]) { case '0': nterm = 0; break; case '1': nterm = 1; break; case '2': nterm = 2; break; case '3': nterm = 3; break; default: nterm = 0; } if (nterm > 0) memcpy (terms, &msg->terms[1], nterm); terms[nterm] = '\0'; if (sscanf (msg->n_cmnds, "%4d", &ncmnds) != 1) { printf ("handleUserRequest: Non-decimal ASCII msg->n_cmnds.\n"); memcpy (rply->u.sub_status, "-00000000001", 12); return 28; }else if (ncmnds == 0) { memcpy (rply->n_rply, "0000", 4); /* Nothing to do!! */ return 28; }else if (ncmnds < 0) { printf ("handleUserRequest: Illegal msg->n_cmnds: %d\n", ncmnds); memcpy (rply->u.sub_status, "-00000000002", 12); return 28; } if (Ts_skts[chan] == 0) { /* ** A connection to the RS-232-C device needs to be opened. ** Open it. On error, assume reply buffer is all ready to ** be returned with its error status set up. */ if (!open_RS232C_Chan (chan, rply)) return 28; Ts_nxt[chan] = Ts_last[chan] = Ts_buff[chan]; /* Empty type-ah'd buffer */ } tmo_detected = False; for (i = 0; i < ncmnds; i++) { if (remaining < (hdr_size + 8)) break; if ((nxt_cmnd_ptr - msg->serial_port) >= msg_size) { printf ("handleUserRequest: runaway command list.\n"); memcpy (rply->u.sub_status, "-00000000003", 12); return 28; } if (sscanf (nxt_cmnd_ptr, fmt_in, &c_len) != 1) { printf ("handleUserRequest: Non-decimal ASCII command length.\n"); memcpy (rply->u.sub_status, "-00000000004", 12); return 28; } if ((c_len < 0) || (c_len >= msg_size)) { printf ("handleUserRequest: illegal command length: %d\n", c_len); memcpy (rply->u.sub_status, "-00000000005", 12); return 28; } nxt_pntr = &nxt_rply_ptr[hdr_size+1]; /* Get ready for the response */ remaining -= (hdr_size + 1); if (tmo_detected) { /*--------------------- ** If a time-out has already been detected for this ** packet of commands, then give ?TMO for the rest. */ sprintf (nxt_rply_ptr, fmt_out, 6); strcpy (nxt_pntr, "?TMO"); nxt_pntr += 5; remaining -= 5; nxt_rply_ptr = nxt_pntr; }else { /*--------------------- ** If there is a command to send, flush the input ** first and then send the command. */ if (c_len > 0) { chanFlush (chan); /* Flush any pending input from device */ status = send (Ts_skts[chan], &nxt_cmnd_ptr[hdr_size], c_len, 0); if (status != c_len) { printf ("handleUserRequest: bad send length: %d %d\n", c_len, status); memcpy (rply->u.sub_status, "-00000000006", 12); if (Trace) traceDo ('C', chan, ERROR, 18, " "); return 28; }else { if (Trace) traceDo ('C', chan, OUT, c_len, &nxt_cmnd_ptr[hdr_size]); } } /*--------------------- ** Before reading anything more from the terminal ** server, use anything which might be in the type- ** ahead buffer. */ while (True) { /* Break out via "break" on time-out or ** terminator found. */ term_fnd = lookForTerm (&bytes_got, &my_term, nterm, terms, Ts_nxt[chan], Ts_last[chan]); if (bytes_got > remaining) { /* If string too long, .. */ bytes_got = remaining; /* .. force "terminator-not-fnd" status */ term_fnd = False; my_term = NIL; } if (bytes_got > 0) { memcpy (nxt_pntr, Ts_nxt[chan], bytes_got); remaining -= bytes_got; nxt_pntr += bytes_got; Ts_nxt[chan] += bytes_got; } if (Ts_nxt[chan] == Ts_last[chan]) { /* If T-ahead buffer now empty, */ Ts_nxt[chan] = Ts_last[chan] = Ts_buff[chan]; /* .. reset it */ } if (term_fnd) { /* Response complete? */ nxt_rply_ptr[hdr_size] = my_term; /* Yes. finish it off */ sprintf (buff, fmt_out, (nxt_pntr - nxt_rply_ptr - hdr_size)); memcpy (nxt_rply_ptr, buff, hdr_size); nxt_rply_ptr = nxt_pntr; break; /* And break out of loop */ }else { /* Response not complete. Try to read some more */ my_rd_msk = Ts_mask[chan]; tmo = tmo_set; status = select (Ts_skts[chan]+1, (fd_set *) &my_rd_msk, NULL, NULL, &tmo); if (status <= 0) { /* Time-out or error? */ if (status < 0) { perror ("handleUserRequest/select"); if (Trace) traceDo ('C', chan, ERROR, 31, "