Files
epics-base/src/util/cmdClient.c
1991-07-31 18:54:12 +00:00

547 lines
16 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* $Id$
* Author: Roger A. Cole
* Date: 11-26-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:
* -----------------
* .01 11-26-90 rac initial version
* .02 07-30-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 cmdClient.c - general purpose client for command-based servers
*
* DESCRIPTION
* Connects to a text-command-based server.
*
* Usage on SunOS:
* % cmdClient hostName portNum
* or
* execl("cmdClient", "cmdClient", "hostName", "portNum",
* (char *)0);
*
* Usage on VxWorks:
* > cmdClient "hostName",portNum
*
* BUGS
* o need to clean up structure to be more in line with cmdProto's structure
* o the stdout stream from this program contains, intermixed, the
* server's stdout and stderr streams
* o under VxWorks, if the server is on the same IOC, then the IOC itself
* must be added to the host table with hostAdd()
* o this program should use tasks.h to establish priorities, stack
* sizes, etc.
* o not all signals are caught
*-***************************************************************************/
#include <genDefs.h>
#include <genTasks.h>
#include <cmdDefs.h>
#include <ezsSockSubr.h>
#ifdef vxWorks
/*----------------------------------------------------------------------------
* includes and defines for VxWorks compile
*---------------------------------------------------------------------------*/
# include <vxWorks.h>
# include <stdioLib.h>
# include <ctype.h>
# include <strLib.h>
# include <sigLib.h>
# include <setjmp.h>
# include <taskLib.h>
# define MAXPRIO 160
#else
/*----------------------------------------------------------------------------
* includes and defines for Sun compile
*---------------------------------------------------------------------------*/
# include <stdio.h>
# include <ctype.h>
# include <strings.h>
# include <signal.h>
# include <setjmp.h>
# define MAXPRIO 50
#endif
/*/subhead CMDCL_CTX-------------------------------------------------------
* CMDCL_CTX - context information
*
* A cmdcl descriptor is the `master handle' which is used for
* handling business.
*----------------------------------------------------------------------------*/
#define CmdclLock semTake(pglCmdclCtx->semLock)
#define CmdclUnlock semGive(pglCmdclCtx->semLock)
#define CmdclLockCheck semClear(pglCmdclCtx->semLock)
#define CmdclLockInitAndLock semInit(pglCmdclCtx->semLock)
typedef struct {
TASK_ID id; /* ID of task */
int status; /* status of task--initially ERROR */
int stop; /* task requested to stop if != 0 */
int stopped; /* task has stopped if != 0 */
int serviceNeeded; /* task needs servicing */
int serviceDone; /* task servicing completed */
jmp_buf sigEnv; /* environment for longjmp at signal time */
} CMDCL_TASK_INFO;
typedef struct cmdclCtx {
SEM_ID semLock;
CMDCL_TASK_INFO cmdclTaskInfo;
CMDCL_TASK_INFO cmdclInTaskInfo;
int showStack; /* show stack stats on task terminate */
} CMDCL_CTX;
/*-----------------------------------------------------------------------------
* prototypes
*----------------------------------------------------------------------------*/
int cmdcl();
void cmdclCmdProcess();
long cmdclInitAtStartup();
long cmdclInTask();
long cmdclTask();
void cmdclTaskSigHandler();
void cmdclTaskSigInit();
/*-----------------------------------------------------------------------------
* global definitions
*----------------------------------------------------------------------------*/
CX_CMD glCmdclCxCmd;
CX_CMD *pglCmdclCxCmd=NULL;
CMDCL_CTX glCmdclCtx;
CMDCL_CTX *pglCmdclCtx=NULL;
#ifndef vxWorks
main(argc, argv)
int argc; /* number of command line args */
char *argv[]; /* command line args */
{
char *hostName; /* host name for server */
int portNum; /* port number for server */
if (argc != 3) /* must be command and 2 args */
goto mainUsage;
hostName = argv[1];
if (sscanf(argv[2], "%d", &portNum) != 1) {
printf("error scanning port number\n");
goto mainUsage;
}
/*----------------------------------------------------------------------------
* do some lwp initialization:
* o set allowable priorities between 1 and MAXPRIO
* o set up a cache of stacks for MAXTHREAD stacks
*
* A side effect is that this routine is turned into a lwp thread with a
* priority of MAXPRIO.
*---------------------------------------------------------------------------*/
(void)pod_setmaxpri(MAXPRIO);
lwp_setstkcache(100000, 9);
return cmdClient(hostName, portNum);
mainUsage:
printf("Usage: %s serverHostName serverPortNumber\n", argv[0]);
return -1;
}
#endif
/*+/subr**********************************************************************
* NAME cmdClient - shell callable interface for cmdClient
*
* DESCRIPTION
* This routine is the only part of cmdClient which is intended to be
* called directly from the shell. Several functions are performed here:
* o spawn the cmdclTask
* o spawn the cmdclInTask
* o wait until the cmdclInTask quits, then return to the shell. If
* other tasks belonging to cmdClient are being stopped, then this
* routine waits until they, too, are stopped before returning to
* the shell.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o stack size and priority should come from tasks.h
* o there are lots of "holes" in detecting whether tasks exist, are
* suspended, etc.
*
*-*/
int
cmdClient(hostName, portNum)
char *hostName; /* I host name for server */
int portNum; /* I port number for server */
{
long stat; /* status return from calls */
pglCmdclCtx = &glCmdclCtx;
pglCmdclCxCmd = &glCmdclCxCmd;
stat = cmdclInitAtStartup(pglCmdclCtx, pglCmdclCxCmd);
assert(stat == OK);
#ifdef vxWorks
assert(taskNameToId("cmdclTask") == ERROR);
assert(taskNameToId("cmdclInTask") == ERROR);
#endif
pglCmdclCtx->showStack = 0;
pglCmdclCtx->cmdclTaskInfo.status = ERROR;
pglCmdclCtx->cmdclTaskInfo.stop = 0;
pglCmdclCtx->cmdclTaskInfo.stopped = 1;
pglCmdclCtx->cmdclInTaskInfo.status = ERROR;
pglCmdclCtx->cmdclInTaskInfo.stop = 0;
pglCmdclCtx->cmdclInTaskInfo.stopped = 1;
pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 0;
pglCmdclCtx->cmdclInTaskInfo.serviceDone = 1;
/*-----------------------------------------------------------------------------
* cmdclTask
* spawn it
*----------------------------------------------------------------------------*/
pglCmdclCtx->cmdclTaskInfo.id = taskSpawn("cmdclTask", MAXPRIO,
VX_STDIO | VX_FP_TASK, 50000, cmdclTask,
&pglCmdclCxCmd, hostName, portNum);
if (GenTaskNull(pglCmdclCtx->cmdclTaskInfo.id)) {
(void)fprintf(stdout, "error spawning cmdclTask\n");
return ERROR;
}
pglCmdclCtx->cmdclTaskInfo.status = OK;
pglCmdclCtx->cmdclTaskInfo.stopped = 0;
/*-----------------------------------------------------------------------------
* cmdclInTask
* spawn it
*----------------------------------------------------------------------------*/
pglCmdclCtx->cmdclInTaskInfo.id = taskSpawn("cmdclInTask", MAXPRIO,
VX_STDIO | VX_FP_TASK, 50000, cmdclInTask, &pglCmdclCxCmd);
if (GenTaskNull(pglCmdclCtx->cmdclInTaskInfo.id)) {
(void)fprintf(stdout, "error spawning cmdclInTask\n");
return ERROR;
}
pglCmdclCtx->cmdclInTaskInfo.status = OK;
pglCmdclCtx->cmdclInTaskInfo.stopped = 0;
/*-----------------------------------------------------------------------------
* wait for cmdclInTask to exit and then return to the shell. If other
* "cmdClient tasks" are also exiting, wait until their wrapups are complete.
*----------------------------------------------------------------------------*/
while (!pglCmdclCtx->cmdclInTaskInfo.stopped)
taskSleep(SELF, 1, 0);
if (pglCmdclCtx->cmdclTaskInfo.stop) {
while (!pglCmdclCtx->cmdclTaskInfo.stopped)
taskSleep(SELF, 1, 0);
}
return OK;
}
/*+/subr**********************************************************************
* NAME cmdclTask - main processing task for cmdClient
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
*-*/
long
cmdclTask(ppCxCmd, hostName, portNum)
CX_CMD **ppCxCmd;
char *hostName; /* I host name for server */
int portNum; /* I port number for server */
{
long stat;
CX_CMD *pCxCmd;
int serverSock=-1; /* socket connected to server */
char serverBuf[80];
FILE *myIn=NULL;
FILE *myOut=NULL;
char *findNl;
char message[80];
int i;
pCxCmd = *ppCxCmd;
CmdclLockInitAndLock;
CmdclUnlock;
cmdclTaskSigInit();
if (setjmp(pglCmdclCtx->cmdclTaskInfo.sigEnv) != 0)
goto cmdclTaskWrapup;
/*-----------------------------------------------------------------------------
* attempt to connect to the server. If the connection is successful,
* print the message returned by the server. Then open two streams to
* the socket, one for characters from keyboard to server, the other
* for characters from server to stdout.
*----------------------------------------------------------------------------*/
if ((i=ezsConnectToServer(&serverSock, portNum, hostName, serverBuf)) < 0) {
if (i == -1) {
(void)sprintf(message, "connect to %s port:%d", hostName, portNum);
perror(message);
}
else
printf("%s\n", serverBuf);
serverSock = -1;
goto cmdclTaskWrapup;
}
printf("%s\n", serverBuf);
if (ezsFopenToFd(&myIn, &serverSock) < 0) {
perror("myIn");
myIn = NULL;
goto cmdclTaskWrapup;
}
if (ezsFopenToFd(&myOut, &serverSock) < 0) {
perror("myOut");
myOut = NULL;
goto cmdclTaskWrapup;
}
/*----------------------------------------------------------------------------
* "processing loop"
* do the interactions with the server, attempting as much as possible
* to show the messages as they come in from the server.
*---------------------------------------------------------------------------*/
while (!pglCmdclCtx->cmdclTaskInfo.stop) {
if (pglCmdclCtx->cmdclInTaskInfo.serviceNeeded) {
fputs(pCxCmd->line, myOut);
fflush(myOut);
pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 0;
pglCmdclCtx->cmdclInTaskInfo.serviceDone = 1;
}
if (ezsCheckFpRead(myIn)) {
if (fgets(serverBuf, 80, myIn) != NULL) {
if (strncmp(serverBuf, "#p#r#", 5) == 0) {
if ((findNl = index(serverBuf, '\n')) != NULL)
*findNl = '\0';
fputs(serverBuf+5, stdout);
}
else
fputs(serverBuf, stdout);
fflush(stdout);
}
else {
if (strncmp(pCxCmd->line, "quit", 4) == 0)
break;
else if (strncmp(pCxCmd->line, "close", 5) == 0)
break;
printf("server gone (?)\n");
break;
}
}
taskSleep(SELF, 0, 100000); /* wait .1 sec */
}
cmdclTaskWrapup:
if (myIn != NULL)
fclose(myIn);
if (myOut != NULL)
fclose(myOut);
if (serverSock >= 0)
close(serverSock);
while ((*ppCxCmd)->pPrev != NULL)
cmdCloseContext(ppCxCmd);
pCxCmd = *ppCxCmd;
pglCmdclCtx->cmdclInTaskInfo.stop = 1;
while (pglCmdclCtx->cmdclInTaskInfo.stopped == 0) {
taskSleep(SELF, 1, 0);
}
#ifdef vxWorks
if (pglCmdclCtx->showStack)
checkStack(pglCmdclCtx->cmdclTaskInfo.id);
#endif
pglCmdclCtx->cmdclTaskInfo.stopped = 1;
pglCmdclCtx->cmdclTaskInfo.status = ERROR;
return 0;
}
/*+/subr**********************************************************************
* NAME cmdclTaskSig - signal handling and initialization
*
* DESCRIPTION
* These routines set up for the signals to be caught for cmdclTask
* and handle the signals when they occur.
*
* RETURNS
* void
*
* BUGS
* o not all signals are caught
* o under VxWorks, taskDeleteHookAdd isn't used
* o it's not clear how useful it is to catch the signals which originate
* from the keyboard
*
*-*/
void
cmdclTaskSigHandler(signo)
int signo;
{
printf("entered cmdclTaskSigHandler for signal:%d\n", signo);
signal(signo, SIG_DFL);
longjmp(pglCmdclCtx->cmdclTaskInfo.sigEnv, 1);
}
void
cmdclTaskSigInit()
{
(void)signal(SIGTERM, cmdclTaskSigHandler); /* SunOS plain kill (not -9) */
(void)signal(SIGQUIT, cmdclTaskSigHandler); /* SunOS ^\ */
(void)signal(SIGINT, cmdclTaskSigHandler); /* SunOS ^C */
(void)signal(SIGILL, cmdclTaskSigHandler); /* illegal instruction */
#ifndef vxWorks
(void)signal(SIGABRT, cmdclTaskSigHandler); /* SunOS assert */
#else
(void)signal(SIGUSR1, cmdclTaskSigHandler); /* VxWorks assert */
#endif
(void)signal(SIGBUS, cmdclTaskSigHandler); /* bus error */
(void)signal(SIGSEGV, cmdclTaskSigHandler); /* segmentation violation */
(void)signal(SIGFPE, cmdclTaskSigHandler); /* arithmetic exception */
}
/*+/subr**********************************************************************
* NAME cmdclInTask - handle the keyboard and keyboard commands
*
* DESCRIPTION
* Gets input text and passes the input to cmdclTask.
*
* This task exists to avoid the possibility of blocking cmdclTask
* while waiting for operator input.
*
* This task waits for input to be available from the keyboard. When
* an input line is ready, this task does some preliminary processing:
*
* o if the command is control-D, "^D" is echoed and the command is
* changed to "quit"
*
* Then cmdclTask is signalled and this task goes into a sleeping loop
* until cmdclTask signals that it is ready for the next command.
*
* RETURNS
* OK, or
* ERROR
*
*-*/
long
cmdclInTask(ppCxCmd)
CX_CMD **ppCxCmd; /* IO ptr to pointer to command context */
{
/*----------------------------------------------------------------------------
* wait for input from keyboard. When some is received, signal caller,
* wait for caller to process it, and then wait for some more input.
*
* stay in the main loop until .stop flag is set by cmdclTask.
*---------------------------------------------------------------------------*/
while (1) {
while (pglCmdclCtx->cmdclInTaskInfo.serviceDone == 0) {
if (pglCmdclCtx->cmdclInTaskInfo.stop == 1)
goto cmdclInTaskDone;
taskSleep(SELF, 0, 500000); /* sleep .5 sec */
}
cmdRead(ppCxCmd, &pglCmdclCtx->cmdclInTaskInfo.stop);
if (pglCmdclCtx->cmdclInTaskInfo.stop == 1)
goto cmdclInTaskDone;
if (strncmp((*ppCxCmd)->line, "quit", 4) == 0) {
char *prompt;
if (*ppCxCmd != (*ppCxCmd)->pCxCmdRoot) {
(void)printf("can't use quit command in source file\n");
(*ppCxCmd)->line[0] = '\0';
}
else {
prompt = (*ppCxCmd)->prompt;
(*ppCxCmd)->prompt = "stop the server (y/n) ? ";
cmdRead(ppCxCmd, &pglCmdclCtx->cmdclInTaskInfo.stop);
if ((*ppCxCmd)->line[0] == 'y' || (*ppCxCmd)->line[0] == 'Y')
(void)strcpy((*ppCxCmd)->line, "quit\n");
else
(*ppCxCmd)->line[0] = '\0';
(*ppCxCmd)->prompt = prompt;
}
}
pglCmdclCtx->cmdclInTaskInfo.serviceDone = 0;
pglCmdclCtx->cmdclInTaskInfo.serviceNeeded = 1;
}
cmdclInTaskDone:
cmdCloseContext(ppCxCmd);
#ifdef vxWorks
if (pglCmdclCtx->showStack)
checkStack(pglCmdclCtx->cmdclInTaskInfo.id);
#endif
pglCmdclCtx->cmdclInTaskInfo.stop = 1;
pglCmdclCtx->cmdclInTaskInfo.stopped = 1;
pglCmdclCtx->cmdclInTaskInfo.status = ERROR;
return;
}
/*+/subr**********************************************************************
* NAME cmdclInitAtStartup - initialization for cmdClient
*
* DESCRIPTION
* Perform several initialization duties:
* o initialize the command context block
*
* RETURNS
* OK, or
* ERROR
*
*-*/
long
cmdclInitAtStartup(pCmdclCtx, pCxCmd)
CMDCL_CTX *pCmdclCtx;
CX_CMD *pCxCmd;
{
pCxCmd->prompt = NULL;
pCxCmd->input = stdin;
pCxCmd->inputName = NULL;
pCxCmd->dataOut = stdout;
pCxCmd->pPrev = NULL;
pCxCmd->pCxCmdRoot = pCxCmd;
return OK;
}