/* @(#)osr.c 17.1.1.1 (ES0-DMD) 01/25/02 17:35:26 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++ .TYPE Module .LANGUAGE C .IDENTIFICATION osr .AUTHOR Francois Ochsenbein [ESO-IPG], Philippe Defert [ST-ECF] .KEYWORDS X25 communication management .ENVIRONMENT VAX/VMS, PSI .COMMENTS Functions in this module allow to connect, clear, read and write messages over X25. The connection number is a number starting with 200. \begin{TeX} \end{TeX} .VERSION 1.0 17-Nov-1986: Creation .VERSION 1.1 16-Dec-1986: Some functions are defined as ``static" .VERSION 1.2 28-Jan-1987: New X25 structure; acquire time at each echange of message. .VERSION 2.0 03-Feb-1988: Transformed into osr routines .VERSION 2.1 30-Mar-1989: Transformed input to ASCII... (IBM computers don't) Force userdata! .VERSION 2.2 11-May-1989: Added osrwait. .VERSION 2.3 05-Sep-1989: Changed some `unclever' macros... ---------------------------------*/ #define DEBUG 0 /* Debugging option */ #define MAX_IPCC 8 /* Maximal Number of Connections */ #include #include #include #include /* System */ #define X25_BUFSIZE 256 /* Size of input buffer */ #define X25_START 200 /* Starting x25 number */ MID_EXTERN int oserror; MID_EXTERN char *oserrmsg; MID_EXTERN int vmserror; char *oshenv(); struct X25_struct { /* X25 packet for communication */ char *dte; /* Destination name */ char *net; /* Network name */ char *mbxname; /* Mailbox name */ char *mbxbuf; /* Mailbox buffer */ char *inbuf; /* Read over X25 buffer */ int inbytes; /* Bytes in inbuf */ int nw_unit; /* Number of NW device */ short int nw_chan; /* Channel to NW device */ short int mbx_chan; /* Channel to Mailbox */ /* unsigned long int efn; /* Event flag */ }; # include /* System */ # include /* Not in system... */ # include /* System */ # include /* System */ # include /* System */ # include /* System */ # include /* System */ # define MBX_BUFSIZE 256 /* Mailbox size */ # define MBX_NAME "X25S_MBA" /* Mail box logical name */ # define L dsc$w_length # define S dsc$a_pointer typedef struct dsc$descriptor descript; # define void_string {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL_PTR(char)} # define InitDescript(text) \ {sizeof(text)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S,text} # define SET_STRING(dsc,str,l) dsc.S = str, dsc.L = l \ /* Set address & length of a string */ typedef struct { /* Quad structure for descriptor */ long int len; char *s; } QUAD; struct item { unsigned short int len; unsigned short int item_code; void * addr; long * return_len; }; static unsigned short int io_status [4]; /* status quadword for qio */ static long returned_length; static descript nw_name = InitDescript("_NWA0:"); /* NW device name */ static descript mbx = void_string; /* Mail box name descriptor */ static QUAD ncb = /* ncb descriptor */ {0, 0}; static struct { /* Used with the GETDVI system service */ struct item item_1; long int end; } getdvi= { {sizeof(int),DVI$_UNIT,0,&returned_length}, 0 }; # define CheckVMS if (!(vmserror&1)) {oserror = EINTR; FINISH;} # define ioCheckVMS if (iotest()) FINISH typedef struct X25_struct X25; #define NULL_X25 NULL_PTR(X25) static X25 *Connections[MAX_IPCC] = {0,0,0,0}, *x25 = NULL_X25; static int nx25 = X25_START; /* Number of used connections */ #define FINISH goto FIN /*===================================================================== VMS-specific Routines *=====================================================================*/ /*=====================================================================*/ static int iotest() /*+++ .PURPOSE Test the status and the i/o control block .RETURNS 0 (OK) / -1 (Bad) .REMARKS *** For internal use *** ---*/ { if (vmserror & 1) if ((vmserror = io_status[0]) == SS$_CLEARED) oserrmsg = "Connection Cleared by remote DTE."; if (!(vmserror&1)) oserror = (oserrmsg ? -1 : EINTR); return(oserror); } /*=====================================================================*/ static int rncb(ptr,dte,subdte,user,net) /*+++ .PURPOSE Create the NCB (Network Control Block) .RETURNS Length used in NCB .REMARKS ***No check on the length of ncb buffer is performed!*** ---*/ unsigned char *ptr; /* OUT: resulting NCB */ unsigned char *dte; /* IN: The Destination address */ unsigned char *subdte; /* IN: Subdestination */ unsigned char *user; /* IN: User data */ unsigned char *net; /* IN: Network name */ { MID_RSTATIC unsigned char user_x29[] = {1,0,0,0}; MID_REGISTER unsigned char *p; MID_REGISTER struct header { unsigned short int len; unsigned short int item; unsigned char count; } *ps; p = ptr; /* remote DTE address */ ps = (struct header *)p, p = (unsigned char *)(ps+1); ps->count = strlen(dte); ps->item = PSI$C_NCB_REMDTE; p += oscopy(p, dte, ps->count); ps->len = p - (unsigned char *)ps; if (subdte != NULL_PTR(unsigned char)) /* remote subaddress */ { ps = (struct header *)p, p = (unsigned char *)(ps+1); ps->count = strlen(subdte); ps->item = PSI$C_NCB_REMSUBADR; p += oscopy(p, subdte, ps->count); ps->len = p - (unsigned char *)ps; } if_not(user) user = (unsigned char *)""; /***** Force User Data ****/ { ps = (struct header *)p, p = (unsigned char *)(ps+1); ps->count = strlen(user) + sizeof(user_x29); ps->item = PSI$C_NCB_USERDATA; p += oscopy(p, user_x29, sizeof(user_x29)); p += oscopy(p, user, ps->count - sizeof(user_x29)); ps->len = p - (unsigned char *)ps; } if (net != NULL_PTR(unsigned char)) /* Local Network name */ { ps = (struct header *)p, p = (unsigned char *)(ps+1); ps->count = strlen(net); ps->item = PSI$C_NCB_LOCNET; p += oscopy(p, net, ps->count); ps->len = p - (unsigned char *)ps; } return(p - ptr); } /*===================================================================== Internal Routines *=====================================================================*/ static int getx25(xid) /*+++ .PURPOSE Retrieve the X25 packet. .RETURNS 0 / -1 (error) .REMARKS ------*/ int xid; /* IN: Circuit Number */ { oserrmsg = NULL_PTR(char); oserror = 0; x25 = NULL_X25; if ((xid < X25_START) || (xid > nx25)) FINISH; x25 = Connections[xid-X25_START]; FIN: if (x25 == NULL_X25) { oserror = -1; oserrmsg = "Bad X25-circuit Number"; } return(oserror); } /*===================================================================== osr Routines *=====================================================================*/ /*=====================================================================*/ int osrinfo(xid, buf) /*+++ .PURPOSE Get info about a connection .RETURNS 0 / -1 (error). However, the net name is always filled. .REMARKS ------*/ int xid; /* IN: The Circuit Number */ struct remstat *buf; /* OUT: The info */ { getx25(xid); if(x25) buf->dte = x25->dte, buf->net = x25->net, buf->device = x25->mbxname, buf->unit= x25->nw_unit; else buf->dte = "", buf->net = oshenv("PSI$NETWORK", "LNM$SYSTEM_TABLE"), buf->device = MBX_NAME, buf->unit = 0; return(x25 ? 0 : -1); } /*=====================================================================*/ int osropen(dte,subdte,user,net) /*+++ .PURPOSE Connect to specified remote address. \begin{TeX} The operations are as follows: \begin{enumerate} \item Create the MailBox, logical name `{\tt X25S\_MB}$x$'. \item Connect the Mailbox to NWA0 channel; \item Retrieve the {\tt NW}$x$ unit, via a {\tt GETDVI} \item Build up the NCB (Network Control Block) \item Set up the virtual circuit, using {\tt IO\$\_ACCESS} \end{enumerate} \end{TeX} .RETURNS Connection facility as a positive number / -1 on error (oserror explains) .REMARKS The destination may be found using oshenv(symbolic_destination, "PSI$DTE_TABLE"), and the user data via oshenv(dest_U, "PSI$DTE_TABLE"). ------*/ char *dte; /* IN: The Destination address */ char *subdte; /* IN: Subdestination (or NULL) */ char *user; /* IN: User data (or NULL) */ char *net; /* IN: Network name (or NULL) */ { register int i; oserrmsg = NULL_PTR(char); /* Error Indicator */ oserror = 0; x25 = NULL_X25; /* Check if there are not too many connections */ for (i = 0; i < ITEMS(Connections); i++) if (!Connections[i]) break; if (i >= ITEMS(Connections)) { oserrmsg = "Too many Connections"; FINISH; } if (!dte) /* No address */ { oserrmsg = "No DTE address"; FINISH; } /* Get Network Name if not specified */ if (!net) net = oshenv("PSI$NETWORK", "LNM$SYSTEM_TABLE"); if (!net) /* No network */ { oserrmsg = "No Network Available"; FINISH; } /* Allocate the X25 structure */ x25 = (X25 *) osmmget(sizeof(X25) + X25_BUFSIZE + MBX_BUFSIZE + 2 + sizeof(MBX_NAME) + strlen(net) + strlen(dte)); if (oserror) FINISH; x25->inbuf = (char *)(x25+1); x25->mbxbuf = x25->inbuf + X25_BUFSIZE; x25->dte = x25->mbxbuf + MBX_BUFSIZE; x25->net = x25->dte + oscopuc(x25->dte, dte, 1000, EOS) + 1; x25->mbxname = x25->net + oscopuc(x25->net, net, 1000, EOS) + 1; x25->inbytes = 0; oscopy(x25->mbxname, MBX_NAME, sizeof(MBX_NAME)); /* Copy Mailbox name */ x25->mbx_chan = 0; x25->nw_chan = 0; /* 2) Create a MailBox */ /* Create a new mailbox name */ *(x25->mbxname + sizeof(MBX_NAME) - 2) = 'A' + i; SET_STRING(mbx, x25->mbxname, sizeof(MBX_NAME)-1); vmserror = SYS$CREMBX(0,&x25->mbx_chan,0,0,0,0,&mbx); CheckVMS; /* 3) Assign channel to NW (associated with mailbox) */ vmserror = SYS$ASSIGN(&nw_name,&x25->nw_chan,0,&mbx); CheckVMS; /* 4) Retrieve the actual NW number (not required, but... */ getdvi.item_1.addr = &(x25->nw_unit); vmserror = sys$getdviw(0,x25->nw_chan,0,&getdvi,io_status,0,0,0); ioCheckVMS; /* 5) Construct the NCB */ ncb.s = x25->inbuf; ncb.len = rncb(ncb.s, dte, subdte, user, net); /* 6) Ask for setup virtual circuit : send the initial packet */ vmserror = SYS$QIOW(0,x25->nw_chan,IO$_ACCESS,io_status,0,0, 0,&ncb,0,0,0,0); CheckVMS; /* If request correctly queued, decode the result from io_status */ vmserror = io_status[0]; switch(vmserror) { case SS$_NORMAL: switch(io_status[2]) { case PSI$M_STS_REMDTELNG: oserrmsg = ("Remote DTE address too long"); FINISH; case PSI$M_STS_USERLNG: oserrmsg = ("Too much user data supplied"); FINISH; } break; case SS$_ABORT: case SS$_IVDEVNAM: oserror = EINTR; switch(io_status[2]) { case PSI$C_ERR_UNKNOWN: oserrmsg = ("PSN Unknown error."); break; case PSI$C_ERR_FACLNG: oserrmsg = ("PSN Facilities too long."); break; case PSI$C_ERR_INVITEM: oserrmsg = ("PSN Invalid item code"); break; case PSI$C_ERR_CONFLICT: oserrmsg = ("PSN Conflicting items"); break; case PSI$C_ERR_BADPARM: oserrmsg = ("PSN Bad parameters specified"); break; case PSI$C_ERR_NOTRANS: oserrmsg = ("PSN No translation for name"); break; case PSI$C_ERR_RECURLMT: oserrmsg = ("Recursion limit reached"); break; case PSI$C_ERR_INVNUM: oserrmsg = ("Invalid ASCII number"); break; case PSI$C_ERR_NOICI: oserrmsg = ("No internal identifier specified"); break; case PSI$C_ERR_MANYICI: oserrmsg = ("More than one internal call identifier"); break; case PSI$C_ERR_NOTIMP: oserrmsg = ("Non implemented feature"); break; case PSI$C_ERR_NOLINES: oserrmsg = ("No lines available for the call"); break; case PSI$C_ERR_NOSUCHLINE: oserrmsg = ("No such line"); break; case PSI$C_ERR_NOSUCHPVC: oserrmsg = ("Specified PVC unknown"); break; case PSI$C_ERR_NOSUCHNET: oserrmsg = ("Specified PSN unknown"); break; case PSI$C_ERR_NOLOCAL: oserrmsg = ("ACP is out of local workspace"); break; case PSI$C_ERR_NONONPAG: oserrmsg = ("Insufficient free non-paged pool"); break; case PSI$C_ERR_NOL3: oserrmsg = ("Internal error"); break; case PSI$C_ERR_BADNAME: oserrmsg = ("Bad counted string"); break; case PSI$C_ERR_L3ERR: oserrmsg = ("Error returned from level 3"); break; } FINISH; case SS$_CLEARED: oserrmsg = ("Remote DTE rejects the call..."); default: oserror = EINTR; FINISH; } /* WAIT FOR ANSWER FROM REMOTE */ /* =========================== */ vmserror = SYS$QIOW(0,x25->mbx_chan,IO$_READVBLK|IO$M_TIMED,io_status,0,0, x25->mbxbuf, MBX_BUFSIZE,4,0,0,0); ioCheckVMS; if ((*(x25->mbxbuf) == MSG$_CONNECT) || (*(x25->mbxbuf) == MSG$_INCDAT)) /* OK TRACE_ED_STRING("Connected to ",x25->name) */ ; else { oserrmsg = "Connection rejected."; FINISH; } Connections[i] = x25; i += X25_START; /* Actual connection number */ if (i > nx25) nx25 = i; FIN: /* If connection failed, clear */ if (oserrmsg) oserror = -1; if (oserror) /* Failed */ { if (x25){ if(x25->nw_chan) SYS$DASSGN(x25->nw_chan); if(x25->mbx_chan) SYS$DASSGN(x25->mbx_chan); osmmfree(x25); } i = -1; } #if DEBUG printf("Connection Summary: dte=%s, net=%s, mailbox=%s, nw#%d\n", x25->dte, x25->net, x25->mbxname, x25->nw_unit); #endif return(i); } /*=====================================================================*/ int osrclose(xid) /*+++ .PURPOSE Clear a virtual circuit .RETURNS 0 / -1 (use osmsg for explanation) .REMARKS ---*/ int xid; /* IN: The circuit to clear */ { if (getx25(xid)) FINISH; vmserror = SYS$QIOW(0,x25->nw_chan,IO$_DEACCESS,&io_status,0,0,0,0,0,0,0,0); iotest(); SYS$DASSGN(x25->nw_chan); SYS$DASSGN(x25->mbx_chan); FIN: if(x25) osmmfree(x25), Connections[xid-X25_START] = NULL_X25; if(oserrmsg) oserror = -1; return (oserror ? -1 : 0); } /*=====================================================================*/ int osrend() /*+++ .PURPOSE Clear all virtual circuits .RETURNS 0 / -1 (use osmsg for explanation) .REMARKS ---*/ { register int i; for (i = nx25; --i >= X25_START; ) osrclose(i); return (oserror ? -1 : 0); } /*=====================================================================*/ static int rin() /*+++ .PURPOSE Check if something is to be read over X25 .RETURNS Length of input / -1 (line cleared) .REMARKS Reading is performed via the internal buffer associated to the x25 packet. ---*/ { register int len; register char *p; /* Look first if there is something in the internal buffer */ len = x25->inbytes; if (len > 0) FINISH; /* Read without wait */ /* vmserror = SYS$QIOW(0, x25->nw_chan, IO$_READVBLK|IO$M_NOW, */ vmserror = SYS$QIO(0, x25->nw_chan, IO$_READVBLK|IO$M_NOW, io_status, 0,0, x25->inbuf, X25_BUFSIZE, 0,0,0,0); CheckVMS; len = io_status[1]; switch(io_status[0]) { case SS$_NODATA: len = 0; break; case SS$_NORMAL: break; default: ioCheckVMS; } x25->inbytes = len; for (p = x25->inbuf + len; p > x25->inbuf; ) /* Transform to ASCII */ *(--p) &= 0x7f; FIN: if (oserrmsg) oserror = -1; return(oserror ? -1 : len); } /*=====================================================================*/ int osrin(xid) /*+++ .PURPOSE Check if something is to be read over X25 .RETURNS Length of input / -1 (error, see osmsg) .REMARKS ---*/ int xid; /* IN: The circuit to clear */ { register int s; if (getx25(xid)) FINISH; s = rin(); FIN: return(oserror ? -1 : s); } /*=====================================================================*/ int osrwait(xid, timeout) /*+++ .PURPOSE Wait for a message over the connection opened with osropen .RETURNS Length of message (0 if none) / -1 if error (osmsg explains) .REMARKS The message is not returned. osrread can be used for it. ---*/ int xid; /* IN: The connection number */ int timeout; /* IN: Max time to wait (s) */ { register int i, len; /* Look first if data are coming --- * Wait for a maximum of timeout seconds until the answer */ len = osrin(xid); if (len) FINISH; /* Done ... or error ! */ for (i=timeout*4; (--i >= 0) && (len == 0); len = rin() ) ospwms(250); FIN: return(oserror ? -1 : len); } /*=====================================================================*/ int osrread(xid, buf, lbuf, timeout) /*+++ .PURPOSE Read a message over the connection opened with osropen .RETURNS Length of message / -1 if error (osmsg explains) .REMARKS Return error if timeout! ---*/ int xid; /* IN: The connection number */ char *buf; /* OUT: Read text */ int lbuf; /* IN: Size of buffer */ int timeout; /* IN: Max time to wait (s) */ { register int i, len; /* Check first of the buffer is at least one character! */ if (lbuf < 1) { oserrmsg = "osrread Buffer is of negative or zero size!"; oserror = -1, len = -1; FINISH; } /* Use osrwait which terminates as soon as somethinf arrives */ len = osrwait(xid, timeout); /* Copy from internal buffer to user-supplied one */ FIN: if (len > 0) { len = MIN(x25->inbytes, lbuf); oscopy(buf, x25->inbuf, len); if ((len == 1) && (*buf == '\01')) /* Line Cleared */ { oserrmsg = "Connection Cleared by remote DTE."; oserror = -1; } i = x25->inbytes - len; /* Bytes still in buffer */ if (i > 0 ) /* There are remaining bytes */ oscopy(x25->inbuf, x25->inbuf + len, i); x25->inbytes = i; } return(oserror ? -1 : len); } /*=====================================================================*/ int osrgets(xid, buf, lbuf, timeout) /*+++ .PURPOSE Read a message over the connection opened with osropen, and reformats (suppress NUL characters and carriage-return, terminate the string with the EOS) .RETURNS Length of message / -1 if error (osmsg explains) .REMARKS ---*/ int xid; /* IN: The connection number */ char *buf; /* OUT: text to read */ int lbuf; /* IN: Size of buffer */ int timeout; /* IN: Max time to wait (s) */ { register int lr; register char *p, *q; /* p is used as the running pointer in buffer. */ p = buf, *p = EOS; lr = osrread(xid, p, lbuf-1, timeout); if (lr <= 0) FINISH; /* Error, or nothing */ /* Suppress NUL and useless * control characters */ for (q = p; --lr >=0; q++) { if (*q == EOS) continue; if (*q == '\r') continue; *(p++) = *q; } *p = EOS; lr = p - buf; FIN: return(oserror ? -1 : lr); } /*=====================================================================*/ int osrwrite(xid, buf, lbuf) /*+++ .PURPOSE Write a message over X25 .RETURNS Size of written message / -1 (osmsg explains) .REMARKS ---*/ int xid; /* IN: The connection number */ char *buf; /* IN: Text to write */ int lbuf; /* IN: Size of buffer */ { if (getx25(xid)) FINISH; vmserror = SYS$QIOW(0, x25->nw_chan, IO$_WRITEVBLK, io_status, 0,0, buf, lbuf, 0, 0,0,0,0); iotest(); FIN: return(oserror ? -1 : lbuf); } /*=====================================================================*/ int osrputs(xid, buf) /*+++ .PURPOSE Write an EOS-terminated message over the network, adding the Carriage Return .RETURNS length of written message / -1 (osmsg explains) .REMARKS ---*/ int xid; /* IN: The connection number */ char *buf; /* IN: EOS-terminated Text to write */ { register char *p; register int l; l = strlen(buf); p = buf + l; *p = '\r'; l = osrwrite(xid, buf, l+1); *p = EOS; /* Reset the EOS */ return(l); }