Initial revision

This commit is contained in:
Janet B. Anderson
1991-06-18 19:01:27 +00:00
parent 11d1f15159
commit 4097019e8f
3 changed files with 1132 additions and 0 deletions

284
src/libCom/cmdSubr.c Normal file
View File

@@ -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 <genDefs.h>
#include <genTasks.h>
#include <cmdDefs.h>
#include <cadef.h>
#ifdef vxWorks
/*----------------------------------------------------------------------------
* includes and defines for VxWorks compile
*---------------------------------------------------------------------------*/
# include <vxWorks.h>
# include <stdioLib.h>
# include <ctype.h>
#else
/*----------------------------------------------------------------------------
* includes and defines for Sun compile
*---------------------------------------------------------------------------*/
# include <stdio.h>
# include <ctype.h>
# include <sys/types.h> /* for 'select' operations */
# include <sys/time.h> /* 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;
}

515
src/libCom/ezsSockSubr.c Normal file
View File

@@ -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 <ezsSockSubr.h>
* 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 <genDefs.h>
#include <ezsSockSubr.h>
#ifdef vxWorks
# include <vxWorks.h>
# include <stdioLib.h>
# include <ioLib.h>
# include <taskLib.h>
# include <types.h>
# include <utime.h>
# include <socket.h>
# include <inetLib.h>
# include <in.h>
# include <ctype.h>
#else
# include <stdio.h>
# include <sys/types.h>
# include <sys/time.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <ctype.h>
#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
}

333
src/libCom/genSubr.c Normal file
View File

@@ -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 <genDefs.h>
#ifdef vxWorks
# include <vxWorks.h>
# include <stdioLib.h>
# include <taskLib.h>
# include <sigLib.h>
#else
# include <stdio.h>
# include <signal.h>
#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<sizeC; )
((char *)ptrL)[i++] = CHK_MALLOC_FILL_PATTERN;
*ptrL = nLong;
*(ptrL+1) = *(ptrL+nLong-1) = (long)ptrL;
return (char *)(ptrL+2);
}
return NULL;
}
void genBufCheck(ptr)
void *ptr;
{
long *ptrL;
unsigned nLong;
assert(ptr != NULL);
ptrL = (long *)ptr - 2;
if (*(ptrL+1) != (long)ptrL) {
(void)fprintf(stderr, "genBufCheck: guard at begin clobbered\n");
assertAlways(0);
}
nLong = *ptrL;
if (*(ptrL+nLong-1) != (long)ptrL) {
(void)fprintf(stderr, "genBufCheck: guard at end clobbered\n");
assertAlways(0);
}
}
void genFree(ptr)
void *ptr;
{
long *ptrL;
genBufCheck(ptr);
ptrL = (long *)ptr - 2;
(void)free((char *)ptrL);
return;
}
/*+/subr**********************************************************************
* NAME genShellCommand - issue a command to the "sh" shell
*
* DESCRIPTION
* Issues a command to the Bourne shell, "sh". The text which results
* from executing the command is returned in the buffer supplied by
* the caller.
*
* RETURNS
* void
*
* NOTES
* 1. Any errors which are explicitly detected by this routine result in
* an error message in the caller's result buffer.
* 2. This routine will result in a SIGCHLD signal, which the caller will
* have to handle.
* 3. If no output results from executing the command, then resultBuf[0]
* will be set to '\0'.
*
*-*/
#ifndef vxWorks
void
genShellCommand(cmdBuf, resultBuf, resultDim)
char *cmdBuf; /* I line to send to shell */
char *resultBuf; /* O buffer to hold result of shell command */
int resultDim; /* I size of result buffer */
{
FILE *shellPipe;
int i;
if ((shellPipe = popen(cmdBuf, "r")) == NULL) {
strcpy(resultBuf, "couldn't issue shell command\n");
return;
}
i = fread(resultBuf, 1, resultDim-1, shellPipe);
resultBuf[i] = '\0';
pclose(shellPipe);
}
#endif
/*+/subr**********************************************************************
* NAME genSigInit - initialize for catching signals
*
* DESCRIPTION
* Set up for catching signals.
*
* RETURNS
* void
*
* BUGS
* o it isn't clear how multiple calls fit in with SunOS lwp library
*
* NOTES
* 1. Under VxWorks, assert() generates SIGUSR1, rather than the
* customary UNIX SIGABRT.
*
* EXAMPLE
* #include <setjmp.h>
* #include <signal.h> (or <sigLib.h> 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