382 lines
12 KiB
C++
Executable File
382 lines
12 KiB
C++
Executable File
// -----------------------------------------------------------------------------
|
|
// Copyright (c) 1995 Southeastern Universities Research Association,
|
|
// Continuous Electron Beam Accelerator Facility
|
|
//
|
|
// This software was developed under a United States Government license
|
|
// described in the NOTICE file included as part of this distribution.
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
//
|
|
// Description:
|
|
// This header file contains the class definitions for the classes
|
|
// associated with the construction of a script service.
|
|
//
|
|
// Author: Walt Akers
|
|
//
|
|
// Revision History:
|
|
// ScriptService.cc,v
|
|
// Revision 1.3 1997/02/14 20:43:41 akers
|
|
// Ongoing improvement
|
|
//
|
|
// Revision 1.2 1997/02/14 20:06:32 akers
|
|
// Ongoing improvement
|
|
//
|
|
// Revision 1.1 1997/02/11 17:37:27 akers
|
|
// Directory restructure
|
|
//
|
|
// Revision 1.3 1997/01/31 18:51:27 akers
|
|
// Ongoing development
|
|
//
|
|
// Revision 1.2 1997/01/31 16:21:24 akers
|
|
// Ongoing development
|
|
//
|
|
// Revision 1.1 1997/01/30 20:35:35 akers
|
|
// Initial installation of Script Service
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#include <signal.h>
|
|
|
|
#include "ScriptService.h"
|
|
#include "cdevDirectory.h"
|
|
#include "cdevClock.h"
|
|
#include "ScriptList.h"
|
|
|
|
cdevSelector ScriptList::selector;
|
|
|
|
// ****************************************************************************
|
|
// * This function is an interrupt handler that will be executed whenever the
|
|
// * program receives a SIGCHLD signal. When called it will execute the poll
|
|
// * method of the default server, in order to remove any dead Script Service
|
|
// * processes.
|
|
// ****************************************************************************
|
|
static void SIGCHLD_handler (int)
|
|
{
|
|
ScriptList::selector.insertEvent();
|
|
}
|
|
|
|
|
|
// *********************************************************************
|
|
// * newScriptService :
|
|
// * This method is called by the cdevSystem object to load the
|
|
// * initial instance of the ScriptService.
|
|
// *********************************************************************
|
|
cdevService * newScriptService ( char * name, cdevSystem * system )
|
|
{
|
|
signal(SIGCHLD, SIGCHLD_handler);
|
|
return new ScriptService(name, *system);
|
|
}
|
|
|
|
|
|
// *********************************************************************
|
|
// * ScriptService::ScriptService :
|
|
// * This is the constructor for the script service. It determines
|
|
// * the tag number for the "filename" tag if it does not already
|
|
// * exist and allocates the ScriptList object that will be used
|
|
// * to hold the individual script instances.
|
|
// *********************************************************************
|
|
ScriptService::ScriptService ( char * name, cdevSystem & system )
|
|
: cdevService(name, system),
|
|
FILENAME_TAG(1000),
|
|
asyncCallback(asyncCallbackFunc, NULL)
|
|
{
|
|
scripts = new ScriptList;
|
|
|
|
while(cdevData::tagC2I("filename", &FILENAME_TAG)!=CDEV_SUCCESS)
|
|
{
|
|
cdevData::insertTag(++FILENAME_TAG, "filename");
|
|
}
|
|
}
|
|
|
|
// *********************************************************************
|
|
// * ScriptService::~ScriptService :
|
|
// * This is the destructor for the script service,it does nothing
|
|
// * but act as a placeholder for the virtual destructor.
|
|
// *********************************************************************
|
|
ScriptService::~ScriptService ( void )
|
|
{
|
|
}
|
|
|
|
// *********************************************************************
|
|
// * ScriptService::getRequestObject :
|
|
// * This is the interface that cdev objects will use to obtain a
|
|
// * ScriptRequestObject object. The ScriptRequestObject
|
|
// * represents a combined device and message pair that is associated
|
|
// * with the ScriptService.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *********************************************************************
|
|
int ScriptService::getRequestObject ( char * device, char * message, cdevRequestObject * &req)
|
|
{
|
|
req = new ScriptRequestObject (device, message, system_);
|
|
return (req ? CDEV_SUCCESS : CDEV_ERROR);
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::getFd
|
|
// * This function will return the list of file descriptors that the
|
|
// * ScriptService is using. This will allow the file descriptors to be
|
|
// * used in global select and poll calls performed at the cdevSystem class
|
|
// * level.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *****************************************************************************
|
|
int ScriptService::getFd ( int * &fd, int & numFd )
|
|
{
|
|
fd = scripts->fdList;
|
|
numFd = scripts->fdCount;
|
|
|
|
return CDEV_SUCCESS;
|
|
}
|
|
|
|
|
|
// *********************************************************************
|
|
// * ScriptService::flush :
|
|
// * This function flushes all communications buffers that the
|
|
// * service may have open.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *********************************************************************
|
|
int ScriptService::flush ( void )
|
|
{
|
|
return CDEV_SUCCESS;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::poll :
|
|
// * This function polls the file descriptors used by the service
|
|
// * until one of them becomes active or a discrete amount of time
|
|
// * has expired.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *****************************************************************************
|
|
int ScriptService::poll ( void )
|
|
{
|
|
if(scripts->fdCount>0) scripts->poll();
|
|
|
|
return CDEV_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::pend :
|
|
// * Pends until the named file descriptor (or any file descriptor
|
|
// * if fd = -1) is ready. Will pend forever if the descriptor does
|
|
// * not become active.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *****************************************************************************
|
|
int ScriptService::pend ( int )
|
|
{
|
|
while(scripts->fdCount>0 && scripts->poll()==0);
|
|
|
|
return CDEV_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::pend :
|
|
// * Pends until the named file descriptor (or any file descriptor
|
|
// * if fd = -1) is ready. Will pend for no longer than the user
|
|
// * specified number of seconds.
|
|
// *
|
|
// * Returns CDEV_SUCCESS on success or CDEV_ERROR on error.
|
|
// *****************************************************************************
|
|
int ScriptService::pend ( double seconds, int )
|
|
{
|
|
if(scripts->fdCount > 0)
|
|
{
|
|
int nHandled = 0;
|
|
cdevTimeValue sec = seconds;
|
|
cdevClock timer;
|
|
|
|
timer.schedule(NULL, sec);
|
|
|
|
do {
|
|
nHandled = scripts->poll();
|
|
} while(!nHandled &&
|
|
!timer.expired() &&
|
|
scripts->fdCount>0);
|
|
}
|
|
return CDEV_SUCCESS;
|
|
}
|
|
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::submit :
|
|
// * This is the mechanism that the request object will use to submit a
|
|
// * message to the service. It is important to note that all of the
|
|
// * data provided to this object becomes the property of the service and
|
|
// * must not be accessed afterwords.
|
|
// *****************************************************************************
|
|
int ScriptService::submit ( cdevTranObj &xobj, cdevData &data )
|
|
{
|
|
int status = CDEV_SUCCESS;
|
|
|
|
// *********************************************************************
|
|
// * Attempt to read the script filename from the DDL file.
|
|
// *********************************************************************
|
|
ScriptData *script = new ScriptData(xobj);
|
|
cdevData temp;
|
|
char DDL_request[256];
|
|
|
|
sprintf (DDL_request,
|
|
"resolveServiceData %s %s",
|
|
xobj.reqObj_->device().name(),
|
|
xobj.reqObj_->message());
|
|
|
|
if((system_.nameServer()).send(DDL_request, NULL, &temp)==CDEV_SUCCESS)
|
|
{
|
|
*DDL_request = 0;
|
|
temp.get (FILENAME_TAG, DDL_request, 256);
|
|
}
|
|
else *DDL_request = 0;
|
|
|
|
// *********************************************************************
|
|
// * If the script file was loaded, then begin the process of creating
|
|
// * a pipe to retrieve the output of the script, and forking off an
|
|
// * additional process to handle execution.
|
|
// *********************************************************************
|
|
if(*DDL_request)
|
|
{
|
|
char * parms = NULL;
|
|
int bufLen = 0;
|
|
|
|
// *************************************************************
|
|
// * Extract the data from the user provided output cdevData
|
|
// * object into a string that can be passed to the script.
|
|
// *************************************************************
|
|
ScriptData::data2Buffer(data, parms, bufLen);
|
|
|
|
// *************************************************************
|
|
// * Make a copy standard output for later use to restore the
|
|
// * standard output file descriptor in the main thread...
|
|
// * This is because the behaviour of vfork on some systems
|
|
// * will leave the file descriptor in the main thread modified.
|
|
// *************************************************************
|
|
int fd = dup(1);
|
|
|
|
// *************************************************************
|
|
// * Set the standard output file descriptor to the write file
|
|
// * descriptor of the socket pair.
|
|
// *************************************************************
|
|
dup2(script->sp.writeFD, 1);
|
|
|
|
// *************************************************************
|
|
// * Fork the process to allow the user application to continue
|
|
// * running in the original branch, and the script to be
|
|
// * executed in the new (child) process.
|
|
// *************************************************************
|
|
switch((script->process = vfork()))
|
|
{
|
|
// *****************************************************
|
|
// * 0 Indicates that we are in the new child process.
|
|
// *****************************************************
|
|
case 0:
|
|
{
|
|
if(bufLen)
|
|
{
|
|
execl(DDL_request,
|
|
DDL_request,
|
|
xobj.reqObj_->device().name(),
|
|
xobj.reqObj_->message(),
|
|
parms, NULL);
|
|
}
|
|
else {
|
|
execl(DDL_request,
|
|
DDL_request,
|
|
xobj.reqObj_->device().name(),
|
|
xobj.reqObj_->message(),
|
|
NULL);
|
|
}
|
|
exit(-1);
|
|
}
|
|
break;
|
|
|
|
// *****************************************************
|
|
// * -1 Indicates that a new process could not be
|
|
// * forked.
|
|
// *****************************************************
|
|
case -1:
|
|
{
|
|
// *********************************************
|
|
// * Restore the standard output file descriptor
|
|
// *********************************************
|
|
dup2(fd, 1);
|
|
close(fd);
|
|
|
|
system_.reportError(
|
|
CDEV_SEVERITY_ERROR,
|
|
"ScriptService",
|
|
xobj.reqObj_,
|
|
"Failed to fork a new process");
|
|
|
|
script->fireCallback(CDEV_ERROR,
|
|
xobj.userCallback_->userarg(),
|
|
*xobj.reqObj_,
|
|
*script->data);
|
|
|
|
status = CDEV_ERROR;
|
|
delete script;
|
|
}
|
|
break;
|
|
|
|
// *****************************************************
|
|
// * Any other value indicates that we are in the parent
|
|
// * and the value is the process ID of the new child.
|
|
// *****************************************************
|
|
default:
|
|
{
|
|
// *********************************************
|
|
// * Restore the standard output file descriptor
|
|
// *********************************************
|
|
dup2(fd, 1);
|
|
close(fd);
|
|
|
|
// *********************************************
|
|
// * Insert the script into the list.
|
|
// *********************************************
|
|
scripts->insert(*script);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// *************************************************************
|
|
// * Delete parameters (if any).
|
|
// *************************************************************
|
|
if(bufLen) delete parms;
|
|
}
|
|
else {
|
|
system_.reportError(
|
|
CDEV_SEVERITY_ERROR,
|
|
"ScriptService",
|
|
xobj.reqObj_,
|
|
"No filename specified for request %s %s",
|
|
xobj.reqObj_->device().name(),
|
|
xobj.reqObj_->message());
|
|
|
|
script->fireCallback(CDEV_ERROR,
|
|
xobj.userCallback_->userarg(),
|
|
*xobj.reqObj_,
|
|
*script->data);
|
|
status = CDEV_ERROR;
|
|
delete script;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// *****************************************************************************
|
|
// * ScriptService::getNameServer :
|
|
// * This function should obtain the default name server for this object.
|
|
// * It does nothing for now.
|
|
// *****************************************************************************
|
|
int ScriptService::getNameServer(cdevDevice * &ns)
|
|
{
|
|
ns = 0;
|
|
return CDEV_SUCCESS;
|
|
}
|