- Extended the Hdb adapter to support SICSdata - Made the simulated histogram memory driver work properly when data has been set. - Implemented the hfactory command - Removed hdbcommand which was never finsihed
1137 lines
31 KiB
C
1137 lines
31 KiB
C
/*--------------------------------------------------------------------------
|
|
|
|
All you need to evaluate macros with SICS
|
|
|
|
The implementation for the macro stuff is complex and non intuitive.
|
|
This is the price to pay for adding the extremly powerful and
|
|
strong Tcl-interpreter to SICS. The problem is that Tcl does not
|
|
know anything about connections and our error handling. We have
|
|
to transport this around it. This is done via the unknown mechanism.
|
|
The unknown-command is called any time Tcl does not find a command
|
|
in its command list. Our unknown calls Sics-objects than and
|
|
provides proper connection and Sics-information. As this unknown is
|
|
going to hold the connection info, we need one interpreter per
|
|
connection. In order to transport interrupt conditions (motor
|
|
going bang) we need an interrupt code field in Connection.
|
|
|
|
Mark Koennecke, November 1996
|
|
|
|
Mark Koennecke, April 1999
|
|
where code added.
|
|
|
|
Mark Koennecke, December 1999
|
|
InternalFileEval added
|
|
|
|
Mark Koennecke, May 2004
|
|
Added protected exec called sys.
|
|
|
|
Copyright:
|
|
|
|
Labor fuer Neutronenstreuung
|
|
Paul Scherrer Institut
|
|
CH-5423 Villigen-PSI
|
|
|
|
|
|
The authors hereby grant permission to use, copy, modify, distribute,
|
|
and license this software and its documentation for any purpose, provided
|
|
that existing copyright notices are retained in all copies and that this
|
|
notice is included verbatim in any distributions. No written agreement,
|
|
license, or royalty fee is required for any of the authorized uses.
|
|
Modifications to this software may be copyrighted by their authors
|
|
and need not follow the licensing terms described here, provided that
|
|
the new terms are clearly indicated on the first page of each file where
|
|
they apply.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
|
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
|
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
MODIFICATIONS.
|
|
--------------------------------------------------------------------------*/
|
|
#include "fortify.h"
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <tcl.h>
|
|
#include <sics.h>
|
|
#include "status.h"
|
|
#include "macro.h"
|
|
#include "splitter.h"
|
|
#include "ifile.h"
|
|
#include "Dbg.h"
|
|
#include "servlog.h"
|
|
#include "stringdict.h"
|
|
#include "exeman.h"
|
|
#include "nxcopy.h"
|
|
|
|
#define SICSERROR "005567SICS"
|
|
/*----------------------------------------------------------------------------
|
|
SICS Macro uses the Tcl "unknown" mechanism to access the Sics-objects.
|
|
The Tcl algorithm is to call a command called unknown when it cannot
|
|
match a command with the Tcl-built ins. Sics will now define the unknown
|
|
to call the Sics object invocation mechanism. In order to do this
|
|
properly the unknown needs to know about the connection invoking the
|
|
command and the SicsInterpreter to use. A suitable datastructure will
|
|
be defined below. And a function which be planted into Tcl to do the
|
|
trick.
|
|
-----------------------------------------------------------------------------*/
|
|
#define MAXSTACK 50
|
|
|
|
|
|
struct __SicsUnknown {
|
|
SConnection *pCon[MAXSTACK];
|
|
char *lastUnknown[MAXSTACK];
|
|
int iStack;
|
|
SicsInterp *pInter;
|
|
};
|
|
|
|
static struct __SicsUnknown *pUnbekannt = NULL;
|
|
/*---------------------------------------------------------------------------*/
|
|
int MacroPush(SConnection *pCon)
|
|
{
|
|
assert(pUnbekannt);
|
|
pUnbekannt->iStack++;
|
|
if(pUnbekannt->iStack >= MAXSTACK)
|
|
{
|
|
SCWrite(pCon,"ERROR: Out of Stack in macro.c",eError);
|
|
return 0;
|
|
}
|
|
pUnbekannt->pCon[pUnbekannt->iStack] = pCon;
|
|
return 1;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
int MacroPop(void)
|
|
{
|
|
assert(pUnbekannt);
|
|
pUnbekannt->iStack--;
|
|
if(pUnbekannt->iStack < 1)
|
|
{
|
|
pUnbekannt->iStack = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static int SicsUnknownProc(ClientData pData, Tcl_Interp *pInter,
|
|
int argc, char *argv[])
|
|
{
|
|
struct __SicsUnknown *pSics = NULL;
|
|
char **myarg = NULL;
|
|
int margc;
|
|
SicsInterp *pSinter = NULL;
|
|
SConnection *pCon = NULL;
|
|
CommandList *pCommand = NULL;
|
|
char *lastCommand = NULL, comBuffer[132];
|
|
int iRet = 0,i;
|
|
int iMacro;
|
|
Statistics *old;
|
|
|
|
/* get the datastructures */
|
|
pSics = (struct __SicsUnknown *)pData;
|
|
assert(pSics);
|
|
pSinter = pSics->pInter;
|
|
pCon = pSics->pCon[pSics->iStack];
|
|
lastCommand = pSics->lastUnknown[pSics->iStack];
|
|
pCon->sicsError = 0;
|
|
|
|
assert(pSinter);
|
|
assert(pCon);
|
|
|
|
|
|
/* shorten the argc, argv by one and invoke */
|
|
margc = argc -1;
|
|
myarg = &argv[1];
|
|
|
|
/* find object */
|
|
if(margc < 1)
|
|
{
|
|
Tcl_SetResult(pInter,"No command found",TCL_VOLATILE);
|
|
return TCL_ERROR;
|
|
}
|
|
pCommand = FindCommand(pSinter,myarg[0]);
|
|
if(!pCommand)
|
|
{
|
|
Tcl_AppendResult(pInter,"Object ",myarg[0]," not found",NULL);
|
|
return TCL_ERROR;
|
|
}
|
|
|
|
/* check for endless loop */
|
|
Arg2Text(margc, myarg, comBuffer,131);
|
|
if(lastCommand != NULL)
|
|
{
|
|
if(strcmp(lastCommand,comBuffer) == 0)
|
|
{
|
|
Tcl_AppendResult(pInter,"ERROR: Never ending loop in unknown\n",
|
|
"Offending command: ",comBuffer,
|
|
" Probably Tcl command not found",NULL);
|
|
SCSetInterrupt(pCon,eAbortBatch);
|
|
return TCL_ERROR;
|
|
}
|
|
}
|
|
if (pSics->lastUnknown[pSics->iStack]) free(pSics->lastUnknown[pSics->iStack]);
|
|
pSics->lastUnknown[pSics->iStack] = strdup(comBuffer);
|
|
|
|
/* invoke */
|
|
iMacro = SCinMacro(pCon);
|
|
SCsetMacro(pCon,1);
|
|
old=StatisticsBegin(pCommand->stat);
|
|
iRet = pCommand->OFunc(pCon,pSinter,pCommand->pData,margc, myarg);
|
|
StatisticsEnd(old);
|
|
SCsetMacro(pCon,iMacro);
|
|
/*
|
|
lastUnkown gets deeply stacked with each SICS command exec'd.
|
|
This is not reflected in code. However, lastUnknown has already
|
|
done its job here, so it is safe to do it the way it is done
|
|
*/
|
|
if(pSics->lastUnknown[pSics->iStack] != NULL)
|
|
{
|
|
free(pSics->lastUnknown[pSics->iStack]);
|
|
pSics->lastUnknown[pSics->iStack] = NULL;
|
|
}
|
|
|
|
/* finish */
|
|
if(iRet == 1)
|
|
{
|
|
return TCL_OK;
|
|
}
|
|
else
|
|
{
|
|
Tcl_SetVar(pInter,SICSERROR,"yes",TCL_GLOBAL_ONLY);
|
|
pCon->sicsError = 1;
|
|
return TCL_ERROR;
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static void UnknownKill(ClientData pData)
|
|
{
|
|
struct __SicsUnknown *pU = NULL;
|
|
|
|
pU = (struct __SicsUnknown *)pData;
|
|
if(pU->pCon[0])
|
|
{
|
|
SCDeleteConnection(pU->pCon[0]);
|
|
}
|
|
free(pData);
|
|
pUnbekannt = NULL;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
void KillSicsUnknown(void) {
|
|
if (pUnbekannt) {
|
|
UnknownKill(pUnbekannt);
|
|
pUnbekannt = NULL;
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------
|
|
Implementation of a protected exec command
|
|
--------------------------------------------------------------------------*/
|
|
static pStringDict allowedCommands = NULL;
|
|
/*----------------------------------------------------------------------*/
|
|
int AllowExec(SConnection *pCon, SicsInterp *pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
if(argc < 2)
|
|
{
|
|
SCWrite(pCon,"ERROR: not enough arguments to allowexec",eError);
|
|
return 0;
|
|
}
|
|
if(!SCMatchRights(pCon,usInternal))
|
|
{
|
|
return 0;
|
|
}
|
|
if(allowedCommands == NULL)
|
|
{
|
|
allowedCommands = CreateStringDict();
|
|
if(allowedCommands == NULL)
|
|
{
|
|
SCWrite(pCon,
|
|
"ERROR: not enough memory for list of allowed system commands",
|
|
eError);
|
|
return 0;
|
|
}
|
|
}
|
|
StringDictAddPair(allowedCommands,argv[1],"Allowed!");
|
|
return 1;
|
|
}
|
|
/*--------------------------------------------------------------------*/
|
|
static void KillExec(ClientData data)
|
|
{
|
|
if(allowedCommands != NULL)
|
|
{
|
|
DeleteStringDict(allowedCommands);
|
|
allowedCommands = NULL;
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------
|
|
This is in the Tcl sources
|
|
*/
|
|
extern int Tcl_ExecObjCmd(ClientData data, Tcl_Interp *interp,
|
|
int objc, Tcl_Obj *CONST objv[]);
|
|
/*-----------------------------------------------------------------------*/
|
|
static int ProtectedExec(ClientData clientData, Tcl_Interp *interp,
|
|
int objc, Tcl_Obj *CONST objv[])
|
|
{
|
|
char *test = NULL;
|
|
|
|
if(objc < 2)
|
|
{
|
|
return Tcl_ExecObjCmd(clientData,interp,objc, objv);
|
|
}
|
|
|
|
test = Tcl_GetStringFromObj(objv[1],NULL);
|
|
if(allowedCommands != NULL)
|
|
{
|
|
if(StringDictExists(allowedCommands,test))
|
|
{
|
|
return Tcl_ExecObjCmd(clientData,interp,objc, objv);
|
|
}
|
|
}
|
|
|
|
/*
|
|
if we are here, we are not allowed to invoke this command
|
|
*/
|
|
Tcl_AppendResult(interp,"System command NOT allowed!",NULL);
|
|
return TCL_ERROR;
|
|
}
|
|
/*--------------------------------------------------------------------------
|
|
initialises a Tcl-Interpreter, installs SICS unknown mechanism and kills
|
|
a few dangerous commands from the normal Tcl command set
|
|
----------------------------------------------------------------------------*/
|
|
extern int Nxinter_SafeInit(Tcl_Interp *pTcl); /* from Swig NeXus Tcl interface */
|
|
|
|
Tcl_Interp *MacroInit(SicsInterp *pSics)
|
|
{
|
|
Tcl_Interp *pInter = NULL;
|
|
struct __SicsUnknown *pUnknown = NULL;
|
|
char *pPtr = NULL;
|
|
char *pPtr2 = NULL;
|
|
FILE *fp = NULL;
|
|
char pBueffel[512];
|
|
int iRet;
|
|
|
|
assert(pSics);
|
|
|
|
/* create interpreter and unknown */
|
|
pInter = Tcl_CreateInterp();
|
|
pUnknown = (struct __SicsUnknown *)malloc(sizeof(struct __SicsUnknown));
|
|
if( (!pInter) || (!pUnknown) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* install unknown command */
|
|
memset(pUnknown,0,sizeof(struct __SicsUnknown));
|
|
pUnknown->pCon[0] = SCCreateDummyConnection(pSics);
|
|
pUnknown->iStack = 0;
|
|
pUnknown->pInter = pSics;
|
|
pUnbekannt = pUnknown;
|
|
/* the cast before SicsUnknwonProc is to avoid a warning
|
|
Tcl_CmdProc has a const char instead of char as argument M.Z. */
|
|
Tcl_CreateCommand(pInter,"unknown",(Tcl_CmdProc *)SicsUnknownProc,
|
|
pUnknown, UnknownKill);
|
|
|
|
/* delete dangers */
|
|
Tcl_DeleteCommand(pInter,"exit");
|
|
Tcl_DeleteCommand(pInter,"socket");
|
|
Tcl_DeleteCommand(pInter,"vwait");
|
|
Tcl_DeleteCommand(pInter,"exec");
|
|
|
|
/*
|
|
install protected exec command
|
|
*/
|
|
Tcl_CreateObjCommand(pInter,"exec",ProtectedExec,NULL,KillExec);
|
|
|
|
Nxinter_SafeInit(pInter);
|
|
NXcopy_Init(pInter);
|
|
|
|
return pInter;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
void MacroDelete(Tcl_Interp *pInter)
|
|
{
|
|
Tcl_DeleteInterp(pInter);
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
Find the first word of a command string
|
|
*/
|
|
static void FirstWord(char *pSource, char *pTarget)
|
|
{
|
|
int i, iLength, iStart, iPos;
|
|
|
|
iLength = strlen(pSource);
|
|
|
|
/* find start */
|
|
for(i = 0, iStart = 0; i < iLength; i++)
|
|
{
|
|
if(pSource[i] != ' ')
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
iStart++;
|
|
}
|
|
}
|
|
|
|
/* do a quick check */
|
|
if(iStart >= iLength - 2)
|
|
{
|
|
pTarget[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
/* do the copy */
|
|
for(i = iStart, iPos = 0; i < iLength; i++)
|
|
{
|
|
if(pSource[i] != ' ')
|
|
{
|
|
pTarget[iPos] = pSource[i];
|
|
iPos++;
|
|
}
|
|
else
|
|
{
|
|
pTarget[iPos] = '\0';
|
|
return;
|
|
}
|
|
}
|
|
pTarget[iPos] = '\0';
|
|
return;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static char *pWhere = NULL;
|
|
static char *pFile = NULL;
|
|
|
|
int MacroWhere(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
assert(pCon);
|
|
|
|
if(pWhere)
|
|
{
|
|
SCWrite(pCon,pWhere,eValue);
|
|
}
|
|
return 1;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
void WhereKill(void *pData)
|
|
{
|
|
|
|
if(pWhere)
|
|
{
|
|
free(pWhere);
|
|
pWhere = NULL;
|
|
}
|
|
if(pFile)
|
|
{
|
|
free(pFile);
|
|
pFile = NULL;
|
|
}
|
|
|
|
if(pData)
|
|
KillDummy(pData);
|
|
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int MacroFileEvalNew(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
void *pCom = NULL;
|
|
pCom = FindCommandData(pInter,"exe","ExeManager");
|
|
assert(pCom != NULL);
|
|
|
|
if(argc < 2){
|
|
SCWrite(pCon,"ERROR: no batch buffer to execute specified",
|
|
eError);
|
|
return 0;
|
|
}
|
|
return runExeBatchBuffer(pCom,pCon,pInter,argv[1]);
|
|
}
|
|
/*----------------------------------------------------------------------*/
|
|
int MacroFileEval(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
FILE *fp = NULL;
|
|
char pBueffel[512];
|
|
int iChar;
|
|
int i, iRun;
|
|
Tcl_DString command;
|
|
char *pCom = NULL;
|
|
int iRet;
|
|
Status eOld;
|
|
Tcl_Interp *pTcl = NULL;
|
|
int iLine = 0;
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
pTcl = InterpGetTcl(pInter);
|
|
|
|
/* check authorisation: only users permitted here */
|
|
if(!SCMatchRights(pCon,usUser))
|
|
{
|
|
SCWrite(pCon,"ERROR: Insufficient Privilege to do FileEval",eError);
|
|
return 0;
|
|
}
|
|
|
|
/* open filename */
|
|
if( argc < 2)
|
|
{
|
|
SCWrite(pCon,"ERROR: No filename specified ",eError);
|
|
return 0;
|
|
}
|
|
fp = fopen(argv[1],"r");
|
|
if(!fp)
|
|
{
|
|
sprintf(pBueffel," Failed to open file -> %s <- ",argv[1]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
|
|
/* handle status first */
|
|
eOld = GetStatus();
|
|
SetStatus(eBatch);
|
|
|
|
SICSLogWrite("Evaluating in MacroFileEval",eValue);
|
|
SICSLogWrite(argv[1],eValue);
|
|
if(pFile)
|
|
{
|
|
free(pFile);
|
|
}
|
|
pFile = strdup(argv[1]);
|
|
|
|
/* cycle through file and execute complete commands */
|
|
i = 0;
|
|
Tcl_DStringInit(&command);
|
|
iRun = 1;
|
|
while(iRun)
|
|
{
|
|
iChar = fgetc(fp);
|
|
if(iChar == EOF)
|
|
{
|
|
iChar = (int)'\n';
|
|
iRun = 0;
|
|
}
|
|
if( iChar == (int)'\n' )
|
|
{
|
|
pBueffel[i] = (char)iChar;
|
|
pBueffel[i+1] = '\0';
|
|
Tcl_DStringAppend(&command,pBueffel,-1);
|
|
pCom = Tcl_DStringValue(&command);
|
|
if(Tcl_CommandComplete(pCom))
|
|
{
|
|
SetStatus(eEager);
|
|
FirstWord(pCom,pBueffel);
|
|
if(FindCommand(pInter,pBueffel) != NULL)
|
|
{
|
|
sprintf(pBueffel,"%s:%d>> %s",pFile,iLine,pCom);
|
|
SCWrite(pCon,pBueffel,eValue);
|
|
if(pWhere != NULL)
|
|
{
|
|
free(pWhere);
|
|
}
|
|
pWhere = strdup(pBueffel);
|
|
iLine++;
|
|
}
|
|
iRet = Tcl_Eval(pTcl,pCom);
|
|
SetStatus(eBatch);
|
|
if(iRet != TCL_OK)
|
|
{
|
|
/* write TCL error and check for total interrupt */
|
|
if(Tcl_GetVar(pTcl,SICSERROR,TCL_GLOBAL_ONLY) == NULL)
|
|
{ /* Tcl error */
|
|
if(strlen(pTcl->result) > 2)
|
|
{
|
|
/*
|
|
local copy in order to resolve a valgrind error
|
|
*/
|
|
strncpy(pBueffel,pTcl->result,511);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
}
|
|
pCom = Tcl_DStringValue(&command);
|
|
SCWrite(pCon,"ERROR: in Tcl block:",eError);
|
|
SCWrite(pCon,pCom,eError);
|
|
SCWrite(pCon,"ERROR: end of Tcl error block",eError);
|
|
}
|
|
else /* SICS error */
|
|
{
|
|
Tcl_UnsetVar(pTcl,SICSERROR,TCL_GLOBAL_ONLY);
|
|
/* SCWrite(pCon,pTcl->result,eError); */
|
|
}
|
|
}
|
|
if(SCGetInterrupt(pCon) >= eAbortBatch)
|
|
{
|
|
fclose(fp);
|
|
Tcl_DStringFree(&command);
|
|
SCWrite(pCon,"ERROR: batch processing interrupted",eError);
|
|
SetStatus(eEager);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
SCSetInterrupt(pCon,eContinue);
|
|
}
|
|
Tcl_DStringFree(&command);
|
|
}
|
|
i = 0;
|
|
}
|
|
else
|
|
{
|
|
pBueffel[i] = (char)iChar;
|
|
i++;
|
|
}
|
|
} /* end while */
|
|
|
|
|
|
/* clean up */
|
|
fclose(fp);
|
|
Tcl_DStringFree(&command);
|
|
SetStatus(eOld);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*----------------------------------------------------------------------
|
|
InternalFileEval evaluates a file but on a dummy connection which
|
|
only writes log files. It also configures a log file for output
|
|
which will be the name of the input file but with .log appended.
|
|
This is here in order to support the evaluation of command files
|
|
generated in a specific directory from the WWW-interface. If the latter
|
|
does not get through, this can safely be deleted.
|
|
*/
|
|
|
|
int InternalFileEval(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
SConnection *pIntern = NULL;
|
|
int iRet;
|
|
char *pFil = NULL, *pExt = NULL;
|
|
|
|
|
|
/* we need a filename */
|
|
if( argc < 2)
|
|
{
|
|
SCWrite(pCon,"ERROR: No filename specified ",eError);
|
|
return 0;
|
|
}
|
|
|
|
/* allocate a dummy connection for this */
|
|
pIntern = SCCreateDummyConnection(pInter);
|
|
if(!pInter)
|
|
{
|
|
SCWrite(pCon,"ERROR: out of memory in InternalFileEval",eError);
|
|
return 0;
|
|
}
|
|
SCnoSock(pIntern);
|
|
|
|
/* configure the log file */
|
|
pFil = strdup(argv[1]);
|
|
pExt = strrchr(pFil,(int)'.');
|
|
if(!pExt)
|
|
{
|
|
SCWrite(pCon,"ERROR: no extension found in InternalFileEval",
|
|
eError);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(pExt,".log");
|
|
SCAddLogFile(pIntern,pFil);
|
|
free(pFil);
|
|
}
|
|
|
|
/* invoke the fileeval */
|
|
MacroPush(pIntern);
|
|
iRet = MacroFileEval(pIntern,pInter,pData,argc,argv);
|
|
MacroPop();
|
|
|
|
/* remove our internal connection */
|
|
SCDeleteConnection(pIntern);
|
|
return iRet;
|
|
}
|
|
/*--------------------------------------------------------------------------
|
|
ClientPut is installed as a command to write data to the client from
|
|
a server script. Syntax:
|
|
ClientPut text outputcode
|
|
The output code is optional and and defaults to eStatus.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#include "outcode.c"
|
|
|
|
int ClientPut(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
OutCode eOut = eWarning;
|
|
int i = 0, iCode, iLen;
|
|
int iMacro;
|
|
char *ppCode;
|
|
char *pMessage = NULL;
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
|
|
if(argc < 2)
|
|
{
|
|
SCWrite(pCon,"Insufficient arguments to ClientPut",eError);
|
|
return 0;
|
|
}
|
|
|
|
/* handle optional I/O codes */
|
|
if(argc > 2)
|
|
{
|
|
/* the last one must be the code */
|
|
iCode = argc - 1;
|
|
ppCode = strdup(argv[iCode]);
|
|
strtolower(ppCode);
|
|
while(pCode[i] != NULL)
|
|
{
|
|
if(strcmp(pCode[i],ppCode) == 0)
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if(ppCode)
|
|
{
|
|
free(ppCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = 10;
|
|
iCode = argc;
|
|
}
|
|
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
eOut = eInternal;
|
|
break;
|
|
case 1:
|
|
eOut = eCommand;
|
|
break;
|
|
case 2:
|
|
eOut = eHWError;
|
|
break;
|
|
case 3:
|
|
eOut = eInError;
|
|
break;
|
|
case 4:
|
|
eOut = eStatus;
|
|
break;
|
|
case 5:
|
|
eOut = eValue;
|
|
break;
|
|
case 6:
|
|
eOut = eWarning;
|
|
break;
|
|
case 7:
|
|
eOut = eFinish;
|
|
break;
|
|
case 8:
|
|
eOut = eEvent;
|
|
break;
|
|
case 9:
|
|
eOut = eWarning;
|
|
break;
|
|
case 10:
|
|
eOut = eError;
|
|
break;
|
|
default:
|
|
eOut = eWarning;
|
|
iCode = argc;
|
|
break;
|
|
}
|
|
|
|
/* recombine the message */
|
|
/* find length */
|
|
iLen = 0;
|
|
for(i = 1; i < iCode; i++)
|
|
{
|
|
iLen += strlen(argv[i]);
|
|
}
|
|
pMessage = (char *)malloc((iLen+100)*sizeof(char));
|
|
if(!pMessage)
|
|
{
|
|
SCWrite(pCon,"ERROR: out of memory in clientput",eError);
|
|
return 0;
|
|
}
|
|
memset(pMessage,0,(iLen+100)*sizeof(char));
|
|
Arg2Text(iCode-1,&argv[1],pMessage,(iLen+100)*sizeof(char));
|
|
|
|
/* now write, thereby tunneling macro flag in order to get proper
|
|
write to client and not into interpreter
|
|
*/
|
|
iMacro = SCinMacro(pCon);
|
|
SCsetMacro(pCon,0);
|
|
SCWrite(pCon,pMessage,eOut);
|
|
SCsetMacro(pCon,iMacro);
|
|
if(pMessage)
|
|
{
|
|
free(pMessage);
|
|
}
|
|
return 1;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
int GumPut(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
OutCode eOut = eWarning;
|
|
int i = 0, iCode, iLen;
|
|
int iMacro;
|
|
char *ppCode;
|
|
char *pMessage = NULL;
|
|
commandContext cc;
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
|
|
if(argc < 2)
|
|
{
|
|
SCWrite(pCon,"Insufficient arguments to ClientPut",eError);
|
|
return 0;
|
|
}
|
|
|
|
/* handle optional I/O codes */
|
|
if(argc > 2)
|
|
{
|
|
/* the last one must be the code */
|
|
iCode = argc - 1;
|
|
ppCode = strdup(argv[iCode]);
|
|
strtolower(ppCode);
|
|
while(pCode[i] != NULL)
|
|
{
|
|
if(strcmp(pCode[i],ppCode) == 0)
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if(ppCode)
|
|
{
|
|
free(ppCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = 10;
|
|
iCode = argc;
|
|
}
|
|
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
eOut = eInternal;
|
|
break;
|
|
case 1:
|
|
eOut = eCommand;
|
|
break;
|
|
case 2:
|
|
eOut = eHWError;
|
|
break;
|
|
case 3:
|
|
eOut = eInError;
|
|
break;
|
|
case 4:
|
|
eOut = eStatus;
|
|
break;
|
|
case 5:
|
|
eOut = eValue;
|
|
break;
|
|
case 6:
|
|
eOut = eWarning;
|
|
break;
|
|
case 7:
|
|
eOut = eFinish;
|
|
break;
|
|
case 8:
|
|
eOut = eEvent;
|
|
break;
|
|
case 9:
|
|
eOut = eWarning;
|
|
break;
|
|
case 10:
|
|
eOut = eError;
|
|
break;
|
|
default:
|
|
eOut = eWarning;
|
|
iCode = argc;
|
|
break;
|
|
}
|
|
|
|
/* recombine the message */
|
|
/* find length */
|
|
iLen = 0;
|
|
for(i = 1; i < iCode; i++)
|
|
{
|
|
iLen += strlen(argv[i]);
|
|
}
|
|
pMessage = (char *)malloc((iLen+100)*sizeof(char));
|
|
if(!pMessage)
|
|
{
|
|
SCWrite(pCon,"ERROR: out of memory in clientput",eError);
|
|
return 0;
|
|
}
|
|
memset(pMessage,0,(iLen+100)*sizeof(char));
|
|
Arg2Text(iCode-1,&argv[1],pMessage,(iLen+100)*sizeof(char));
|
|
|
|
/* now write, thereby tunneling macro flag in order to get proper
|
|
write to client and not into interpreter. We also make sure that the device
|
|
is gumput
|
|
*/
|
|
iMacro = SCinMacro(pCon);
|
|
SCsetMacro(pCon,0);
|
|
cc = SCGetContext(pCon);
|
|
strcpy(cc.deviceID,"gumput");
|
|
SCPushContext2(pCon,cc);
|
|
SCWrite(pCon,pMessage,eOut);
|
|
SCPopContext(pCon);
|
|
SCsetMacro(pCon,iMacro);
|
|
if(pMessage)
|
|
{
|
|
free(pMessage);
|
|
}
|
|
return 1;
|
|
}
|
|
/*----------------------------------------------------------------------*/
|
|
int Broadcast(SConnection *pCon, SicsInterp *pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
int iMacro;
|
|
char pBueffel[256];
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
|
|
if(argc < 2)
|
|
{
|
|
SCWrite(pCon,"Insufficient arguments to Broadcast",eError);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* now write, thereby tunneling macro flag in order to get proper
|
|
write to client and not into interpreter
|
|
*/
|
|
Arg2Text(argc-1, &argv[1],pBueffel,255);
|
|
iMacro = SCinMacro(pCon);
|
|
SCsetMacro(pCon,0);
|
|
ServerWriteGlobal(pBueffel,eWarning);
|
|
SCsetMacro(pCon,iMacro);
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------
|
|
This implements a scheme to provide Tcl commands to Tcl. The Tcl commands
|
|
(either procedures or objects) must be defined in a separate file. Than
|
|
you call:
|
|
Publish command UserRights
|
|
This will make the command known to Sics, and user with user rights matching
|
|
the rights initialized may use it.
|
|
|
|
Below the datastructure to hold for each command
|
|
----------------------------------------------------------------------------*/
|
|
typedef struct {
|
|
pObjectDescriptor pDes;
|
|
char *command;
|
|
int iUser;
|
|
} PubTcl, *pPubTcl;
|
|
/*--------------------------------------------------------------------------*/
|
|
static pPubTcl CreatePublish(char *name, int iUser)
|
|
{
|
|
pPubTcl pRes = NULL;
|
|
|
|
pRes = (pPubTcl)malloc(sizeof(PubTcl));
|
|
if(!pRes)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
pRes->pDes = CreateDescriptor("Macro");
|
|
if(!pRes->pDes)
|
|
{
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
pRes->command = strdup(name);
|
|
pRes->iUser = iUser;
|
|
return pRes;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void DeletePublish(void *pData)
|
|
{
|
|
pPubTcl self = NULL;
|
|
|
|
self = (pPubTcl)pData;
|
|
assert(self);
|
|
|
|
if(self->pDes)
|
|
{
|
|
DeleteDescriptor(self->pDes);
|
|
}
|
|
if(self->command)
|
|
{
|
|
free(self->command);
|
|
}
|
|
free(self);
|
|
}
|
|
/*--------------------------------------------------------------------------
|
|
TclAction checks the user rights and than invokes the arguments as a Tcl
|
|
command
|
|
*/
|
|
|
|
static int TclAction(SConnection *pCon,SicsInterp *pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
char pBueffel[1024];
|
|
char *pCommand;
|
|
pPubTcl self = NULL;
|
|
int iRet, length;
|
|
char *pPtr;
|
|
Tcl_Interp *pTcl = NULL;
|
|
|
|
self = (pPubTcl)pData;
|
|
assert(pCon);
|
|
assert(pSics);
|
|
assert(self);
|
|
pTcl = InterpGetTcl(pSics);
|
|
|
|
if(!SCMatchRights(pCon,self->iUser))
|
|
{
|
|
sprintf(pBueffel,"ERROR: you are not authorised to invoke %s",
|
|
argv[0]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 1;
|
|
}
|
|
|
|
/* make a string */
|
|
pCommand = Arg2Tcl0(argc-1,argv+1,pBueffel,sizeof(pBueffel),self->command);
|
|
if (!pCommand) {
|
|
SCWrite(pCon, "ERROR: no more memory", eError);
|
|
return 0;
|
|
}
|
|
iRet = Tcl_Eval(pTcl,pCommand);
|
|
if (pCommand != pBueffel) free(pCommand);
|
|
if(iRet == TCL_OK)
|
|
{
|
|
if(strlen(pTcl->result) > 0){
|
|
SCPrintf(pCon, eStatus, "%s", pTcl->result);
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if(Tcl_GetVar(pTcl,SICSERROR,TCL_GLOBAL_ONLY) != NULL)
|
|
{
|
|
Tcl_UnsetVar(pTcl,SICSERROR,TCL_GLOBAL_ONLY);
|
|
}
|
|
SCPrintf(pCon,eError,"%s",pTcl->result);
|
|
return 0;
|
|
}
|
|
return 1; /* not reached */
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int TclPublish(SConnection *pCon, SicsInterp *pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
pPubTcl pNew = NULL;
|
|
char pBueffel[132];
|
|
int iUser, i, iRet;
|
|
|
|
/* check no of args */
|
|
if(argc < 3)
|
|
{
|
|
sprintf(pBueffel,"ERROR: Insufficient no of arguments to %s",
|
|
argv[0]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
|
|
/* check user rights */
|
|
if(!SCMatchRights(pCon,usMugger))
|
|
{
|
|
sprintf(pBueffel,"ERROR: you are not authorised to use %s",
|
|
argv[0]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* try convert last parameter to user code */
|
|
iUser = decodeSICSPriv(argv[2]);
|
|
if(iUser < 0)
|
|
{
|
|
sprintf(pBueffel,"ERROR: cannot identify %s as a valid user code",
|
|
argv[2]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
|
|
/* check if the macro already exists */
|
|
pNew = FindCommandData(pSics, argv[1], "Macro");
|
|
if (pNew)
|
|
{ /* yes -> overwrite access code */
|
|
pNew->iUser = iUser;
|
|
return 1;
|
|
}
|
|
/* do a job !*/
|
|
pNew = CreatePublish(argv[1],iUser);
|
|
if(!pNew)
|
|
{
|
|
sprintf(pBueffel,"ERROR: memory error in %s",argv[0]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
iRet = AddCommand(pSics,argv[1],TclAction,DeletePublish,(void *)pNew);
|
|
if(!iRet)
|
|
{
|
|
sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
Transact executes a command and sends a TRANSACTIONFINISHED string
|
|
at the end. This is to permit clients to search for this string in
|
|
order to find out when a command has finished.
|
|
*/
|
|
|
|
|
|
|
|
int TransactAction(SConnection *pCon, SicsInterp *pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
char pBuffer[1024];
|
|
char *pCommand;
|
|
int iRet;
|
|
|
|
pCommand = Arg2Tcl(argc-1,&argv[1],pBuffer, sizeof(pBuffer));
|
|
if (!pCommand) {
|
|
SCWrite(pCon,"ERROR: no memory", eError);
|
|
return 0;
|
|
}
|
|
strtolower(argv[0]);
|
|
if(strcmp(argv[0],"fulltransact") == 0){
|
|
SCPrintf(pCon,eError, "TRANSACTIONSTART %s",pCommand);
|
|
}
|
|
iRet = InterpExecute(pSics,pCon,pCommand);
|
|
if (pCommand != pBuffer) free(pCommand);
|
|
SCWrite(pCon,"TRANSACTIONFINISHED",eError);
|
|
return iRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|