Files
cdev-1.7.2n/extensions/ScriptService/src/ScriptService.cc
2022-12-13 12:44:04 +01:00

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;
}