1276 lines
33 KiB
C
1276 lines
33 KiB
C
#include <strings.h>
|
|
#include <tcl.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include "sics.h"
|
|
#include "sicsobj.h"
|
|
#include "initializer.h"
|
|
#include "commandlog.h"
|
|
#include "hipadaba.h"
|
|
#include "sicshipadaba.h"
|
|
#include "dynstring.h"
|
|
#include "ascon.h"
|
|
#include "errormsg.h"
|
|
|
|
/** \file
|
|
* \brief script context implementation (Sct)
|
|
*/
|
|
|
|
/** \brief the maximum hdb path length */
|
|
#define MAX_HDB_PATH 1024
|
|
|
|
typedef enum {
|
|
sct_send_keyword,
|
|
sct_result_keyword,
|
|
sct_command_keyword,
|
|
sct_path_keyword,
|
|
sct_update_keyword,
|
|
sct_take_keyword,
|
|
sct_get_keyword,
|
|
sct_complete_keyword,
|
|
sct_retry_keyword,
|
|
sct_chain_keyword,
|
|
sct_next_keyword,
|
|
sct_steplist_keyword,
|
|
sct_no_keyword
|
|
} SctKeyword;
|
|
|
|
/* keywords must have the same order as SctKeyword */
|
|
static char *sctKeywords[]={
|
|
"send",
|
|
"result",
|
|
"command",
|
|
"path",
|
|
"update",
|
|
"take",
|
|
"get",
|
|
"complete",
|
|
"retry",
|
|
"chain",
|
|
"next",
|
|
"steplist",
|
|
NULL
|
|
};
|
|
|
|
static char *reset_type="";
|
|
|
|
typedef struct SctList SctList;
|
|
|
|
typedef struct SctChain {
|
|
struct SctChain *next;
|
|
char *command;
|
|
SctList *scriptList;
|
|
char *relatedPath;
|
|
ErrMsg *msg;
|
|
int doit;
|
|
char *dynamic;
|
|
} SctChain;
|
|
|
|
/* define SctChainList and their functions */
|
|
#define MC_NAME(T) SctChain##T
|
|
#include "mclist.c"
|
|
|
|
typedef enum {set_type, timed_type, manual_type, no_type} SctScriptListType;
|
|
static char *slTypes[]={"set", "timed", "manual", NULL};
|
|
|
|
struct SctList {
|
|
struct SctList *next;
|
|
char *name;
|
|
SctChain *beginScript;
|
|
SctChain *endScript;
|
|
SctScriptListType type;
|
|
int dirty;
|
|
double interval;
|
|
double lastPeriod;
|
|
SctChain *lastScript;
|
|
SctChainList chains;
|
|
};
|
|
|
|
/* define SctListList and their functions */
|
|
#define MC_NAME(T) SctList##T
|
|
#include "mclist.c"
|
|
|
|
typedef struct Sct Sct;
|
|
|
|
typedef struct SctParData {
|
|
struct SctParData *next;
|
|
Sct *sct;
|
|
SctChain *setChain;
|
|
SctChain *updateChain;
|
|
int pending, inprogress;
|
|
int refCnt;
|
|
SCStore conCtx;
|
|
} SctParData;
|
|
|
|
typedef struct SctNode {
|
|
struct SctNode *next;
|
|
pHdb node;
|
|
} SctNode;
|
|
|
|
struct Sct {
|
|
pObjectDescriptor desc;
|
|
char *name;
|
|
SctListList scriptLists;
|
|
Ascon *ascon;
|
|
char *currentPath; /* path for configuration (sct is owner) */
|
|
char *relatedPath; /* node for scripts (chains are owner) */
|
|
char *cmd;
|
|
char *result;
|
|
char *nextScript;
|
|
SctChain *runningChain;
|
|
int tryCnt;
|
|
SCStore debugConn;
|
|
char *debugCommand;
|
|
int sent;
|
|
SctKeyword operation;
|
|
int killTask;
|
|
int killObj;
|
|
int verbose;
|
|
int taskSteps;
|
|
time_t maxAge;
|
|
};
|
|
|
|
static Sct *sct = NULL;
|
|
|
|
static void SctFreeParData(void *d) {
|
|
SctParData *data=d;
|
|
data->refCnt--;
|
|
if (data->refCnt == 0) {
|
|
if (data->conCtx) SCStoreFree(data->conCtx);
|
|
free(data);
|
|
}
|
|
}
|
|
|
|
static int UpdateNode(SConnection *con, ErrMsgList *e, pHdb node, int argc, char *argv[]) {
|
|
hdbValue newValue;
|
|
static char errtxt[512];
|
|
char *str;
|
|
int status;
|
|
|
|
if (!cloneHdbValue(&node->value, &newValue)) {
|
|
ErrPutMsg(e, NULL, "no memory");
|
|
return 0;
|
|
}
|
|
str = ConcatArgs(argc, argv);
|
|
if (str == NULL) {
|
|
ErrPutMsg(e, NULL, "no memory");
|
|
return 0;
|
|
}
|
|
if (!readHdbValue(&newValue, str, errtxt, sizeof errtxt)) {
|
|
ErrPutMsg(e, errtxt, "conversion failure");
|
|
return 0;
|
|
}
|
|
free(str);
|
|
status = UpdateHipadabaPar(node, newValue, con);
|
|
ReleaseHdbValue(&newValue);
|
|
return status;
|
|
}
|
|
|
|
static int SctFindKeyword(char *name, char *keywords[]) {
|
|
int i;
|
|
for (i=0; keywords[i]!=NULL; i++) {
|
|
if (strcasecmp(name, keywords[i]) == 0) return i;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static SctChain *SctMakeChain(char *command) {
|
|
SctChain *sc;
|
|
|
|
sc = calloc(1, sizeof(*sc));
|
|
if (sc == NULL) return NULL;
|
|
sc->doit = 0;
|
|
sc->dynamic = NULL;
|
|
sc->command = command;
|
|
sc->scriptList = NULL;
|
|
sc->relatedPath = NULL;
|
|
sc->msg = NULL;
|
|
return sc;
|
|
}
|
|
|
|
static void SctSetListDirty(SctList *sl) {
|
|
if (!sl->dirty) {
|
|
sl->dirty = 1;
|
|
if (sl->beginScript) {
|
|
sl->beginScript->doit = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SctSetDirty(SctChain *sc) {
|
|
SctSetListDirty(sc->scriptList);
|
|
sc->doit = 1;
|
|
}
|
|
|
|
static int SctUpdateCallback(void *user, void *conn, pHdb node, hdbValue value) {
|
|
SctParData *data = user;
|
|
SConnection *con;
|
|
char path[MAX_HDB_PATH];
|
|
pDynString str;
|
|
|
|
if (data->inprogress) {
|
|
if (data->sct->operation == sct_complete_keyword) {
|
|
data->inprogress = 0;
|
|
GetHdbPath(node, path, sizeof path);
|
|
con = SCStorePush(data->conCtx);
|
|
str = formatValue(value, node);
|
|
SCPrintf(con, eStatus, "%s = %s", path,
|
|
GetCharArray(str));
|
|
DeleteDynString(str);
|
|
SCStorePop(data->conCtx);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctSetCallback(void *user, void *conn, pHdb node, hdbValue value) {
|
|
SctParData *data = user;
|
|
SConnection *oldCon;
|
|
char path[MAX_HDB_PATH];
|
|
pDynString str;
|
|
pHdb target;
|
|
|
|
if (data->setChain) {
|
|
if (data->pending) {
|
|
GetHdbPath(node, path, sizeof path);
|
|
oldCon = SCStorePush(data->conCtx);
|
|
str = formatValue(value, node);
|
|
SCPrintf(oldCon, eStatus, "target of %s changed to %s", path,
|
|
GetCharArray(str));
|
|
DeleteDynString(str);
|
|
SCStorePop(data->conCtx);
|
|
}
|
|
data->conCtx = SCSave(conn, data->conCtx);
|
|
data->pending = 1;
|
|
SctSetDirty(data->setChain);
|
|
target = GetHipadabaNode(node, "target");
|
|
if (target) {
|
|
UpdateHipadabaPar(target, value, NULL);
|
|
}
|
|
return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int SctReadCallback(void *user, void *conn, pHdb node, hdbValue value) {
|
|
SctParData *data = user;
|
|
|
|
/* not used ? */
|
|
return 1;
|
|
}
|
|
|
|
int SctCallDynamicScript(Sct *sct, char *name, char *script) {
|
|
SctList *sl;
|
|
SctChain *sc;
|
|
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
for (sc = sl->chains.head; sc != NULL; sc = sc->next) { /* don't touch the list pos */
|
|
if (sc->dynamic && strcasecmp(name, sc->dynamic) == 0) {
|
|
if (sc->command) free(sc->command);
|
|
sc->command = script;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int SctExec(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) {
|
|
SctKeyword keyword;
|
|
SctList *sl;
|
|
SctChain *sc;
|
|
SctParData *data;
|
|
pHdb node, target;
|
|
hdbValue newValue;
|
|
pDynString result;
|
|
char *args;
|
|
char path[MAX_HDB_PATH];
|
|
SConnection *dcon;
|
|
|
|
if (sct == NULL) {
|
|
SCPrintf(con, eError, "ERROR: %s may be called only in proper context", argv[0]);
|
|
return 0;
|
|
};
|
|
if (argc < 2) {
|
|
SCPrintf(con, eInError, "ERROR: missing %s subcommand", argv[0]);
|
|
goto quit;
|
|
};
|
|
if (sct->verbose != 0) {
|
|
args = ConcatArgs(argc, argv);
|
|
dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "commnd: %s", args);
|
|
SCStorePop(sct->debugConn);
|
|
free(args);
|
|
args = NULL;
|
|
}
|
|
keyword = SctFindKeyword(argv[1], sctKeywords);
|
|
sct->operation = keyword;
|
|
switch (keyword) {
|
|
case sct_send_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s send <cmd> <script ...>", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (sct->cmd != NULL || sct->nextScript != NULL) {
|
|
SCPrintf(con, eInError, "ERROR: %s busy", argv[0]);
|
|
sct->taskSteps = 0;
|
|
goto quit;
|
|
}
|
|
if (sct->ascon == 0) {
|
|
SCPrintf(con, eInError, "ERROR: %s has no connection", argv[0]);
|
|
goto quit;
|
|
}
|
|
sct->cmd = strdup(argv[2]);
|
|
if (sct->verbose != 0) {
|
|
dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "send : %s", sct->cmd);
|
|
SCStorePop(sct->debugConn);
|
|
}
|
|
AsconWrite(sct->ascon, sct->cmd, 0);
|
|
sct->nextScript = ConcatArgs(argc-3, argv+3);
|
|
if (sct->result) free(sct->result);
|
|
sct->result = NULL;
|
|
break;
|
|
case sct_result_keyword:
|
|
if (!sct->result) {
|
|
SCPrintf(con, eInError, "ERROR: no result available");
|
|
goto quit;
|
|
}
|
|
SCWrite(con, sct->result, eValue);
|
|
goto success;
|
|
case sct_command_keyword:
|
|
if (!sct->cmd) {
|
|
SCPrintf(con, eInError, "ERROR: no command available");
|
|
goto quit;
|
|
}
|
|
SCWrite(con, sct->cmd, eValue);
|
|
goto success;
|
|
case sct_path_keyword:
|
|
if (sct->relatedPath == NULL) {
|
|
SCWrite(con, "ERROR: undefined current path", eInError);
|
|
goto quit;
|
|
}
|
|
SCWrite(con, sct->relatedPath, eValue);
|
|
goto success;
|
|
case sct_complete_keyword:
|
|
/* fall through */
|
|
case sct_update_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s %s <par> <value>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
node = FindHdbNode(sct->relatedPath, argv[2], con);
|
|
if (node == 0) goto quit;
|
|
return UpdateNode(con, AsconGetErrList(sct->ascon), node, argc-3, argv+3);
|
|
case sct_get_keyword:
|
|
case sct_take_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s %s <par>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
node = FindHdbNode(sct->relatedPath, argv[2], con);
|
|
if (node == 0) goto quit;
|
|
memset(&newValue,0,sizeof(hdbValue));
|
|
if (keyword == sct_take_keyword) {
|
|
data = FindHdbCallbackData(node, HCBUPDATE, SctUpdateCallback, sct);
|
|
result = NULL;
|
|
if (data) {
|
|
if (data->pending) {
|
|
data->pending = 0;
|
|
data->inprogress = 1;
|
|
}
|
|
target = GetHipadabaNode(node, "target");
|
|
if (target) {
|
|
result = formatValue(target->value, node);
|
|
}
|
|
}
|
|
} else {
|
|
result = formatValue(node->value, node);
|
|
}
|
|
if (result == NULL) {
|
|
SCPrintf(con, eInError, "ERROR: %s has no target value", node->name);
|
|
goto quit;
|
|
}
|
|
if (sct->verbose != 0) {
|
|
dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "result: %s", GetCharArray(result));
|
|
SCStorePop(sct->debugConn);
|
|
}
|
|
SCWrite(con, GetCharArray(result), eValue);
|
|
DeleteDynString(result);
|
|
goto success;
|
|
case sct_steplist_keyword:
|
|
if (argc != 3) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s stepList <list>", argv[0]);
|
|
goto quit;
|
|
}
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0) {
|
|
SctSetListDirty(sl);
|
|
goto success;
|
|
}
|
|
|
|
}
|
|
goto missingScriptList;
|
|
case sct_retry_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s retry <max try> <message>", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (sct->runningChain == NULL) {
|
|
SCPrintf(con, eInError, "ERROR: no running chain (fatal error)", argv[0]);
|
|
goto quit;
|
|
}
|
|
sct->tryCnt++;
|
|
if (sct->tryCnt >= atoi(argv[2])) {
|
|
SCPrintf(con, eInError, "ERROR: too may retries (%d) after %s",
|
|
sct->tryCnt, ConcatArgs(argc-3, argv+3));
|
|
goto quit;
|
|
}
|
|
sct->nextScript = sct->runningChain->command;
|
|
if (sct->result) free(sct->result);
|
|
sct->result = NULL;
|
|
break;
|
|
case sct_chain_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s chain <name> <script>", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (!SctCallDynamicScript(sct, argv[2], ConcatArgs(argc-3, argv+3))) {
|
|
SCPrintf(con, eInError, "ERROR: dynamic script %s not found", argv[2]);
|
|
goto quit;
|
|
}
|
|
goto success;
|
|
case sct_next_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eInError, "ERROR: should be: %s next <script>", argv[0]);
|
|
goto quit;
|
|
}
|
|
sc = sct->runningChain;
|
|
if (sc == NULL || sc->dynamic == NULL) goto notDynamic;
|
|
if (sc->command) free(sc->command);
|
|
sc->command = ConcatArgs(argc - 2, argv + 2);
|
|
sc->doit = 1;
|
|
break;
|
|
case sct_no_keyword:
|
|
SCPrintf(con, eInError, "ERROR: %s %s: unknown keyword", argv[0], argv[1]);
|
|
goto quit;
|
|
default:
|
|
SCPrintf(con, eInError, "ERROR: %s %s: untreated keyword", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
/* no OK message needed, as this command will never be called from a client */
|
|
success:
|
|
sct->operation = sct_no_keyword;
|
|
return 1;
|
|
missingPar:
|
|
SCPrintf(con, eInError, "ERROR: parameter %s not found", argv[2]);
|
|
goto quit;
|
|
notDynamic:
|
|
SCPrintf(con, eInError, "ERROR: script is not dynamic");
|
|
goto quit;
|
|
missingScriptList:
|
|
SCPrintf(con, eInError, "ERROR: script list %s not found", argv[2]);
|
|
goto quit;
|
|
quit:
|
|
sct->operation = sct_no_keyword;
|
|
return 0;
|
|
}
|
|
|
|
static char *SctChainCmd(char *cmd) {
|
|
static char buf[512];
|
|
|
|
if (sct->runningChain && strcmp(sct->runningChain->command, cmd) != 0) {
|
|
snprintf(buf, sizeof buf, "chain: %s\ncmd : %s\n", sct->runningChain->command, cmd);
|
|
} else {
|
|
snprintf(buf, sizeof buf, "cmd : %s\n", cmd);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static char *SctCall(char *command) {
|
|
char *error;
|
|
Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics);
|
|
int ret, l;
|
|
ErrMsg *msg;
|
|
SConnection *dcon;
|
|
|
|
if (sct->verbose != 0) {
|
|
dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eInError, "\nscript: %s\n", command);
|
|
SCStorePop(sct->debugConn);
|
|
}
|
|
l = strlen(command);
|
|
ret = Tcl_EvalEx(pTcl, command, l, 0);
|
|
error = (char *)Tcl_GetStringResult(pTcl);
|
|
if (ret != TCL_OK && error[0]!='\0') {
|
|
msg = ErrPutMsg(AsconGetErrList(sct->ascon), NULL,
|
|
"%serror: %s", SctChainCmd(command), error);
|
|
if (sct->runningChain) {
|
|
sct->runningChain->msg = msg;
|
|
}
|
|
return error;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *SctCallChain(SctChain *sc) {
|
|
if (sc->command[0] == '\0' || strcmp(sc->command, "0") == 0) {
|
|
return NULL; /* empty script: do nothing */
|
|
}
|
|
sct->runningChain = sc;
|
|
sct->tryCnt = 0;
|
|
sct->relatedPath = sc->relatedPath;
|
|
sc->msg = NULL;
|
|
return SctCall(sc->command);
|
|
}
|
|
|
|
static void SctCallNext(void) {
|
|
char *cmd = sct->nextScript;
|
|
|
|
sct->nextScript = NULL;
|
|
if (SctCall(cmd) != NULL) {
|
|
if (sct->cmd) {
|
|
free(sct->cmd);
|
|
sct->cmd = NULL;
|
|
}
|
|
}
|
|
free(cmd);
|
|
}
|
|
|
|
static int SctTask(void *object) {
|
|
AsconStatus status;
|
|
char *response;
|
|
double period;
|
|
SctList *sl;
|
|
SctChain *sc;
|
|
SConnection *dcon = NULL;
|
|
|
|
if (sct) {
|
|
WriteToCommandLog("SCT>", "forbidden command (like wait) called in script");
|
|
goto quit;
|
|
}
|
|
sct = object;
|
|
if (sct->killObj) {
|
|
sct->taskSteps = 0;
|
|
sct = NULL;
|
|
return 0;
|
|
}
|
|
if (!sct->ascon) { /* no connection */
|
|
goto quit;
|
|
}
|
|
if (sct->taskSteps == 0) {
|
|
goto quit;
|
|
}
|
|
/* Sometimes we do not want to give away control if there is a chance,
|
|
that we can do the next step without waiting. This following label is
|
|
for this purpose */
|
|
recycle:
|
|
status = AsconTask(sct->ascon);
|
|
if (status == AsconFailure) {
|
|
/* decide what to do on failure */
|
|
} else if (status != AsconReady) {
|
|
/* we have to wait, give CPU time to other tasks */
|
|
goto quit;
|
|
}
|
|
if (sct->taskSteps > 0) {
|
|
/* debugging code */
|
|
if (sct->taskSteps == 0) {
|
|
goto quit;
|
|
}
|
|
sct->taskSteps--;
|
|
}
|
|
response = AsconRead(sct->ascon);
|
|
if (response != NULL) {
|
|
/* there was a response, treat it */
|
|
if (sct->verbose != 0) {
|
|
if (dcon == NULL) dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "answer: %s", response);
|
|
}
|
|
if (sct->cmd) free(sct->cmd);
|
|
sct->cmd = NULL;
|
|
if (sct->sent) {
|
|
/* response to a "<controller> send" command */
|
|
sct->sent = 0;
|
|
if (dcon == NULL) dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "response> %s", response);
|
|
free(sct->debugCommand);
|
|
sct->debugCommand = NULL;
|
|
goto recycle;
|
|
} else if (sct->nextScript) {
|
|
/* save the result for the next script */
|
|
sct->result = strdup(response);
|
|
}
|
|
}
|
|
if (sct->debugCommand) {
|
|
/* the "<controller> send" command was called */
|
|
if (sct->sent) {
|
|
/* we are already waiting for ta response */
|
|
goto quit;
|
|
}
|
|
/* let us send the response */
|
|
sct->sent = 1;
|
|
if (sct->verbose != 0) {
|
|
if (dcon == NULL) dcon = SCStorePush(sct->debugConn);
|
|
SCPrintf(dcon, eWarning, "sent : %s", sct->debugCommand);
|
|
}
|
|
AsconWrite(sct->ascon, sct->debugCommand, 0);
|
|
goto recycle;
|
|
}
|
|
if (sct->nextScript) {
|
|
/* treat first the next script of a chain */
|
|
SctCallNext();
|
|
goto recycle; /* may the script has no send command, so let's look what's next */
|
|
}
|
|
/* now look for commands in script lists, lists are ordered by priority */
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (sl->type == set_type) {
|
|
if (sl->dirty) {
|
|
if (sl->beginScript && sl->beginScript->doit) {
|
|
sl->beginScript->doit = 0;
|
|
SctCallChain(sl->beginScript);
|
|
goto recycle;
|
|
}
|
|
sc = SctChainThis(&sl->chains);
|
|
if (sc != NULL && sc->doit) {
|
|
SctChainNext(&sl->chains);
|
|
SctCallChain(sc);
|
|
goto recycle;
|
|
}
|
|
/* look for pending scripts */
|
|
for (sc = SctChainFirst(&sl->chains); sc != NULL; sc = SctChainNext(&sl->chains)) {
|
|
if (sc->doit) break;
|
|
}
|
|
if (sc != NULL) {
|
|
SctChainNext(&sl->chains);
|
|
sc->doit = 0;
|
|
SctCallChain(sc);
|
|
goto recycle; /* there are still pending scripts */
|
|
}
|
|
sl->dirty = 0;
|
|
SctChainFirst(&sl->chains);
|
|
if (sl->endScript) {
|
|
SctCallChain(sl->endScript);
|
|
goto recycle;
|
|
}
|
|
/* go to next scriptlist */
|
|
}
|
|
} else {
|
|
if (sl->type == timed_type) { /* check if timed list is due */
|
|
if (!sl->dirty) {
|
|
period = floor(DoubleTime() / sl->interval);
|
|
if (period != sl->lastPeriod) {
|
|
sl->lastPeriod = period;
|
|
SctSetListDirty(sl);
|
|
}
|
|
}
|
|
}
|
|
if (sl->dirty) {
|
|
if (sl->beginScript && sl->beginScript->doit) {
|
|
SctCallChain(sl->beginScript);
|
|
goto recycle;
|
|
}
|
|
sc = SctChainThis(&sl->chains);
|
|
if (sc != NULL) {
|
|
if (SctChainNext(&sl->chains) == NULL || sl->type == manual_type) {
|
|
sl->dirty = 0;
|
|
}
|
|
SctCallChain(sc);
|
|
goto recycle;
|
|
}
|
|
SctChainFirst(&sl->chains);
|
|
}
|
|
/* go to next scriptlist */
|
|
}
|
|
}
|
|
quit:
|
|
if (sct->debugConn) {
|
|
SCStorePop(sct->debugConn);
|
|
}
|
|
sct = NULL;
|
|
return 1;
|
|
}
|
|
|
|
static void SctKillChain(SctChain *sc) {
|
|
if (sc->command) free(sc->command);
|
|
if (sc->relatedPath) free(sc->relatedPath);
|
|
if (sc->dynamic) free(sc->dynamic);
|
|
free(sc);
|
|
}
|
|
|
|
static void SctKillSL(SctList *sl) {
|
|
if (sl->name) free(sl->name);
|
|
if (sl->beginScript) SctKillChain(sl->beginScript);
|
|
if (sl->endScript) SctKillChain(sl->endScript);
|
|
SctChainDelete(&sl->chains, SctKillChain);
|
|
free(sl);
|
|
}
|
|
|
|
static void SctKill(Sct *sct) {
|
|
SctListDelete(&sct->scriptLists, SctKillSL);
|
|
if (sct->ascon) AsconKill(sct->ascon);
|
|
if (sct->name) free(sct->name);
|
|
if (sct->cmd) free(sct->cmd);
|
|
if (sct->result) free(sct->result);
|
|
if (sct->nextScript) free(sct->nextScript);
|
|
if (sct->debugCommand) free(sct->debugCommand);
|
|
if (sct->debugConn) SCStoreFree(sct->debugConn);
|
|
if (sct->desc) DeleteDescriptor(sct->desc);
|
|
if (sct->currentPath) free(sct->currentPath);
|
|
RemoveSICSInternalCallback(sct);
|
|
free(sct);
|
|
}
|
|
|
|
/* kill sct only after SctKillTask _and_ SctKillObj were invoked */
|
|
|
|
static void SctKillTask(void *object) {
|
|
Sct *sct = object;
|
|
if (sct->killObj) {
|
|
SctKill(sct);
|
|
} else {
|
|
sct->killTask = 1;
|
|
}
|
|
}
|
|
|
|
static void SctKillObj(void *object) {
|
|
Sct *sct = object;
|
|
|
|
if (sct->killTask) {
|
|
SctKill(sct);
|
|
} else {
|
|
sct->killObj = 1;
|
|
}
|
|
}
|
|
|
|
int SctAddNode2Script(Sct *sct, SctChain *sc, pHdb node, SConnection *con) {
|
|
hdbValue val;
|
|
char path[MAX_HDB_PATH];
|
|
pHdbCallback cb;
|
|
SctParData *data;
|
|
pHdb child;
|
|
hdbValue hval;
|
|
|
|
data = FindHdbCallbackData(node, HCBUPDATE, SctUpdateCallback, sct);
|
|
if (data == NULL) {
|
|
data = calloc(1, sizeof(*data));
|
|
assert(data);
|
|
data->pending = 0;
|
|
data->inprogress = 0;
|
|
data->sct = sct;
|
|
data->setChain = NULL;
|
|
data->updateChain = NULL;
|
|
data->conCtx = NULL;
|
|
data->refCnt = 3; /* insert 3 times */
|
|
|
|
cb = MakeHipadabaCallback(SctUpdateCallback, data, SctFreeParData, -1, sct);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, HCBUPDATE, cb);
|
|
|
|
cb = MakeHipadabaCallback(SctSetCallback, data, SctFreeParData, -1, sct);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, HCBSET, cb);
|
|
|
|
cb = MakeHipadabaCallback(SctReadCallback, data, SctFreeParData, -1, sct);
|
|
assert(cb);
|
|
AppendHipadabaCallback(node, HCBREAD, cb);
|
|
} else {
|
|
if (data->sct != sct) {
|
|
SCPrintf(con, eError, "ERROR: node is already attached to another script context controller");
|
|
return 0;
|
|
}
|
|
}
|
|
child = GetHipadabaNode(node, "error");
|
|
if (child == NULL) {
|
|
child = MakeSICSHdbPar("error", usInternal, MakeHdbText(""));
|
|
AddHipadabaChild(node, child, NULL);
|
|
SetHdbProperty(child, "visible", "false");
|
|
}
|
|
if (sc->scriptList->type == set_type) {
|
|
child = GetHipadabaNode(node, "target");
|
|
if (child == NULL) {
|
|
child = MakeSICSHdbPar("target", usInternal, node->value);
|
|
AddHipadabaChild(node, child, NULL);
|
|
SetHdbProperty(child, "visible","false");
|
|
}
|
|
data->setChain = sc;
|
|
} else {
|
|
data->updateChain = sc;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void SctWriteError(ErrMsg *msg, SConnection *con, time_t maxAge) {
|
|
time_t age;
|
|
char timetext[8];
|
|
age = time(NULL) - msg->last;
|
|
if (age < maxAge) {
|
|
if (age < 3600) {
|
|
snprintf(timetext, sizeof timetext, "%2ldm%2.2lds", age/60, age % 60);
|
|
} else if (age < 99*3600) {
|
|
age = age / 60;
|
|
snprintf(timetext, sizeof timetext, "%2ldh%2.2ldm", age/60, age % 60);
|
|
} else {
|
|
snprintf(timetext, sizeof timetext, "%5ldh", age/(24*3600));
|
|
}
|
|
if (msg->data) {
|
|
SCPrintf(con, eStatus, "--- %s ago, cnt %5d %s", timetext, msg->cnt, msg->data);
|
|
} else {
|
|
SCPrintf(con, eStatus, "--- %s ago, cnt %5d", timetext, msg->cnt);
|
|
}
|
|
SCPrintf(con, eStatus, "%s", msg->text, eStatus);
|
|
}
|
|
}
|
|
|
|
static int SctAction(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) {
|
|
Sct *sct = object;
|
|
SctList *sl, *slItem;
|
|
SctChain *sc;
|
|
int type = 0, priv = -1;
|
|
pHdb node, parent;
|
|
char *fmt;
|
|
char *name;
|
|
char *text;
|
|
char path[MAX_HDB_PATH];
|
|
pSICSOBJ sicsobj;
|
|
int i;
|
|
ErrMsgList *list;
|
|
ErrMsg *msg;
|
|
double interval;
|
|
hdbValue val;
|
|
pDynString str;
|
|
int doit;
|
|
|
|
typedef enum {
|
|
connect_keyword,
|
|
path_keyword,
|
|
addscript_keyword,
|
|
addmultiscript_keyword,
|
|
addnode_keyword,
|
|
dynscript_keyword,
|
|
scriptlist_keyword,
|
|
beginscript_keyword,
|
|
endscript_keyword,
|
|
interval_keyword,
|
|
debug_keyword,
|
|
send_keyword,
|
|
chain_keyword,
|
|
list_keyword,
|
|
error_keyword,
|
|
no_keyword
|
|
} Keyword;
|
|
/* keywords must have the same order as Keyword */
|
|
static char *keywords[]={
|
|
"connect",
|
|
"path",
|
|
"addscript",
|
|
"addmultiscript",
|
|
"addnode",
|
|
"dynscript",
|
|
"scriptlist",
|
|
"beginscript",
|
|
"endscript",
|
|
"interval",
|
|
"debug",
|
|
"send",
|
|
"chain",
|
|
"list",
|
|
"error",
|
|
NULL
|
|
};
|
|
Keyword keyword;
|
|
|
|
assert(sct);
|
|
if (argc < 2) {
|
|
SCPrintf(con, eError, "ERROR: missing %s subcommand", argv[0]);
|
|
goto quit;
|
|
};
|
|
keyword = SctFindKeyword(argv[1], keywords);
|
|
sct->operation = keyword;
|
|
switch (keyword) {
|
|
|
|
case dynscript_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eError, "ERROR: should be %s %s <list> <name>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
/* fall through */
|
|
|
|
case addmultiscript_keyword:
|
|
|
|
case addscript_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eError, "ERROR: should be %s %s <list> <par> <script ...>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
if (keyword == dynscript_keyword) {
|
|
node = NULL;
|
|
} else {
|
|
node = FindHdbNode(sct->currentPath, argv[3], con);
|
|
if (node == NULL) {
|
|
goto quit;
|
|
}
|
|
}
|
|
for (sl = SctListFirst(&sct->scriptLists); sl != NULL; sl = SctListNext(&sct->scriptLists)) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0) {
|
|
if (keyword == dynscript_keyword) {
|
|
if (sl->type == set_type) {
|
|
SCPrintf(con, eError, "ERROR: dynscripts may not be added to a set type list");
|
|
goto quit;
|
|
}
|
|
}
|
|
if (argc == 4 && keyword != dynscript_keyword) { /* take last script */
|
|
sc = SctChainThis(&sl->chains);
|
|
if (sc == NULL) {
|
|
SCPrintf(con, eError, "ERROR: script list is empty");
|
|
goto quit;
|
|
}
|
|
} else {
|
|
sc = SctMakeChain(ConcatArgs(argc-4, argv+4));
|
|
assert(sc);
|
|
if (node) {
|
|
GetHdbPath(node, path, sizeof(path));
|
|
sc->relatedPath = strdup(path);
|
|
} else {
|
|
sc->relatedPath = strdup(sct->currentPath);
|
|
}
|
|
sc->scriptList = sl;
|
|
if (keyword == dynscript_keyword) {
|
|
sc->dynamic = strdup(argv[3]);
|
|
}
|
|
SctChainAdd(&sl->chains, sc);
|
|
}
|
|
if (node && keyword == addscript_keyword) {
|
|
if (!SctAddNode2Script(sct, sc, node, con)) goto quit;
|
|
}
|
|
if (sl->type != set_type) {
|
|
sc->doit = 1;
|
|
}
|
|
/* the current script chain and script list is stored in the position of the Lists,
|
|
this is important for the addnode command */
|
|
goto success;
|
|
}
|
|
}
|
|
goto missingScriptList;
|
|
|
|
case addnode_keyword:
|
|
if (argc != 3) {
|
|
SCPrintf(con, eError, "ERROR: should be %s %s <par>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
sl = SctListThis(&sct->scriptLists);
|
|
if (sl == NULL) {
|
|
sc = NULL;
|
|
} else {
|
|
sc = SctChainThis(&sl->chains);
|
|
}
|
|
if (sc == NULL) {
|
|
SCPrintf(con, eError, "ERROR: no script was specified");
|
|
goto quit;
|
|
}
|
|
node = FindHdbNode(sct->currentPath, argv[2], con);
|
|
if (node == NULL) {
|
|
goto quit;
|
|
}
|
|
if (!SctAddNode2Script(sct, sc, node, con)) goto quit;
|
|
goto success;
|
|
|
|
case scriptlist_keyword:
|
|
if (argc < 4) {
|
|
type == no_type;
|
|
} else {
|
|
type = SctFindKeyword(argv[2], slTypes);
|
|
}
|
|
if (type == no_type) {
|
|
SCPrintf(con, eError, "ERROR: should be %s scriptList [set|timed|manual] <list>", argv[0]);
|
|
goto quit;
|
|
}
|
|
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0) goto scriptListExists;
|
|
}
|
|
sl = calloc(1, sizeof(*sl));
|
|
assert(sl);
|
|
sl->name = strdup(argv[3]);
|
|
sl->type = type;
|
|
sl->beginScript = NULL;
|
|
sl->endScript = NULL;
|
|
sl->lastPeriod = 0.0;
|
|
sl->lastScript = NULL;
|
|
sl->chains.head = NULL;
|
|
sl->interval = 1e-6;
|
|
sl->dirty = (type == timed_type);
|
|
SctListAdd(&sct->scriptLists, sl);
|
|
break;
|
|
|
|
case interval_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eError, "ERROR: should be %s interval <list> [<value>]", argv[0]);
|
|
goto quit;
|
|
}
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0) break;
|
|
}
|
|
if (sl == NULL) goto scriptListNotFound;
|
|
if (argc > 3) {
|
|
interval = atof(argv[3]);
|
|
if (interval < 1e-6) {
|
|
sl->interval = 1e-6;
|
|
interval = 0;
|
|
} else {
|
|
sl->interval = interval;
|
|
}
|
|
} else {
|
|
interval = sl->interval;
|
|
if (interval <= 1.001e-6) {
|
|
interval = 0;
|
|
}
|
|
}
|
|
SCPrintf(con, eValue, "%s %s = %.5g (sec)", argv[0], argv[1], sl->interval);
|
|
return 1;
|
|
|
|
case beginscript_keyword:
|
|
case endscript_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eError, "ERROR: should be %s %s <list> <script>", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0) break;
|
|
}
|
|
if (sl == NULL) goto scriptListNotFound;
|
|
sc = SctMakeChain(ConcatArgs(argc-3, argv+3));
|
|
sc->relatedPath = strdup(sct->currentPath);
|
|
sc->scriptList = sl;
|
|
if (keyword == beginscript_keyword) {
|
|
sl->beginScript = sc;
|
|
} else {
|
|
sl->endScript = sc;
|
|
}
|
|
break;
|
|
|
|
case connect_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eError, "ERROR: should be %s connect <protocol> <host:port> ...", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (sct->ascon) AsconKill(sct->ascon);
|
|
sct->ascon = AsconMake(con, argc - 2, argv + 2);
|
|
if (!sct->ascon) goto quit;
|
|
break;
|
|
|
|
case path_keyword:
|
|
if (argc > 3) {
|
|
SCPrintf(con, eError, "ERROR: should be %s path [<path>]", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (argc == 3) {
|
|
node = FindHdbNode(sct->currentPath, argv[2], con);
|
|
if (node == NULL) {
|
|
goto quit;
|
|
}
|
|
} else {
|
|
node = FindHdbNode(sct->currentPath, ".", con);
|
|
}
|
|
if (!GetHdbPath(node, path, sizeof path)) {
|
|
SCWrite(con, "ERROR: no such path or path name too long", eError);
|
|
goto quit;
|
|
}
|
|
if (argc == 3 && strcmp(path, sct->currentPath) != 0) {
|
|
free(sct->currentPath);
|
|
sct->currentPath = strdup(path);
|
|
}
|
|
SCWrite(con, sct->currentPath, eValue);
|
|
goto success;
|
|
|
|
case debug_keyword:
|
|
if (argc < 2) {
|
|
SCPrintf(con, eError, "ERROR: should be %s debug [0 | <steps>]", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (argc < 3) {
|
|
i = 1;
|
|
} else {
|
|
i = atoi(argv[2]);
|
|
}
|
|
if (i != 0) {
|
|
sct->taskSteps = i;
|
|
sct->debugConn = SCSave(con, sct->debugConn);
|
|
sct->verbose = 1;
|
|
while (sct->taskSteps > 0) {
|
|
SctTask(sct);
|
|
}
|
|
return 1;
|
|
} else {
|
|
sct->taskSteps = -1;
|
|
sct->verbose = 0;
|
|
}
|
|
break;
|
|
|
|
case list_keyword:
|
|
if (argc < 3) {
|
|
str = CreateDynString(60, 63);
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
DynStringConcatChar(str, ',');
|
|
DynStringConcat(str, sl->name);
|
|
}
|
|
text = GetCharArray(str);
|
|
if (text[0] == ',') text++;
|
|
SCWrite(con, text, eValue);
|
|
DeleteDynString(str);
|
|
} else if (strcasecmp(argv[2], "dynscript") == 0) {
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
doit = 1;
|
|
for (sc = sl->chains.head; sc != NULL; sc = sc->next) { /* don't touch the list pos */
|
|
if (sc->dynamic) {
|
|
if (doit) {
|
|
SCPrintf(con, eStatus, "%s", sl->name);
|
|
doit = 0;
|
|
}
|
|
SCPrintf(con, eStatus, " %s: %s", sc->dynamic, sc->command);
|
|
if (sc->msg) {
|
|
SCPrintf(con, eStatus, "ERROR:");
|
|
SctWriteError(sc->msg, con, sct->maxAge);
|
|
SCPrintf(con, eStatus, " ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (sl = sct->scriptLists.head; sl != NULL; sl = sl->next) {
|
|
if (strcasecmp(sl->name, argv[2]) == 0 || strcasecmp(argv[2], "all") == 0) {
|
|
SCPrintf(con, eStatus, "%s", sl->name);
|
|
for (sc = sl->chains.head; sc != NULL; sc = sc->next) { /* don't touch the list pos */
|
|
if (sc->dynamic) {
|
|
SCPrintf(con, eStatus, " %s: %s", sc->dynamic, sc->command);
|
|
} else {
|
|
SCPrintf(con, eStatus, " %s", sc->command);
|
|
}
|
|
if (sc->msg) {
|
|
SCPrintf(con, eStatus, "ERROR:");
|
|
SctWriteError(sc->msg, con, sct->maxAge);
|
|
SCPrintf(con, eStatus, " ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
goto success;
|
|
|
|
case error_keyword:
|
|
if (argc > 2) {
|
|
sct->maxAge = atoi(argv[2]);
|
|
}
|
|
list = AsconGetErrList(sct->ascon);
|
|
for (msg = ErrMsgFirst(list); msg != NULL; msg = msg->next) {
|
|
SctWriteError(msg, con, sct->maxAge);
|
|
}
|
|
return 1;
|
|
|
|
case send_keyword:
|
|
if (argc < 3) {
|
|
SCPrintf(con, eError, "ERROR: should be %s send <command>", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (sct->debugCommand != NULL) {
|
|
SCPrintf(con, eError, "ERROR: busy");
|
|
goto quit;
|
|
}
|
|
sct->sent = 0;
|
|
sct->debugConn = SCSave(con, sct->debugConn);
|
|
sct->debugCommand = ConcatArgs(argc - 2, argv + 2);
|
|
goto success;
|
|
|
|
|
|
case chain_keyword:
|
|
if (argc < 4) {
|
|
SCPrintf(con, eError, "ERROR: should be: %s chain <name> <script>", argv[0]);
|
|
goto quit;
|
|
}
|
|
if (!SctCallDynamicScript(sct, argv[2], ConcatArgs(argc-3, argv+3))) {
|
|
SCPrintf(con, eError, "ERROR: dynamic script %s not found", argv[2]);
|
|
goto quit;
|
|
}
|
|
goto success;
|
|
|
|
case no_keyword:
|
|
SCPrintf(con, eError, "ERROR: %s %s: unknown keyword", argv[0], argv[1]);
|
|
goto quit;
|
|
|
|
default:
|
|
SCPrintf(con, eError, "ERROR: %s %s: untreated keyword", argv[0], argv[1]);
|
|
goto quit;
|
|
}
|
|
SCSendOK(con);
|
|
success:
|
|
sct->operation = sct_no_keyword;
|
|
return 1;
|
|
missingScriptList:
|
|
SCPrintf(con, eError, "ERROR: script list %s not found", argv[2]);
|
|
goto quit;
|
|
scriptListExists:
|
|
SCWrite(con, "ERROR: script list exists", eError);
|
|
goto quit;
|
|
nodeExists:
|
|
SCPrintf(con, eError, "ERROR: node %s exists", argv[2]);
|
|
goto quit;
|
|
nodeNotFound:
|
|
SCPrintf(con, eError, "ERROR: node %s not found", name);
|
|
goto quit;
|
|
scriptListNotFound:
|
|
SCPrintf(con, eError, "ERROR: script list %s not found", argv[2]);
|
|
goto quit;
|
|
quit:
|
|
sct->operation = sct_no_keyword;
|
|
return 0;
|
|
}
|
|
|
|
static int SctInit(SConnection *con, int argc, char *argv[], int dynamic) {
|
|
/* syntax: makeobject <name> sct */
|
|
|
|
if (sct) {
|
|
SCPrintf(con, eError, "ERROR: script context busy");
|
|
return 0;
|
|
}
|
|
|
|
if (argc != 3) {
|
|
SCPrintf(con, eError, "ERROR: should be: makeobject <name> sct");
|
|
return 0;
|
|
}
|
|
|
|
sct = calloc(1,sizeof(*sct));
|
|
assert(sct);
|
|
sct->desc = CreateDescriptor("sct");
|
|
assert(sct->desc);
|
|
sct->scriptLists.head = NULL;
|
|
sct->cmd = NULL;
|
|
sct->result = NULL;
|
|
sct->nextScript = NULL;
|
|
sct->runningChain = NULL;
|
|
sct->debugConn = NULL;
|
|
sct->debugCommand = NULL;
|
|
sct->verbose = 0;
|
|
sct->taskSteps = -1;
|
|
sct->name = strdup(argv[1]);
|
|
sct->currentPath = strdup("/");
|
|
sct->maxAge = 600;
|
|
if (!AddCommand(pServ->pSics, argv[1], SctAction, SctKillObj, sct)) {
|
|
SCPrintf(con, eError, "%s already exists", argv[1]);
|
|
SctKill(sct);
|
|
goto quit;
|
|
}
|
|
TaskRegister(pServ->pTasker, SctTask, NULL, SctKillTask, sct, 0);
|
|
sct->killObj = 0;
|
|
sct->killTask = 0;
|
|
sct = NULL;
|
|
return 1;
|
|
quit:
|
|
sct = NULL;
|
|
return 0;
|
|
}
|
|
|
|
/** \brief the startup routine */
|
|
void SctStartup(void) {
|
|
MakeDriver("sct", SctInit, 0, "Script Context Driver");
|
|
AddCmd("sct", SctExec);
|
|
}
|