Files
sics/scriptcontext.c
zolliker a529bda307 - added FindCommandDescriptor to SCinter.*
- modified support for dynamic objects
- improved logger system
- various fixes
2008-03-03 14:49:15 +00:00

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