/*-------------------------------------------------------------------------- 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 #include #include #include #include #include #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; }