From 4097019e8f880f631fed2a4469b24781e7f9b389 Mon Sep 17 00:00:00 2001 From: "Janet B. Anderson" Date: Tue, 18 Jun 1991 19:01:27 +0000 Subject: [PATCH] Initial revision --- src/libCom/cmdSubr.c | 284 +++++++++++++++++++++ src/libCom/ezsSockSubr.c | 515 +++++++++++++++++++++++++++++++++++++++ src/libCom/genSubr.c | 333 +++++++++++++++++++++++++ 3 files changed, 1132 insertions(+) create mode 100644 src/libCom/cmdSubr.c create mode 100644 src/libCom/ezsSockSubr.c create mode 100644 src/libCom/genSubr.c diff --git a/src/libCom/cmdSubr.c b/src/libCom/cmdSubr.c new file mode 100644 index 000000000..cc681341b --- /dev/null +++ b/src/libCom/cmdSubr.c @@ -0,0 +1,284 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 10-24-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .00 10-24-90 rac initial version + * .01 06-18-91 rac installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/mod*********************************************************************** +* TITLE cmdSubr - routines for implementing keyboard command processing +* +* DESCRIPTION +* intended for use with either UNIX lwp library or with VxWorks +* +* void cmdCloseContext( ppCxCmd ) +* long cmdBgCheck( pCxCmd ) +* void cmdRead( ppCxCmd, pStopFlag ) +* void cmdSource( ppCxCmd ) +* +* BUGS +* o if changes in the command context (e.g., re-directing output) are +* made at other than root context level, such changes aren't preserved +* when closing out the level and moving to the previous level. +* +*-***************************************************************************/ +#include +#include +#include +#include + +#ifdef vxWorks +/*---------------------------------------------------------------------------- +* includes and defines for VxWorks compile +*---------------------------------------------------------------------------*/ +# include +# include +# include +#else +/*---------------------------------------------------------------------------- +* includes and defines for Sun compile +*---------------------------------------------------------------------------*/ +# include +# include +# include /* for 'select' operations */ +# include /* for 'select' operations */ +#endif + +/*+/subr********************************************************************** +* NAME cmdBgCheck - validate a ``bg'' command +* +* DESCRIPTION +* Check to see if a bg command can be honored. This depends on +* whether the command was in a source'd file (can't have bg there), +* and on whether running under VxWorks (bg is OK) or UNIX (not OK). +* +* If the command shouldn't be honored, this routine prints a message. +* +* RETURNS +* OK, or +* ERROR if the bg command should not be honored +* +*-*/ +long +cmdBgCheck(pCxCmd) +CX_CMD *pCxCmd; /* IO pointer to command context */ +{ + HELP_TOPIC *pBgTopic; + + if (pCxCmd != pCxCmd->pCxCmdRoot) { + (void)printf("can't use bg command in source file\n"); + return ERROR; + } +#ifdef vxWorks + return OK; +#else + pBgTopic = helpTopicFind(&pCxCmd->helpList, "bg"); + if (pBgTopic != NULL) + helpTopicPrint(stdout, pBgTopic); + return ERROR; +#endif +} + +/*+/subr********************************************************************** +* NAME cmdRead - read the next input line +* +* DESCRIPTION +* Prints a prompt, waits for input to be available, and then reads +* the input line into the buffer in the command context. +* +* If input is from a source'd file, no prompt is printed, and the +* input line is printed. At EOF on the file, the "source level" +* in the command context is closed, changing to the previous +* source level; the line buffer will contain a zero length line. +* +* RETURNS +* void +* +* BUGS +* o under VxWorks, with stdout redirected to a socket, a prompt which +* doesn't end with '\n' doesn't get sent +* +* SEE ALSO +* cmdSource() +* +*-*/ +void +cmdRead(ppCxCmd, pStopFlag) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +int *pStopFlag; /* I pointer to flag; 1 says to "stop" */ +{ +#ifndef vxWorks + fd_set fdSet; /* set of fd's to watch with select */ + int fdSetWidth; /* width of select bit mask */ + struct timeval fdSetTimeout;/* timeout interval for select */ +#endif + CX_CMD *pCxCmd; /* pointer to present command context */ + + pCxCmd = *ppCxCmd; +/*----------------------------------------------------------------------------- +* handle keyboard input as a special case. Only for keyboard input is a +* prompt printed. Only for keyboard input is there protection against blocking +*----------------------------------------------------------------------------*/ + if (pCxCmd->inputName == NULL) { + if (pCxCmd->prompt != NULL) { + (void)printf("%s", pCxCmd->prompt); + (void)fflush(stdout); + } + +#ifndef vxWorks +/*---------------------------------------------------------------------------- +* under SunOS, with lightweight processes, just doing a simple +* gets() call would block the pod. Instead, under SunOS, do a +* select() to check for input available. +*---------------------------------------------------------------------------*/ + fdSetWidth = getdtablesize(); + while (1) { + if (*pStopFlag != 0) + break; + fdSetTimeout.tv_sec = 0; + fdSetTimeout.tv_usec = 0; + FD_ZERO(&fdSet); + FD_SET(fileno(stdin), &fdSet); + + if (select(fdSetWidth, &fdSet, NULL, NULL, &fdSetTimeout) != 0) + break; /* input's ready !!! */ + taskSleep(SELF, 1, 0); + } +#endif + } + + if (*pStopFlag != 0) + pCxCmd->line[0] = '\0'; + else { + if (fgets(pCxCmd->line, 80, pCxCmd->input) == NULL) { + if (pCxCmd->inputName != NULL) { + (void)printf("EOF on source'd file: %s\n", pCxCmd->inputName); + } + else { + (void)printf("^D\n"); + pCxCmd->inputEOF = 1; + } + clearerr(pCxCmd->input); + cmdCloseContext(ppCxCmd); + pCxCmd = *ppCxCmd; + } + else if (pCxCmd->inputName != NULL) + (void)printf("%s", pCxCmd->line); + } + + pCxCmd->pLine = pCxCmd->line; +} + +/*+/subr********************************************************************** +* NAME cmdCloseContext - closes a command context +* +* DESCRIPTION +* Closes a command context. The action depends on source of input: +* +* keyboard (or socket): +* o store "quit\n" in the line buffer +* +* source'd file: +* o close out this "source level" +* o move to the previous "source level" +* o store '\0' in the line buffer +* +* RETURNS +* void +* +* BUGS +* o text +* +*-*/ +void +cmdCloseContext(ppCxCmd) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */ +{ + CX_CMD *pCxCmd; /* temp pointer */ + + assert(ppCxCmd != NULL); + + if ((*ppCxCmd)->inputName == NULL) + strcpy((*ppCxCmd)->line, "quit\n"); + else { + (void)fclose((*ppCxCmd)->input); + assert((*ppCxCmd)->pPrev != NULL); + pCxCmd = (*ppCxCmd)->pPrev; + GenFree((char *)(*ppCxCmd)); + *ppCxCmd = pCxCmd; + (*ppCxCmd)->line[0] = '\0'; + } +} + +/*+/subr********************************************************************** +* NAME cmdSource - process a ``source'' command +* +* DESCRIPTION +* Processes a "source fileName" command. A new command context is +* built and set up for reading from the named file. If an error +* is detected, the present command context is preserved. +* +* RETURNS +* void +* +*-*/ +void +cmdSource(ppCxCmd) +CX_CMD **ppCxCmd; /* IO ptr to pointer to command context. The + pCommand item is assumed to point to the + source command. This routine scans for + the file name. */ +{ + CX_CMD *pCxCmdNew; /* new command context structure */ + CX_CMD *pCxCmd; /* present command context structure */ + + pCxCmd = *ppCxCmd; + pCxCmd->fldLen = + nextNonSpaceField(&pCxCmd->pLine, &pCxCmd->pField, &pCxCmd->delim); + if (pCxCmd->fldLen <= 1) { + (void)printf("you must specify a file name\n"); + return; + } + if ((pCxCmdNew = (CX_CMD *)GenMalloc(sizeof(CX_CMD))) == NULL) { + (void)printf("couldn't malloc command structure\n"); + return; + } + *pCxCmdNew = *pCxCmd; /* inherit useful info from present context */ + if ((pCxCmdNew->input = fopen(pCxCmd->pField, "r")) == NULL) { + (void)printf("couldn't open file\n"); + GenFree((char *)pCxCmdNew); + return; + } + pCxCmdNew->pPrev = pCxCmd; + pCxCmdNew->inputName = pCxCmd->pField; + *ppCxCmd = pCxCmdNew; +} diff --git a/src/libCom/ezsSockSubr.c b/src/libCom/ezsSockSubr.c new file mode 100644 index 000000000..61e5c3f9d --- /dev/null +++ b/src/libCom/ezsSockSubr.c @@ -0,0 +1,515 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 11-23-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .00 11-23-90 rac initial version + * .01 06-18-91 rac installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/mod*********************************************************************** +* TITLE ezsSockSubr.c - easy-to-use socket routines +* +* DESCRIPTION +* This set of routines supports using Internet sockets for transferring +* text, with a bias toward use by server/client programs. +* +* All of these routines use the sockets as TCP/IP sockets, with the +* sockets specified as SOCK_STREAM. +* +* QUICK REFERENCE +* #include +* int ezsCheckFdRead( fd ) +* int ezsCheckFpRead( fp ) +* int ezsConnectToServer( >pServerSock, portNum, hostName ) +* int ezsCreateListenSocket( >pListenSock, portNum ) +* int ezsFopenToFd( pFp, pFd ) +* int ezsFreopenToFd( fp, pFd, >pOldFd ) +* int ezsListenExclusiveUse( pClientSock, pListenSock, pInUseFlag, +* >clientName, greetingMsg ) +* void ezsSleep( seconds, usec ) +* +* BUGS +* o doesn't provide a way for client to distinguish between server output +* to stdout and stderr, since if server redirects those streams to the +* same socket, their individual identity is lost +* o needs examples +*-***************************************************************************/ + +#include +#include +#ifdef vxWorks +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +/*+/subr********************************************************************** +* NAME ezsCheckFpRead - check fp to see if read is possible +* +* DESCRIPTION +* Checks the specified stream to see if a read operation is possible. +* +* ezsCheckFdRead(fd) performs a similar function for fd's. +* +* RETURNS +* >0 if a read can be done +* 0 if a read can't presently be done +* +* BUGS +* o due to restrictions in VxWorks 4.0.2, cannot be used for stdin, +* stdout, or stderr +* +*-*/ +int +ezsCheckFpRead(fp) +FILE *fp; /* I pointer to FILE to check for "input available" */ +{ +#ifdef vxWorks + if (fp == stdin) assertAlways(0); + else if (fp == stdout) assertAlways(0); + else if (fp == stderr) assertAlways(0); +#endif + + return(ezsCheckFdRead(fileno(fp))); +} +ezsCheckFdRead(fd) +int fd; +{ + fd_set readbits, other; + struct timeval timer; + int ret; + + timer.tv_sec = 0; + timer.tv_usec = 0; + FD_ZERO(&readbits); + FD_ZERO(&other); + FD_SET(fd, &readbits); + + ret = select(fd+1, &readbits, &other, &other, &timer); +#ifdef vxWorks + return ret; +#else + if (FD_ISSET(fd, &readbits)) + return 1; + return 0; +#endif +} + +/*+/subr********************************************************************** +* NAME ezsConnectToServer - attempt to connect via socket to server +* +* DESCRIPTION +* +* RETURNS +* 0, or +* -1 if an error occurs, or +* -2 if the host name can't be found or the server rejects the +* connection. The message buffer will contain a relevant message. +* +* BUGS +* o under VxWorks 4.0.2, if the server is on the same machine as the +* client, then the connect will fail unless the machine's name is +* in the host table +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +int +ezsConnectToServer(pServerSock, portNum, hostName, message) +int *pServerSock; /* O ptr to socket connected to server */ +int portNum; /* I port number of server */ +char *hostName; /* I name (or Internet address) of host on which + the server runs */ +char *message; /* O message from server (dimension of 80 assumed) */ +{ + struct sockaddr_in server; + struct hostent *hp; + struct hostent *gethostbyname(); + int i; + + assert(pServerSock != NULL); + assert(portNum > 0); + assert(hostName != NULL); + assert(message != NULL); + +/*----------------------------------------------------------------------------- +* set up to create the socket and connect it to the server +*----------------------------------------------------------------------------*/ + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; + server.sin_port = htons(portNum); + + if (isdigit(hostName[0])) + server.sin_addr.s_addr = inet_addr(hostName); + else { +#ifdef vxWorks + if ((server.sin_addr.s_addr = hostGetByName(hostName)) == ERROR) { + sprintf(message, "host not in VxWorks host table: %s", hostName); + return -2; + } +#else + if ((hp = gethostbyname(hostName)) == NULL) { + sprintf(message, "host unknown: %s", hostName); + return -2; + } + bcopy(hp->h_addr, &server.sin_addr, hp->h_length); +#endif + } + + *pServerSock = socket(AF_INET, SOCK_STREAM, 0); + if (*pServerSock < 0) + return -1; + if (connect(*pServerSock, &server, sizeof(server)) < 0) { + close(*pServerSock); + *pServerSock = -1; + return -1; + } + +/*----------------------------------------------------------------------------- +* now, with connection completed to server, wait for the message from +* server--the message will either identify the server, or else indicate +* that the connection is rejected. +*----------------------------------------------------------------------------*/ + if ((i=read(*pServerSock, message, 79)) <= 0) { + close(*pServerSock); + *pServerSock = -1; + sprintf(message, "connection rejected"); + return -2; + } + message[i] = '\0'; + if (strncmp(message, "***", 3) == 0) { + close(*pServerSock); + *pServerSock = -1; + return -2; + } + + return 0; +} + +/*+/subr********************************************************************** +* NAME ezsCreateListenSocket - create socket to listen for connections +* +* DESCRIPTION +* Create a socket to be used by a server to listen for connections +* by potential clients. The socket is bound to the caller-specified +* port number. +* +* The port number specified, in conjunction with the name of the +* host, constitutes a network "address" at which the server can be +* reached. +* +* Ideally, the port number and service name would be registered with +* the system manager, who would place the information in the +* /etc/services file. If this is done, then getservbyname(3N) can be +* used by a potential client to determine the proper port number to +* use. In actual practice, potential clients would "know" what port +* number to use by means of an include file provided by the service +* for compiling clients. +* +* RETURNS +* 0, or +* -1 if an error occurs (errno will have error code) +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +int +ezsCreateListenSocket(pListenSock, portNum) +int *pListenSock; /* O ptr to socket for listening for connects */ +int portNum; /* I number of service's port */ +{ + struct sockaddr_in server; /* temporary Internet socket structure */ + + assert(pListenSock != NULL); + assert(portNum > 0); + + if ((*pListenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return -1; + + bzero(&server, sizeof(server)); + server.sin_family = AF_INET; /* internet */ + server.sin_addr.s_addr = INADDR_ANY; /* anybody can connect */ + server.sin_port = htons(portNum); /* server's port */ + if (bind(*pListenSock, (struct sockaddr *)&server, sizeof(server)) < 0) { + close(*pListenSock); + return -1; + } + + if (listen(*pListenSock, 1) < 0) + return -1; + + return 0; +} + +/*+/subr********************************************************************** +* NAME ezsFopenToFd - open a specified stream to an fd +* +* DESCRIPTION +* Open a stream to an fd. Typically, this is used to open a stream +* to a socket. +* +* This routine should not be used for stdin, stdout, or stderr. For +* these streams, ezsFreopenToFd() should be used. +* +* RETURNS +* 0, or +* -1 if errors are encountered (errno has error code) +* +* BUGS +* o the stream is always opened in "r+" (update) mode +* +* SEE ALSO +* ezsFreopenToFd(), ezsFrestoreToOldFd() +* +*-*/ +int +ezsFopenToFd(pFp, pFd) +FILE **pFp; /* O pointer to pointer to new FILE */ +int *pFd; /* I pointer tofd to be used by file pointer */ +{ + assert(pFp != NULL); + assert(pFd != NULL); + + if ((*pFp=fdopen(*pFd, "r+")) == NULL) + return -1; + return 0; +} + +/*+/subr********************************************************************** +* NAME ezsFreopenToFd - reopen a specified stream to an fd +* +* DESCRIPTION +* Reopens a stream (assumed to presently be open) to an fd. Typically, +* this is used in a server program to redirect stdin, stdout, or stderr +* to use a socket. +* +* Under VxWorks, this is a pseudo re-open, which applies only to +* the current task. If a "program" consists of several tasks, each +* task must re-direct its I/O. Typically, one task will supply a +* non-NULL pOldFd argument, and other tasks will use NULL. +* +* RETURNS +* 0, or +* -1 if errors are encountered (errno has error code) +* -2 if illegal stream is used +* +* BUGS +* o the only streams which are handled are stdin, stdout, and stderr +* o stdout and stderr are line buffered under VxWorks, and output won't +* get sent (even if fflush() is used, as of 4.0.2) until '\n' is sent. +* +* SEE ALSO +* ezsFopenToFd(), ezsFrestoreToOldFd() +* +* EXAMPLE +* +*-*/ +int +ezsFreopenToFd(fp, pFd, pOldFd) +FILE *fp; /* IO pointer to FILE, such as stdin or stdout */ +int *pFd; /* I pointer to fd to be used by file pointer */ +int *pOldFd; /* O NULL, or pointer to place to store fd + previously used by file pointer */ +{ + int fdNumber; + + assert(fp != NULL); + assert(pFd != NULL); + + if (fp == stdin) fdNumber = 0; + else if (fp == stdout) fdNumber = 1; + else if (fp == stderr) fdNumber = 2; + else return -2; + +#ifdef vxWorks + if (pOldFd != NULL) + *pOldFd = ioTaskStdGet(0, fdNumber); + ioTaskStdSet(0, fdNumber, *pFd); +#else + if (pOldFd != NULL) { + if ((*pOldFd = dup(fileno(fp))) < 0) + return -1; + } + if (dup2(*pFd, fileno(fp)) < 0) + return -1; +#endif + + return 0; +} + +/*+/subr********************************************************************** +* NAME ezsListenExclusiveUse - listen for client connect; allow only 1 +* +* DESCRIPTION +* Checks to see if a client connect request is queued. If so, the +* action depends on whether a client is presently connected: +* +* o if no client is presently connected, then the connection is made: +* - a socket is created to converse with the client, +* - a greeting message is sent to the new client, +* - the inUseFlag is set to 1, and +* - the new socket is returned to the caller +* o if a client is presently connected, then the connection is +* refused, with a rejection message sent to the hopeful new client. +* The rejection message has the form "*** server inuse by host", +* where "host" is the name of the host from which the present client +* is connected. +* +* This routine simply polls the listen socket. It must be called +* periodically. Only one queued request is processed on each call. +* +* RETURNS +* -1 if an error occurs +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +int +ezsListenExclusiveUse(pClientSock, pListenSock, pInUseFlag, name, greetMsg) +int *pClientSock; /* IO ptr to socket (to be) connected to client */ +int *pListenSock; /* I socket for listening for connects */ +int *pInUseFlag; /* I ptr to flag; 1 indicates server already engaged */ +char *name; /* O ptr to buf[30] where client name will go */ +char *greetMsg; /* I greeting message to sent to new client */ +{ + int i; + static char inUseText[80]; + int connSock; /* socket connected to potential client */ + struct sockaddr_in clientAddr;/* address of client socket */ + struct hostent *clientHost; + char *clientHostAddr; + char clientHostAddrText[30]; + char clientBuf[80]; + + if (ezsCheckFdRead(*pListenSock)) { + i = sizeof(clientAddr); + connSock = accept(*pListenSock, (struct sockaddr *)&clientAddr, &i); + if (connSock < 0) + return -1; + if (*pInUseFlag) { +/*----------------------------------------------------------------------------- +* server is already engaged; send rejection message to hopeful client +*----------------------------------------------------------------------------*/ + write(connSock, inUseText, strlen(inUseText)); + close(connSock); + } + else { +/*----------------------------------------------------------------------------- +* server available; consummate connection: +* o set inUseFlag to 1 +* o build a rejection message to send if other clients attempt connect +* o send greeting message to new client +*----------------------------------------------------------------------------*/ +#ifndef vxWorks + clientHost = + gethostbyaddr((char *)&clientAddr.sin_addr, i, AF_INET); + if (clientHost != NULL) + sprintf(clientHostAddrText, "%s", clientHost->h_name); + else { +#endif + clientHostAddr = inet_ntoa(clientAddr.sin_addr); + sprintf(clientHostAddrText, "%s", clientHostAddr); +#ifndef vxWorks + } +#endif + (void)strcpy(name, clientHostAddrText); + sprintf(inUseText, "*** server in use by %s", clientHostAddrText); + write(connSock, greetMsg, strlen(greetMsg)); + *pClientSock = connSock; + *pInUseFlag = 1; + } + } + return 0; +} + +/*+/subr********************************************************************** +* NAME ezsSleep - sleep +* +* DESCRIPTION +* The current task sleeps for the specified time. +* +* This routine should not be used by programs implemented with the +* UNIX lwp (light-weight process) library--taskSleep() in genTaskSubr.c +* should be used, instead. +* +* RETURNS +* void +* +* SEE ALSO +* taskSleep() +* +*-*/ +void +ezsSleep(seconds, usec) +int seconds; /* I number of seconds (added to usec) to sleep */ +int usec; /* I number of micro-sec (added to sec) to sleep */ +{ +#ifndef vxWorks + usleep((unsigned)(seconds*1000000 + usec)); +#else + int ticks; + static int ticksPerSec=0; + static int usecPerTick; + + if (ticksPerSec == 0) { + ticksPerSec = sysClkRateGet(); + usecPerTick = 1000000 / ticksPerSec; + } + + ticks = seconds*ticksPerSec + usec/usecPerTick + 1; + taskDelay(ticks); +#endif +} diff --git a/src/libCom/genSubr.c b/src/libCom/genSubr.c new file mode 100644 index 000000000..6699f9e6a --- /dev/null +++ b/src/libCom/genSubr.c @@ -0,0 +1,333 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 03-29-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .00 03-29-90 rac initial version + * .01 06-18-91 rac installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/mod*********************************************************************** +* TITLE genSubr.c - some general use routines +* +* DESCRIPTION +* This file contains some routines which are generally useful for +* EPICS programs. Some routines are for SunOS vs. VxWorks +* compatibility. +* +* SEE ALSO +* genDefs.h +*-***************************************************************************/ +#include + +#ifdef vxWorks +# include +# include +# include +# include +#else +# include +# include +#endif + + +/*+/macro********************************************************************* +* NAME assert - check assertion +* +* DESCRIPTION +* Checks to see if an expression is true. If the expression is false, +* an error message is printed and a signal is raised (SIGABRT or, for +* VxWorks, SIGUSR1). +* +* This is actually a macro provided by genDefs.h . If the preprocessor +* symbol NDEBUG is defined, then the expression isn't evaluated or +* checked. +* +* USAGE +* assert(expression); +* assertAlways(expression); (same as assert(), but isn't disabled +* by defining NDEBUG) +* +* RETURNS +* void +* +* NOTES +* 1. Since evaluation of the expression is conditional (depending on +* whether NDEBUG is defined), it is poor practice to use expressions +* which have side effects. +* 2. assertAlways(0) is recommended rather than abort(). +* +*-*/ +/*---------------------------------------------------------------------------- +* assertFail - handle assertion failure +* For SunOS, call abort(). +* For VxWorks, try to deliver a SIGUSR1 signal. If that's not +* possible, generate a bus error. +*---------------------------------------------------------------------------*/ +int +assertFail(fileName, lineNum) +char *fileName; +int lineNum; +{ + (void)fprintf(stderr, "assertFail: in file %s line %d\n", + fileName, lineNum); +#ifdef vxWorks + if (kill(taskIdSelf(), SIGUSR1) == ERROR) { + int *j; + j = (int *)(-1); + j = (int *)(*j); + } + exit(1); +#else + abort(); +#endif +} + +/*+/subr********************************************************************** +* NAME genMalloc - malloc() and free() 'pre-processor' with "guards" +* +* DESCRIPTION +* These routines support catching overwriting a block of memory +* obtained by malloc(). A 'guard' field is added at the beginning +* and end of the block of memory when genMalloc() is called to +* allocate the block. The 'guard' field is checked by genFree(). +* +* genMalloc() works the same as malloc(), returning a pointer to +* the 'usable' part of the memory block. (I.e., the guard fields are +* 'hidden' from the caller.) +* +* genFree() works the same as free(), except it checks to see if +* the guard field has been clobbered. +* +* A third routine, genBufCheck() allows checking at arbitrary times +* (even from dbx) on whether a block has been clobbered. +* +*****************************************************************************/ + +#if 0 /* a simple demonstration of usage */ +main() +{ + int *x; + int i; + + x = (int *)genMalloc(100); + genBufCheck(x); + for (i=0; i<26; i++) + *(x+i) = i; + genFree(x); + + exit(0); +} +#endif + +# define CHK_MALLOC_FILL_PATTERN 1 + +/*---------------------------------------------------------------------------- +* malloc a block with the following layout, with the block and guard +* fields aligned on longword boundaries +* +* longword 0 nLong for block, including this longword and the +* 'guard' longwords +* longword 1 &longword 0 +* user bytes +* longword n &longword 0 +*---------------------------------------------------------------------------*/ +char *genMalloc(nBytes) +int nBytes; +{ + long *ptrL; + int i; + unsigned nLong, sizeC; + + nLong = 3 + (nBytes+sizeof(long)-1)/sizeof(long); + sizeC = nLong * sizeof(long); + ptrL = (long *)malloc(sizeC); + if (ptrL != NULL) { + for (i=0; i +* #include (or for VxWorks) +* +* jmp_buf mySigEnv; +* +* void myHandler(sigNum) +* int sigNum; +* { +* signal(sigNum, SIG_DFL); +* longjmp(mySigEnv, sigNum); +* } +* +* main() +* { +* int sigNum; +* +* genSigInit(myHandler); +* if ((sigNum = setjmp(mySigEnv)) != 0) +* goto sigOccurred; +* . +* . +* exit(0); +* sigOccurred: +* printf("signal %d detected\n", sigNum); +* exit(1); +* } +* +*-*/ +void +genSigInit(handler) +void (* handler)(); /* I pointer to signal handler */ +{ + (void)signal(SIGTERM, handler); /* SunOS plain kill (not -9) */ + (void)signal(SIGQUIT, handler); /* SunOS ^\ */ + (void)signal(SIGINT, handler); /* SunOS ^C */ + (void)signal(SIGILL, handler); /* illegal instruction */ +#ifndef vxWorks + (void)signal(SIGABRT, handler); /* SunOS assert */ +#else + (void)signal(SIGUSR1, handler); /* VxWorks assert */ +#endif + (void)signal(SIGBUS, handler); /* bus error */ + (void)signal(SIGSEGV, handler); /* segmentation violation */ + (void)signal(SIGFPE, handler); /* arithmetic exception */ + (void)signal(SIGPIPE, handler); /* write to disconnected socket */ +} + +#ifdef vxWorks +/*+/subr********************************************************************** +* NAME perror - print message corresponding to errno +* +* DESCRIPTION +* Under VxWorks, provides the capability provided by perror() under +* UNIX. (This routine isn't present in genLib except for VxWorks +* version.) +* +*-*/ +perror(str) +char *str; /* I string to print in conjunction with error message */ +{ + if (str != NULL) + (void)printf("%s: ", str); + printErrno(0); +} +#endif