- use dig for resolving host names - ascon.c: fix terminator parsing - property callback: change property before callback - logger.c:default for logger period must be the old value instead of 1 - add frappy type history writing - increase max. logreader line length - HIPNONE returns "null" with json protocol - encode strings properly in formatNameValue - fix memory leak in json2tcl - scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty - scriptcontext: remove args for action timestamp - scriptcontext: "que" function will replace an already queued action, e.g. for 'halt - introduced updatestatus script
1069 lines
28 KiB
C
1069 lines
28 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.
|
|
|
|
Markus Zolliker, May 2020
|
|
reduced version for SEA only
|
|
|
|
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 "sicsglobal.h"
|
|
#include "stringdict.h"
|
|
#include "exeman.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;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
SConnection *MacroPeek(void)
|
|
{
|
|
assert(pUnbekannt);
|
|
return pUnbekannt->pCon[pUnbekannt->iStack];
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
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);
|
|
|
|
if(pSics->iStack >= MAXSTACK -1) {
|
|
Tcl_SetResult(pInter,"ERROR: cyclic call or to deep a nesting of SICSUnknown",
|
|
TCL_VOLATILE);
|
|
return 0;
|
|
}
|
|
pSinter = pSics->pInter;
|
|
pCon = pSics->pCon[pSics->iStack];
|
|
lastCommand = pSics->lastUnknown[pSics->iStack];
|
|
|
|
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;
|
|
}
|
|
|
|
/* invoke */
|
|
SCSetSicsError(pCon,0);
|
|
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) {
|
|
Arg2Text(argc-1,&argv[1],comBuffer,sizeof(comBuffer));
|
|
/*
|
|
suppress the sct commands: there is no point in having them in
|
|
the history
|
|
if(strstr(argv[1],"sct") == NULL){
|
|
Log(DEBUG,"history","%s",comBuffer);
|
|
}
|
|
*/
|
|
return TCL_OK;
|
|
} else {
|
|
Tcl_SetVar(pInter, SICSERROR, "yes", TCL_GLOBAL_ONLY);
|
|
SCSetSicsError(pCon,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;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* static Tcl_ObjCmdProc oldExec = NULL; */
|
|
static int (*oldExec)(ClientData clientData,Tcl_Interp *interp,
|
|
int objc,Tcl_Obj *const objv[] ) = NULL;
|
|
/*-----------------------------------------------------------------------*/
|
|
static int ProtectedExec(ClientData clientData, Tcl_Interp * interp,
|
|
int objc, Tcl_Obj * CONST objv[])
|
|
{
|
|
char *test = NULL;
|
|
|
|
if (objc < 2) {
|
|
return oldExec(clientData, interp, objc, objv);
|
|
}
|
|
|
|
test = Tcl_GetStringFromObj(objv[1], NULL);
|
|
if (allowedCommands != NULL) {
|
|
if (StringDictExists(allowedCommands, test)) {
|
|
return oldExec(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
|
|
----------------------------------------------------------------------------*/
|
|
|
|
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;
|
|
Tcl_CmdInfo execInfo;
|
|
|
|
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_GetCommandInfo(pInter,"exec",&execInfo);
|
|
oldExec = execInfo.objProc;
|
|
Tcl_DeleteCommand(pInter, "exec");
|
|
|
|
/*
|
|
install protected exec command
|
|
*/
|
|
Tcl_CreateObjCommand(pInter, "exec", ProtectedExec, NULL, KillExec);
|
|
|
|
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) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, " Failed to open file -> %s <- ", argv[1]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* handle status first */
|
|
|
|
Log(INFO,"com","Evaluating %s in MacroFileEval", argv[1]);
|
|
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') {
|
|
iLine++;
|
|
pBueffel[i] = (char) iChar;
|
|
pBueffel[i + 1] = '\0';
|
|
Tcl_DStringAppend(&command, pBueffel, -1);
|
|
pCom = Tcl_DStringValue(&command);
|
|
if (Tcl_CommandComplete(pCom)) {
|
|
FirstWord(pCom, pBueffel);
|
|
if (FindCommand(pInter, pBueffel) != NULL) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%s:%d>> %s", argv[1], iLine, pCom);
|
|
SCWrite(pCon, pBueffel, eLog);
|
|
if (pWhere != NULL) {
|
|
free(pWhere);
|
|
}
|
|
pWhere = strdup(pBueffel);
|
|
}
|
|
iRet = Tcl_Eval(pTcl, pCom);
|
|
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
|
|
*/
|
|
strlcpy(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);
|
|
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);
|
|
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);
|
|
|
|
|
|
/* 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 eLog.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#include "outcode.h"
|
|
|
|
int ClientPut(SConnection * pCon, SicsInterp * pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
int eOut;
|
|
int i = 0, iCode;
|
|
size_t iLen;
|
|
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;
|
|
eOut = OutCodeFromText(argv[iCode], NULL);
|
|
if (eOut < 0) { /* invalid code: assume it is content */
|
|
iCode = argc;
|
|
}
|
|
} else {
|
|
eOut = eLog;
|
|
iCode = argc;
|
|
}
|
|
|
|
/* recombine the message */
|
|
/* find length */
|
|
iLen = 1;
|
|
for (i = 1; i < iCode; i++) {
|
|
iLen += strlen(argv[i]) + 1;
|
|
}
|
|
pMessage = (char *) malloc(iLen);
|
|
if (!pMessage) {
|
|
SCWrite(pCon, "ERROR: out of memory in clientput", eLogError);
|
|
return 0;
|
|
}
|
|
memset(pMessage, 0, iLen);
|
|
Arg2Text(iCode - 1, &argv[1], pMessage, iLen);
|
|
|
|
/*
|
|
as the outcode is optional we have to test the message in order to get
|
|
proper outcodes for the log
|
|
*/
|
|
if (eOut < 0) { /* code not given */
|
|
eOut = eLog;
|
|
if(strstr(pMessage,"ERROR") != NULL){
|
|
eOut = eLogError;
|
|
}
|
|
if(strstr(pMessage,"WARNING") != NULL){
|
|
eOut = eWarning;
|
|
}
|
|
}
|
|
|
|
SCWrite(pCon, pMessage, eOut);
|
|
if (pMessage) {
|
|
free(pMessage);
|
|
}
|
|
return 1;
|
|
}
|
|
/*-----------------------------------------------------------------
|
|
ClientLog writes to the socket and the command log only
|
|
-------------------------------------------------------------------*/
|
|
int ClientLog(SConnection * pCon, SicsInterp * pInter, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
OutCode eOut = eLog;
|
|
int i = 0, iLen;
|
|
char *pMessage = NULL;
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
|
|
if (argc < 2) {
|
|
SCWrite(pCon, "Insufficient arguments to ClientLog", eError);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* recombine the message */
|
|
/* find length */
|
|
iLen = 0;
|
|
for (i = 1; i < argc; i++) {
|
|
iLen += strlen(argv[i]);
|
|
}
|
|
pMessage = (char *) malloc((iLen + 100) * sizeof(char));
|
|
if (!pMessage) {
|
|
SCWrite(pCon, "ERROR: out of memory in clientlo", eLogError);
|
|
return 0;
|
|
}
|
|
memset(pMessage, 0, (iLen + 100) * sizeof(char));
|
|
Arg2Text(argc - 1, &argv[1], pMessage, (iLen + 100) * sizeof(char));
|
|
|
|
SCLogWrite(pCon,pMessage, eLog);
|
|
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;
|
|
SConnection *conCon = NULL;
|
|
|
|
assert(pCon);
|
|
assert(pInter);
|
|
|
|
if (argc < 2) {
|
|
SCWrite(pCon, "Insufficient arguments to ClientPut", eLogError);
|
|
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;
|
|
case 11:
|
|
eOut = eLog;
|
|
break;
|
|
case 12:
|
|
eOut = eLogError;
|
|
break;
|
|
default:
|
|
eOut = eLog;
|
|
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
|
|
*/
|
|
conCon = SCCopyConnection(pCon);
|
|
if (conCon == NULL) {
|
|
SCWrite(pCon, "ERROR: out of memory in gumput", eError);
|
|
}
|
|
strcpy(conCon->deviceID, "gumput");
|
|
SCWrite(conCon, pMessage, eOut);
|
|
SCDeleteConnection(conCon);
|
|
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", eLog);
|
|
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);
|
|
ServerWriteGlobal(pBueffel, eLog);
|
|
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)) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to invoke %s",
|
|
argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
Tcl_ResetResult(pTcl);
|
|
iRet = Tcl_Eval(pTcl, pCommand);
|
|
if (iRet == TCL_OK) {
|
|
if (strlen(pTcl->result) > 0) {
|
|
SCPrintf(pCon, eValue, "%s", pTcl->result);
|
|
}
|
|
if (pCommand != pBueffel)
|
|
free(pCommand);
|
|
return 1;
|
|
} else {
|
|
if (Tcl_GetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY) != NULL) {
|
|
Tcl_UnsetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY);
|
|
}
|
|
if (strlen(pTcl->result) > 0) {
|
|
SCPrintf(pCon, eError, "ERROR: Tcl reported %s in %s", pTcl->result,
|
|
pCommand);
|
|
}
|
|
if (pCommand != pBueffel)
|
|
free(pCommand);
|
|
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) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: Insufficient no of arguments to %s",
|
|
argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* try convert last parameter to user code */
|
|
iUser = decodeSICSPriv(argv[2]);
|
|
if (iUser < 0) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "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 */
|
|
if (pNew->iUser == iUser) {
|
|
return 1;
|
|
}
|
|
/* check user rights */
|
|
if (!SCMatchRights(pCon, usMugger)) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to use %s", argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
pNew->iUser = iUser;
|
|
return 1;
|
|
}
|
|
|
|
/* check user rights */
|
|
if (!SCMatchRights(pCon, usMugger)) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to use %s", argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
|
|
/* do a job ! */
|
|
pNew = CreatePublish(argv[1], iUser);
|
|
if (!pNew) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: memory error in %s", argv[0]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
iRet =
|
|
AddCommand(pSics, argv[1], TclAction, DeletePublish, (void *) pNew);
|
|
if (!iRet) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "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.
|
|
|
|
MK, May 2016
|
|
|
|
I changed this to write the TRANSACT strings only to the socket and not to the
|
|
log. Because this is clogging the log with useless stuff. If you want to
|
|
debug the protocol you still can sit on the line with sockspy. But I am unsure
|
|
of this change...
|
|
*/
|
|
|
|
|
|
|
|
int TransactAction(SConnection * pCon, SicsInterp * pSics, void *pData,
|
|
int argc, char *argv[])
|
|
{
|
|
char pBuffer[1024];
|
|
char pStartBuffer[1088];
|
|
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) {
|
|
snprintf(pStartBuffer, sizeof(pStartBuffer), "TRANSACTIONSTART %s", pCommand);
|
|
SCPureSockWrite(pCon, pStartBuffer,eLog);
|
|
}
|
|
iRet = InterpExecute(pSics, pCon, pCommand);
|
|
if (pCommand != pBuffer)
|
|
free(pCommand);
|
|
SCPureSockWrite(pCon, "TRANSACTIONFINISHED", eLog);
|
|
return iRet;
|
|
}
|