diff --git a/utils/SerPortServer.c b/utils/SerPortServer.c new file mode 100755 index 00000000..c9c2fbb3 --- /dev/null +++ b/utils/SerPortServer.c @@ -0,0 +1,1497 @@ +#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, + "