Files
sics/sea_macro.c
zolliker 61341b52f4 various improvements
- 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
2021-09-16 12:26:18 +02:00

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