From 3967dc8844f75516b834295ea2f630709aa53108 Mon Sep 17 00:00:00 2001 From: zolliker Date: Wed, 14 May 2008 14:23:16 +0000 Subject: [PATCH] - added new scriptcontext with devser --- ascon.c | 22 +- ascon.h | 3 +- ascon.i | 4 +- conman.c | 12 +- conman.h | 14 +- devser.c | 301 +++++++ devser.h | 113 +++ evcontroller.c | 2 +- evcontroller.i | 2 +- evcontroller.tex | 14 +- evcontroller.w | 2 +- genericcontroller.c | 4 +- hdbqueue.c | 18 +- logger.c | 67 +- logger.h | 2 +- logreader.c | 77 +- logsetup.c | 22 +- make_gen | 2 +- makefile_slinux | 5 +- network.c | 4 +- ofac.c | 2 +- proxy.c | 2 +- remob.c | 2 +- scriptcontext.c | 1929 +++++++++++++++---------------------------- sicsobj.c | 6 +- statusfile.c | 12 +- stringdict.c | 23 + stringdict.h | 4 + 28 files changed, 1307 insertions(+), 1363 deletions(-) create mode 100644 devser.c create mode 100644 devser.h diff --git a/ascon.c b/ascon.c index 72a6043d..a017e1c8 100644 --- a/ascon.c +++ b/ascon.c @@ -52,7 +52,9 @@ static int CreateSocketAdress( double DoubleTime(void) { struct timeval now; - + /* the resolution of this function is usec, if the machine supports this + and the mantissa of a double is 51 bits or more (31 for sec and 20 for micro) + */ gettimeofday(&now, NULL); return now.tv_sec + now.tv_usec / 1e6; } @@ -128,6 +130,7 @@ void AsconStdInit(Ascon *a, char *hostport) { a->fd = -1; a->state = AsconConnectStart; a->timeout = 2.0; /* sec */ + a->reconnectInterval = 10; a->hostport = strdup(hostport); } @@ -399,6 +402,9 @@ Ascon *AsconMake(SConnection *con, int argc, char *argv[]) { a->wrBuffer = CreateDynString(60, 63); a->errList.head = NULL; a->responseValid = 0; + a->timeout = 2.0; + a->reconnectInterval = 10; + a->lastReconnect = 0; return a; } @@ -416,9 +422,10 @@ void AsconKill(Ascon *a) { free(a); } -AsconStatus AsconTask(Ascon *a) { - a->handler(a); - while (1) { +AsconStatus AsconTask(Ascon *a) { + double now; + + while (a->handler(a)) { switch (a->state) { case AsconReading: case AsconWriting: @@ -448,6 +455,11 @@ AsconStatus AsconTask(Ascon *a) { return AsconPending; case AsconFailed: if (a->state <= AsconConnectFailed) { + now = DoubleTime(); + if (now > a->lastReconnect + a->reconnectInterval) { + a->lastReconnect = now; + a->state = AsconConnectStart; + } return AsconUnconnected; } return AsconFailure; @@ -458,8 +470,8 @@ AsconStatus AsconTask(Ascon *a) { return AsconReady; } } - a->handler(a); } + return AsconIdle; } int AsconWrite(Ascon *a, char *command, int noResponse) { diff --git a/ascon.h b/ascon.h index 0378c211..75b417e8 100644 --- a/ascon.h +++ b/ascon.h @@ -1,6 +1,7 @@ #ifndef ASCON_H #define ASCON_H +#include "sics.h" #include "errormsg.h" /** \file @@ -42,7 +43,7 @@ void AsconKill(Ascon *a); */ AsconStatus AsconTask(Ascon *a); -/** \brief write to the connection. allowed only when the state is ascon_ready +/** \brief write to the connection. allowed only when the state is AsconReady * \param a the connection * \param command the command to be sent * \param noResponse 0 normally, 1 if no reponse is expected diff --git a/ascon.i b/ascon.i index a3f3b625..0bffb423 100644 --- a/ascon.i +++ b/ascon.i @@ -62,7 +62,7 @@ struct Ascon { pDynString rdBuffer;/**< read buffer */ pDynString wrBuffer;/**< write buffer */ int wrPos; /**< write buffer position */ - float timeout; /**< read timeout (sec) */ + double timeout; /**< read timeout (sec) */ char *hostport; /**< host:port to connect */ ErrMsgList errList; /**< error message list */ double start; /**< unix time when read was started */ @@ -70,6 +70,8 @@ struct Ascon { int noResponse; /**< no response expected */ int responseValid; /**< a valid response is ready */ AsconHandler handler; /**< handler function */ + double reconnectInterval; /**< reconnect interval */ + double lastReconnect; /**< last connect try */ }; #define ASCON_SELECT_ERROR -1 diff --git a/conman.c b/conman.c index c4dc7b4b..0286422f 100644 --- a/conman.c +++ b/conman.c @@ -2230,7 +2230,7 @@ int SCActive(SConnection *self) return 0; } /*--------------------------------------------------------------------------*/ -SCStore SCSave(SConnection *pCon, SCStore oldStore) { +SCStore *SCSave(SConnection *pCon, SCStore *oldStore) { commandContext cc; if (oldStore == NULL) { @@ -2247,7 +2247,7 @@ SCStore SCSave(SConnection *pCon, SCStore oldStore) { return oldStore; } /*--------------------------------------------------------------------------*/ -SConnection *SCLoad(SCStore conStore) { +SConnection *SCLoad(SCStore *conStore) { SConnection *pCon = NULL; commandContext old; @@ -2266,7 +2266,7 @@ SConnection *SCLoad(SCStore conStore) { return pServ->dummyCon; } /*--------------------------------------------------------------------------*/ -SConnection *SCStorePush(SCStore conStore) { +SConnection *SCStorePush(SCStore *conStore) { SConnection *pCon; pCon = SCLoad(conStore); @@ -2281,7 +2281,7 @@ SConnection *SCStorePush(SCStore conStore) { return pCon; } /*--------------------------------------------------------------------------*/ -void SCStorePop(SCStore conStore) { +void SCStorePop(SCStore *conStore) { SConnection *pCon; pCon = SCLoad(conStore); @@ -2294,13 +2294,13 @@ void SCStorePop(SCStore conStore) { } } /*--------------------------------------------------------------------------*/ -int SCStoreConnected(SCStore conStore) { +int SCStoreConnected(SCStore *conStore) { return (conStore && conStore->pCon && conStore->pCon->ident == conStore->ident); } /*--------------------------------------------------------------------------*/ -void SCStoreFree(SCStore conStore) { +void SCStoreFree(SCStore *conStore) { free(conStore); } /* --------------------------------------------------------------------------*/ diff --git a/conman.h b/conman.h index a26b56bf..aaf0e006 100644 --- a/conman.h +++ b/conman.h @@ -168,24 +168,24 @@ typedef int (*writeFunc)(struct __SConnection *pCon, int ConSicsAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); /******************************** Store ************************************/ -typedef struct SCStore *SCStore; +typedef struct SCStore SCStore; -SCStore SCSave(SConnection *pCon, SCStore oldStore); +SCStore *SCSave(SConnection *pCon, SCStore *oldStore); /* save a connection and its context for later use. */ -SConnection *SCLoad(SCStore conStore); +SConnection *SCLoad(SCStore *conStore); /* check con and return SConnection if still valid or a dummy connection otherwise. */ -SConnection *SCStorePush(SCStore conStore); +SConnection *SCStorePush(SCStore *conStore); /* load connection and push stored context. Must be paired with an SCStorePop command */ -void SCStorePop(SCStore conStore); +void SCStorePop(SCStore *conStore); /* pop context */ -int SCStoreConnected(SCStore conStore); +int SCStoreConnected(SCStore *conStore); /* check if a stored connection is not closed */ -void SCStoreFree(SCStore conStore); +void SCStoreFree(SCStore *conStore); /* free an SCStore */ void KillFreeConnections(void); diff --git a/devser.c b/devser.c new file mode 100644 index 00000000..aa559ec1 --- /dev/null +++ b/devser.c @@ -0,0 +1,301 @@ +#include +#include "ascon.h" +#include "devser.h" + +typedef struct DevAction { + struct DevAction *next; + void *data; + DevActionHandler *hdl; + DevPrio prio; + DevKillActionData *kill; +} DevAction; + +typedef struct SchedHeader { + struct SchedHeader *next; + DevAction *actions; /* list of actions for given interval and prio */ + DevAction *followingAction; + double interval; + double timeDue; + DevPrio prio; +} SchedHeader; + +struct DevSer { + Ascon *asyncConn; /* connection */ + DevAction *current; + int killCurrent; + DevAction *actions; /* the action queue */ + SchedHeader *headers; + int killMe; + int steps; +}; + +static char *devPrio[NumberOfPRIO] = { + "null", "slow", "read", "progress", "write", "halt" +}; + +char *DevPrio2Text(DevPrio prio) { + if (prio <= 0 || prio >= NumberOfPRIO) { + prio = NullPRIO; + } + return devPrio[prio]; +} + +DevPrio DevText2Prio(char *text) { + DevPrio prio; + for (prio = 0; prio < NumberOfPRIO; prio++) { + if (strcasecmp(text, devPrio[prio]) == 0) return prio; + } + return NullPRIO; +} + +static void DevFreeActionList(DevAction *actions) { + DevAction *victim; + while (actions != NULL) { + victim = actions; + actions = victim->next; + if (victim->kill != NULL) victim->kill(victim->data); + free(victim); + } +} + +static void DevKillTask(void *devser) { + free(devser); +} + +DevAction *DevNextAction(DevSer *devser) { + DevPrio prio; + double now; + SchedHeader *header; + + devser->current = NULL; + if (devser->actions) { + prio = devser->actions->prio; + } else { + prio = NullPRIO; + } + now = DoubleTime(); + for (header = devser->headers; + header != NULL && header->prio > prio; + header = header->next) { + if (header->followingAction == NULL) { + if (now >= header->timeDue) { + header->followingAction = header->actions; + header->timeDue = (floor(now / header->interval) + 1) + * header->interval; + } + } + if (header->followingAction != NULL) { + devser->current = header->followingAction; + devser->killCurrent = 0; + header->followingAction = header->followingAction->next; + return devser->current; + } + } + if (devser->actions) { + devser->current = devser->actions; + devser->killCurrent = 1; + devser->actions = devser->actions->next; + } + return devser->current; +} + +int DevQueueTask(void *ds) { + DevSer *devser = ds; + AsconStatus status; + DevAction *action; + char *sendData; + char *replyData; + + if (devser->steps == 0) return 1; + if (devser->killMe) { + return 0; + } + action = devser->current; + if (action == NULL) { + action = DevNextAction(devser); + } + while (action != NULL) { + status = AsconTask(devser->asyncConn); + if (status == AsconFailure) { + /* TODO: error handling */ + } else if (status != AsconReady) { + return 1; + } + if (devser->steps > 0) { /* debugging mode */ + devser->steps--; + } + replyData = AsconRead(devser->asyncConn); + sendData = action->hdl(action->data, replyData); + if (sendData != NULL) { + AsconWrite(devser->asyncConn, sendData, 0); + return 1; + } + if (devser->killCurrent) { + if (action->kill != NULL) action->kill(action->data); + devser->killCurrent = 0; + free(action); + devser->current = NULL; + } + action = DevNextAction(devser); + } + return 1; +} + +DevSer *DevMake(SConnection *con, int argc, char *argv[]) { + DevSer *devser; + Ascon *asyncConn; + + asyncConn = AsconMake(con, argc, argv); + if (!asyncConn) { + return NULL; + } + devser = calloc(1, sizeof(*devser)); + assert(devser); + devser->asyncConn = asyncConn; + devser->current = NULL; + devser->killCurrent = 0; + devser->actions = NULL; + devser->headers = NULL; + devser->killMe = 0; + devser->steps = -1; /* no debugging by default */ + TaskRegister(pServ->pTasker, DevQueueTask, NULL, DevKillTask, devser, 0); + return devser; +} + +void DevDebugMode(DevSer *devser, int steps) { + devser->steps = steps; +} + +DevAction *DevNewAction(void *data, DevActionHandler hdl, + DevKillActionData *killFunc, DevPrio prio) { + DevAction *action; + action = calloc(1, sizeof(*action)); + assert(action); + action->data = data; + action->hdl = hdl; + action->kill = killFunc; + action->prio = prio; + action->next = NULL; + return action; +} + +void DevKill(DevSer *devser) { + SchedHeader *h, *victim; + + if (devser->asyncConn) { + AsconKill(devser->asyncConn); + } + DevFreeActionList(devser->actions); + h = devser->headers; + while (h != NULL) { + victim = h; + h = victim->next; + DevFreeActionList(victim->actions); + free(victim); + } + devser->killMe = 1; +} + +void DevQueue(DevSer *devser, void *actionData, DevPrio prio, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) { + DevAction *action, **ptr2Last; + DevAction *new; + + if (prio <= NullPRIO) prio = NullPRIO + 1; + if (prio >= NumberOfPRIO) prio = NumberOfPRIO - 1; + ptr2Last = &devser->actions; + for (action = devser->actions; action != NULL && action->prio >= prio; action = action->next) { + if (matchFunc(actionData, action->data) && action->hdl == hdl) { + return; /* there is already an identic action */ + } + ptr2Last = &action->next; + } + new = DevNewAction(actionData, hdl, killFunc, prio); + new->next = action; + *ptr2Last = new; +} + +int DevUnschedule(DevSer *devser, void *actionData, + DevActionHandler hdl, DevActionMatch *matchFunc) { + SchedHeader *header = NULL; + DevAction **ptr2Last = NULL; + DevAction *action = NULL; + int cnt=0; + + /* scan through all headers */ + for (header = devser->headers; header != NULL; header = header->next) { + ptr2Last = &header->actions; + for (action = header->actions; action != NULL; action = *ptr2Last) { + if (matchFunc(actionData, action->data) && action->hdl == hdl) { + if (action == header->followingAction) { + /* advance followingAction if equal*/ + header->followingAction = action->next; + } + cnt++; + /* remove from list */ + *ptr2Last = action->next; + if (action->kill != NULL) action->kill(action->data); + free(action); + } else { + ptr2Last = &action->next; + } + } + } + return cnt; +} + +void DevSchedule(DevSer *devser, void *actionData, + DevPrio prio, double interval, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) { + SchedHeader *header = NULL; + SchedHeader **ptr2LastHeader = NULL; + SchedHeader *newHeader; + DevAction *action = NULL; + DevAction **ptr2Last = NULL; + DevAction *newAction; + + if (prio <= NullPRIO) prio = NullPRIO + 1; + if (prio >= NumberOfPRIO) prio = NumberOfPRIO - 1; + DevUnschedule(devser, actionData, hdl, matchFunc); + + newAction = DevNewAction(actionData, hdl, killFunc, prio); + /* find matching header */ + ptr2LastHeader = &devser->headers; + for (header = devser->headers; header != NULL; header = *ptr2LastHeader) { + if (header->prio == newAction->prio && header->interval == interval) { + /* append new action at the tail */ + ptr2Last = &header->actions; + for (action = header->actions; action != NULL; action=action->next) { + ptr2Last = &action->next; + } + *ptr2Last = newAction; + assert(newAction->next == NULL); + return; + } else if (header->prio < newAction->prio || + (header->prio == newAction->prio + && header->interval > interval)) { + break; + } + if (header->actions == NULL) { + /* remove empty header */ + *ptr2LastHeader = header->next; + free(header); + } else { + ptr2LastHeader = &header->next; + } + } + + /* insert new header */ + newHeader = calloc(1, sizeof(*newHeader)); + assert(newHeader); + newHeader->actions = newAction; + newHeader->followingAction = NULL; + newHeader->prio = newAction->prio; + newHeader->interval = interval; + newHeader->next = header; + newHeader->timeDue = DoubleTime() + interval; + *ptr2LastHeader = newHeader; + return; +} diff --git a/devser.h b/devser.h new file mode 100644 index 00000000..f0a4b722 --- /dev/null +++ b/devser.h @@ -0,0 +1,113 @@ +#ifndef DEVSER_H +#define DEVSER_H + +/** \file + * \brief Device Serializer + */ + +typedef struct DevSer DevSer; + +/** \brief The action handler to be called + * \param actionData the data stored with the action + * \param lastReply the last reply or NULL when no command was + * sent in the last action + * \return the command to be sent or NULL if no command has to be sent + */ +typedef char *DevActionHandler(void *actionData, char *lastReply); + +/** \brief Check if two actions match + * \param actionData1 the first action data + * \param actionData2 the second action data + * \return 1 on a match, 0 on no match + */ +typedef int DevActionMatch(void *actionData1, void *actionData2); + +/** \brief Kill ActionData + * \param actionData action data + */ +typedef void DevKillActionData(void *actionData); + +/** \brief possible priorities. + * NullPRIO and NumberOfPRIO must not be used as priority + */ +typedef enum { + NullPRIO, SlowPRIO, ReadPRIO, ProgressPRIO, WritePRIO, HaltPRIO, NumberOfPRIO +} DevPrio; + +/** \brief Make a new device serializer and async connection. + * \param con the SICS connection (for error messages) + * \param argc the number of args for specifying the protocol + * \param argv the args + * \return the created device serializer or NULL on failure + */ +DevSer *DevMake(SConnection *con, int argc, char *argv[]); + +/** \brief put the device serializer into debug mode + * \param devser the device serializer + * \param steps the number of steps to be executed or -1 for disable debugging mode + */ +void DevDebugMode(DevSer *devser, int steps); + +/** \brief Kill the device serializer and its async connection. + * \param devser the device serializer + */ +void DevKill(DevSer *devser); + +/** \brief Queue an action + * + * If a matching action with the same action handler + * exists already, no new action is queued. + * \param devser the device serializer + * \param actionData the action data + * \param prio the priority + * \param hdl the action handler + * \param matchFunc the match function + * \param killFunc the action data kill function (called from DevKill and + * after the action has finished, i.e. when hdl returned NULL) + * or NULL if no kill function is needed. + */ +void DevQueue(DevSer *devser, void *actionData, DevPrio prio, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) ; + +/** \brief Schedule a periodic action + * + * If a matching action exists already, + * it is overwritten with a possibly changed interval and priority. + * \param devser the device serializer + * \param actionData the action data + * \param prio the priority + * \param interval the interval in seconds (0 is allowed) + * \param hdl the action handler + * \param matchFunc the match function + * \param killFunc the action data kill function (called from DevKill and + * from DevUnschedule) or NULL if no kill function is needed. + */ +void DevSchedule(DevSer *devser, void *actionData, + DevPrio prio, double interval, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc); + +/** \brief Unschedule matching actions + * \param devser the device serializer + * \param actionData the action data to be compared for a match + * \param hdl the action handler + * \param matchFunc the match function + */ +int DevUnschedule(DevSer *devser, void *actionData, + DevActionHandler hdl, DevActionMatch *matchFunc); + +/** \brief Convert integer priority to text + * \param prio + * \return text + */ +char *DevPrio2Text(DevPrio prio); + +/** \brief Convert text priority to integer + * \param text + * \return prio + */ +DevPrio DevText2Prio(char *text); + + +#endif diff --git a/evcontroller.c b/evcontroller.c index 81c72d6d..3a2e5a7e 100644 --- a/evcontroller.c +++ b/evcontroller.c @@ -376,7 +376,7 @@ } /*---------------------------- Error Handlers --------------------------------*/ -static void ErrWrite(char *txt, SCStore conn) +static void ErrWrite(char *txt, SCStore *conn) { pExeList pExe; SConnection *pCon = NULL; diff --git a/evcontroller.i b/evcontroller.i index d10a59f5..ad57cdbc 100644 --- a/evcontroller.i +++ b/evcontroller.i @@ -41,7 +41,7 @@ int iWarned; int iTcl; int iStop; - SCStore conn; + SCStore *conn; char *creationArgs; char *runScript; void *pPrivate; diff --git a/evcontroller.tex b/evcontroller.tex index 3514e5c8..c51b8ee5 100644 --- a/evcontroller.tex +++ b/evcontroller.tex @@ -52,13 +52,13 @@ $\langle$evdata {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int iWarned;@\\ \mbox{}\verb@ int iTcl;@\\ \mbox{}\verb@ int iStop;@\\ -\mbox{}\verb@ SCStore conn;@\\ +\mbox{}\verb@ SCStore *conn;@\\ \mbox{}\verb@ char *creationArgs;@\\ \mbox{}\verb@ char *runScript;@\\ \mbox{}\verb@ void *pPrivate;@\\ \mbox{}\verb@ void (*KillPrivate)(void *pData);@\\ \mbox{}\verb@ } EVControl;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -123,7 +123,7 @@ $\langle$evdriv {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ void *pPrivate;@\\ \mbox{}\verb@ void (*KillPrivate)(void *pData);@\\ \mbox{}\verb@ } EVDriver;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -208,7 +208,7 @@ $\langle$dvfunc {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -286,7 +286,7 @@ See the documentation for commands understood. \mbox{}\verb@#include "varlog.h"@\\ \mbox{}\verb@@$\langle$dvfunc {\footnotesize ?}$\rangle$\verb@@\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -315,7 +315,7 @@ See the documentation for commands understood. \mbox{}\verb@#define SETTLE 8@\\ \mbox{}\verb@@\\ \mbox{}\verb@@$\langle$evdata {\footnotesize ?}$\rangle$\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -340,7 +340,7 @@ See the documentation for commands understood. \mbox{}\verb@/*-------------------- life & death of a driver --------------------------*/@\\ \mbox{}\verb@ pEVDriver CreateEVDriver(int argc, char *argv[]);@\\ \mbox{}\verb@ void DeleteEVDriver(pEVDriver pDriv);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] diff --git a/evcontroller.w b/evcontroller.w index e9e4aeb6..0146f28f 100644 --- a/evcontroller.w +++ b/evcontroller.w @@ -47,7 +47,7 @@ used by EVControl: int iWarned; int iTcl; int iStop; - SCStore conn; + SCStore *conn; char *creationArgs; char *runScript; void *pPrivate; diff --git a/genericcontroller.c b/genericcontroller.c index 7e8f0378..75586942 100644 --- a/genericcontroller.c +++ b/genericcontroller.c @@ -593,13 +593,13 @@ int GenControllerFactory(SConnection *pCon, SicsInterp *pSics, pNew->KillPrivate = killGeneric; textValue = MakeHdbText("Undefined"); - funcValue = makeHdbData(HIPFUNC,1,EnqueFunc); + funcValue = MakeHdbFunc((voidFunc *)EnqueFunc); node = MakeSICSHdbPar("enqueue",usUser, funcValue); AddSICSHdbPar(node,"node",usUser,textValue); AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew)); AddHipadabaChild(pNew->objectNode,node,NULL); - funcValue = makeHdbData(HIPFUNC,1,EnqueHeadFunc); + funcValue = MakeHdbFunc((voidFunc *)EnqueHeadFunc); node = MakeSICSHdbPar("enqueuehead",usUser, funcValue); AddSICSHdbPar(node,"node",usUser,textValue); AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew)); diff --git a/hdbqueue.c b/hdbqueue.c index ebf0c7c6..2192412b 100644 --- a/hdbqueue.c +++ b/hdbqueue.c @@ -425,52 +425,52 @@ static void Configure(pSICSOBJ self){ n = MakeHipadabaNode("queue",HIPNONE,1); AddHipadabaChild(obj,n, NULL); - funcValue = makeHdbData(HIPFUNC,1,EnqueFunc); + funcValue = MakeHdbFunc((voidFunc *)EnqueFunc); n = MakeSICSHdbPar("enqueue",usUser, funcValue); AddSICSHdbPar(n,"description",usUser,textValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,AddCmdData); + funcValue = MakeHdbFunc((voidFunc *)AddCmdData); n = MakeSICSHdbPar("addcommand",usUser, funcValue); AddSICSHdbPar(n,"command",usUser,textValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Dequeue); + funcValue = MakeHdbFunc((voidFunc *)Dequeue); n = MakeSICSHdbPar("dequeue",usUser,funcValue); AddHipadabaChild(obj,n,NULL); AddSICSHdbPar(n,"index",usUser,intValue); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Clean); + funcValue = MakeHdbFunc((voidFunc *)Clean); n = MakeSICSHdbPar("clean",usUser, funcValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,CleanAll); + funcValue = MakeHdbFunc((voidFunc *)CleanAll); n = MakeSICSHdbPar("cleanall",usUser, funcValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Start); + funcValue = MakeHdbFunc((voidFunc *)Start); n = MakeSICSHdbPar("start",usUser, funcValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Restart); + funcValue = MakeHdbFunc((voidFunc *)Restart); n = MakeSICSHdbPar("restart",usUser, funcValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Stop); + funcValue = MakeHdbFunc((voidFunc *)Stop); n = MakeSICSHdbPar("stop",usUser, funcValue); AddHipadabaChild(obj,n,NULL); AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); - funcValue = makeHdbData(HIPFUNC,1,Move); + funcValue = MakeHdbFunc((voidFunc *)Move); n = MakeSICSHdbPar("move",usUser,funcValue); AddHipadabaChild(obj,n,NULL); AddSICSHdbPar(n,"moveindex",usUser,intValue); diff --git a/logger.c b/logger.c index 598173e0..1f9eed5f 100644 --- a/logger.c +++ b/logger.c @@ -76,7 +76,7 @@ time_t LoggerGetLastLife(char *dirarg) { } fclose(fil); } else { - printf("can not read %s\n", path); + /* printf("can not read %s\n", path); */ } return t; } @@ -108,16 +108,17 @@ char *LoggerGetDir(void) { return dir; } /*--------------------------------------------------------------------------*/ -int LoggerVarPath(char *dir, char *path, int pathLen, char *name) { +int LoggerVarPath(char *dir, char *path, int pathLen, char *name, struct tm *t) { int l; l = strlen(dir); - if (l + strlen(name) + 2 >= pathLen) { + if (l + strlen(name) + 8 >= pathLen) { path[0]='\0'; return 0; } strcpy(path, dir); - path[l] = '/'; l++; + strftime(path + l, pathLen - l, "/%Y/", t); + l += 6; for (;*name != '\0'; name++, l++) { path[l] = tolower(*name); } @@ -140,7 +141,7 @@ int LoggerWrite0(Logger *log, time_t now, int period, char *value) { } lasttm = *localtime(&log->last); tm = *localtime(&now); - l = LoggerVarPath(dir, path, sizeof path, log->name); + l = LoggerVarPath(dir, path, sizeof path, log->name, &tm); strftime(path + l, sizeof path - l, "%m-%d.log", &tm); strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", &tm); @@ -226,7 +227,7 @@ int LoggerWrite(Logger *log, time_t now, int period, char *value) { yday = tm->tm_yday; /* -- debug logging if dir/debug exists */ - l = LoggerVarPath(dir, path, sizeof path, "debug"); + l = LoggerVarPath(dir, path, sizeof path, "debug", tm); strftime(path + l, sizeof path - l, "%m-%d.log", tm); fil=fopen(path, "a"); if (fil) { @@ -279,25 +280,53 @@ void LoggerKill(Logger *log) { } } /*--------------------------------------------------------------------------*/ -Logger *LoggerMake(char *name, int period, int exact) { - Logger *log; - char path[256]; +static int LoggerMakeDir(char *path) { + static char buffer[4096]; struct stat st; - int i; - time_t t; - - LoggerGetDir(); - if (dir == NULL) return NULL; - LoggerVarPath(dir, path, sizeof path, name); + int i, lpath, l; + char *slash; + i = stat(path, &st); if (i >= 0) { if (((st.st_mode >> 12) & 15) != 4) { /* exists, but is no directory */ - return NULL; + return 0; } - } else { - i = mkdir(path, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); - if (i < 0) return NULL; /* mkdir failed */ + return 1; } + i = mkdir(path, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); + if (i < 0) { + if (errno != ENOENT) return 0; /* mkdir failed */ + snprintf(buffer, sizeof buffer, "%s", path); + lpath = strlen(buffer); + do { + slash = strrchr(buffer, '/'); + if (!slash) return 0; + *slash='\0'; + i = mkdir(buffer, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); + } while (i < 0 && errno == ENOENT); + l = strlen(buffer); + while (lpCon = pCon; @@ -127,7 +129,7 @@ static void PutFinish(Compressor *c, time_t now) { if (c->tlim != 0) { /* there is data already */ c->omitEqual = 0; if (c->type == NUMERIC) { - if (now > c->last.t) { /* copy last value to the actual time */ + if (now > c->last.t + c->period) { /* copy last value to the actual time */ if (c->last.y != LOGGER_NAN) { snprintf(value, sizeof value, "%g", c->last.y); PutValue(c, now, value); @@ -214,7 +216,7 @@ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, */ time_t from, to, step, xs, lastt; char *p, *varp; - int i, j, iarg, l, iret, loss, np; + int i, j, iarg, pathLen, iret, loss, np; int inRange; int yday=0; time_t t, startim; @@ -229,8 +231,12 @@ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, char *opt; int isdst; int overflow; - time_t now, now1, now2, nowi; + time_t now, nows[MAX_DIRS], nowi; char var[256]; + char dirPathBuffer[1024]; + char *dirPath; + int idir; + char *colon; /* argtolower(argc, argv); */ if (argc < 4) goto illarg; @@ -288,22 +294,39 @@ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, snprintf(line, sizeof line, "%ld\n", (long)now); SCWrite(pCon, line, eWarning); - if (dir == NULL) { - dir = IFindOption(pSICSOptions, "LoggerDir"); - if (dir == NULL) { - SCWrite(pCon, "ERROR: LoggerDir not found", eError); + dirPath = IFindOption(pSICSOptions, "LogReaderPath"); + if (dirPath == NULL) { /* for compatibility, check */ + dirs[0] = IFindOption(pSICSOptions, "LoggerDir"); + if (dirs[0] == NULL) { + SCWrite(pCon, "ERROR: LoggerPath not found", eError); return 0; } - } - if (dir2 == NULL) { - dir2 = IFindOption(pSICSOptions, "LoggerDir2"); - if (dir2 == NULL) { - dir2=""; + nows[0] = LoggerGetLastLife(NULL); + if (dirs[1] == NULL) { + dirs[1] = IFindOption(pSICSOptions, "LoggerDir2"); + nows[1] = LoggerGetLastLife(dirs[1]); + } + } else { + snprintf(dirPathBuffer, sizeof dirPathBuffer, "%s", dirPath); + dirPath = dirPathBuffer; + for (ndirs = 0; ndirs < MAX_DIRS; ndirs++) { + dirs[ndirs] = dirPath; + colon = strchr(dirPath, ':'); + if (colon != NULL) { + *colon = '\0'; + } + if (ndirs == 0) { + nows[0] = LoggerGetLastLife(NULL); + } else { + nows[ndirs] = LoggerGetLastLife(dirPath); + } + if (colon == NULL) { + ndirs++; + break; + } + dirPath = colon + 1; } } - now1 = LoggerGetLastLife(NULL); - if (now1 == 0) now1 = now; - now2 = 0; loss = 0; overflow = 0; @@ -331,21 +354,15 @@ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, var[j] = '\0'; c.type = type0; c.np = np; - l = LoggerVarPath(dir, path, sizeof path, var); - dr = opendir(path); - if (dr) { - nowi = now1; - closedir(dr); - } else { - if (now2 == 0) { - now2 = LoggerGetLastLife(dir2); - if (now2 == 0) now2 = now; - } - l = LoggerVarPath(dir2, path, sizeof path, var); - nowi = now2; + tm = *localtime(&from); + pathLen = 0; + for (idir = 0; idir < ndirs; idir++) { + pathLen = LoggerVarPath(dirs[idir], path, sizeof path, var, &tm); dr = opendir(path); if (dr) { + nowi = nows[idir]; closedir(dr); + break; } } @@ -361,7 +378,7 @@ static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, } if (fil == NULL) { yday = tm.tm_yday; - strftime(path + l, sizeof path - l, "%m-%d.log", &tm); + strftime(path + pathLen, sizeof path - pathLen, "%m-%d.log", &tm); fil = fopen(path, "r"); if (fil != NULL) { /* check if file is from the given year */ strftime(stim, sizeof stim, "#%Y-%m-%d", &tm); diff --git a/logsetup.c b/logsetup.c index f1343532..46059917 100644 --- a/logsetup.c +++ b/logsetup.c @@ -2,8 +2,10 @@ #include "sics.h" #include "sicshipadaba.h" -static hdbCallbackReturn LoggerUpdateCallback(pHdb node, void *userData, - pHdbMessage message) { +static char *loggerID = "loggerID"; + +static hdbCallbackReturn LoggerUpdateCallback(pHdb node, + void *userData, pHdbMessage message) { Logger *logger = userData; pDynString str; SConnection *conn = NULL; @@ -11,16 +13,16 @@ static hdbCallbackReturn LoggerUpdateCallback(pHdb node, void *userData, pHdbDataMessage mm = NULL; pHdbDataSearch dsm = NULL; - if((dsm = GetHdbDataSearchMessage(message)) != NULL){ - if(dsm->testFunc == LoggerUpdateCallback){ - dsm->result = userData; - return hdbAbort; - } - return hdbContinue; + if ((dsm = GetHdbDataSearchMessage(message)) != NULL) { + if (dsm->testPtr == loggerID) { + dsm->result = userData; + return hdbAbort; + } + return hdbContinue; } if((mm = GetHdbUpdateMessage(message)) == NULL){ - return hdbContinue; + return hdbContinue; } value = *(mm->v); @@ -92,7 +94,7 @@ static int LogSetup(SConnection *pCon, SicsInterp *pSics, void *pData, } else { numeric = 0; } - logger = FindHdbCallbackData(node, LoggerUpdateCallback, NULL); + logger = FindHdbCallbackData(node, loggerID); if (logger != 0) { /* logger exists already, changed only period */ LoggerSetPeriod(logger, period); } else { diff --git a/make_gen b/make_gen index c4ea991d..4417649d 100644 --- a/make_gen +++ b/make_gen @@ -32,7 +32,7 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \ sinfox.o sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \ ascon.o errormsg.o scriptcontext.o logger.o logreader.o logsetup.o \ - savehdb.o statusfile.o sicshdbfactory.o proxy.o \ + savehdb.o statusfile.o sicshdbfactory.o proxy.o devser.o \ moregress.o multicounter.o regresscter.o histregress.o \ sicshdbadapter.o polldriv.o sicspoll.o statemon.o hmslave.o \ nwatch.o asyncqueue.o asyncprotocol.o sicsobj.o \ diff --git a/makefile_slinux b/makefile_slinux index 5435aa63..e83c3e53 100644 --- a/makefile_slinux +++ b/makefile_slinux @@ -26,8 +26,9 @@ SUBLIBS = psi/libpsi.a psi/hardsup/libhlib.a matrix/libmatrix.a \ LIBS = -L$(HDFROOT)/lib $(SUBLIBS) $(NILIB)\ -ltcl $(HDFROOT)/lib/libhdf5.a \ $(HDFROOT)/lib/libmfhdf.a $(HDFROOT)/lib/libdf.a \ - $(HDFROOT)/lib/libjpeg.a -lsz $(HDFROOT)/lib/libjson.a \ - -ldl -lz -lmxml -lghttp -lm -lc + $(HDFROOT)/lib/libjpeg.a $(HDFROOT)/lib/libsz.a \ + $(HDFROOT)/lib/libjson.a \ + -ldl -lz -lmxml $(HDFROOT)/lib/libghttp.a -lm -lc include make_gen diff --git a/network.c b/network.c index c75e34f4..37825153 100644 --- a/network.c +++ b/network.c @@ -510,8 +510,8 @@ CreateSocketAdress( if (l > 0) { self->iType = 0; if (!disconnected) { /* do not write an error message on disconnect */ - snprintf(buf, sizeof buf, "NETWrite: timeout, only %ld of %ld bytes sent (socket %d)", - lLen - l, lLen, self->sockid); + snprintf(buf, sizeof buf, "NETWrite: timeout, only %ld of %ld bytes sent (socket %d) %ld.%6.6ld", + lLen - l, lLen, self->sockid, tmo.tv_sec, tmo.tv_usec); NetError(buf); } return 0; diff --git a/ofac.c b/ofac.c index 9c5a5061..073b0443 100644 --- a/ofac.c +++ b/ofac.c @@ -443,7 +443,7 @@ INIT(StatisticsInit); INIT(InitializerInit); INIT(SaveHdbInit); /* must be after InitializerInit */ - INIT(SctStartup); + INIT(SctInit); INIT(LogReaderInit); INIT(LogSetupInit); INIT(StatusFileInit); diff --git a/proxy.c b/proxy.c index 1a388030..6bf6740d 100644 --- a/proxy.c +++ b/proxy.c @@ -452,7 +452,7 @@ int ProxyFactory(SConnection *pCon, SicsInterp *pSics, void *pData, AppendHipadabaCallback(pNew->objectNode, MakeHipadabaCallback(ProxyCallback, pNew,NULL)); - v = makeHdbData(HIPFUNC,1,MapFunc); + v = MakeHdbFunc((voidFunc *)MapFunc); mapFunc = MakeSICSHdbPar("map", usMugger, v); SetHdbProperty(mapFunc,"visible","false"); v = MakeHdbText("Undefined"); diff --git a/remob.c b/remob.c index d8240267..e0308028 100644 --- a/remob.c +++ b/remob.c @@ -45,7 +45,7 @@ typedef struct RemServer { int taskActive; int interestActive; int forwardMessages; - SCStore conn; + SCStore *conn; } RemServer; struct Remob { diff --git a/scriptcontext.c b/scriptcontext.c index f931e1e8..52ef3101 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -4,1324 +4,755 @@ #include #include "sics.h" #include "sicsobj.h" +#include "splitter.h" #include "initializer.h" #include "commandlog.h" #include "hipadaba.h" #include "sicshipadaba.h" #include "dynstring.h" -#include "ascon.h" -#include "errormsg.h" +#include "devser.h" -/** \file - * \brief script context implementation (Sct) - */ +/* TODO: retry feature, error handling +*/ -/** \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; +typedef struct ScriptContext { + ObjectDescriptor *desc; + Hdb *node; +} ScriptContext; -/* keywords must have the same order as SctKeyword */ -static char *sctKeywords[]={ - "send", - "result", - "command", - "path", - "update", - "take", - "get", - "complete", - "retry", - "chain", - "next", - "steplist", - NULL -}; +typedef struct ContextStack { + struct ContextStack *next; + Hdb *node; +} ContextStack; -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; +typedef struct SctController { + DevSer *devser; + Hdb *node; /* the controller node */ + SCStore *conn; int verbose; - int taskSteps; - time_t maxAge; -}; +} SctController; -static Sct *sct = NULL; +typedef struct SctData { + int writable; + char *name; + SctController *controller; + SCStore *conCtx; + int answered; + Hdb *node; /* back link used by SctMatchWrite/SctMatchPoll */ +} SctData; -static void SctFreeParData(void *d) { - SctParData *data=d; - data->refCnt--; - if (data->refCnt == 0) { - if (data->conCtx) SCStoreFree(data->conCtx); - free(data); - } -} +static ScriptContext *nctx = NULL; /* the node context */ +static ScriptContext *cctx = NULL; /* the controller context */ +static ContextStack *stack, *stackTrash; +static SCStore *currentCon = NULL; -static int UpdateNode(SConnection *con, ErrMsgList *e, pHdb node, int argc, char *argv[]) { - hdbValue newValue; - static char errtxt[512]; - char *str; - int status; +static struct { + char *name; +} actionCallback; + +static char *mainCallback = "main callback"; + +void PushContext(Hdb *node) { + ContextStack *new; - if (!cloneHdbValue(&node->value, &newValue)) { - ErrPutMsg(e, NULL, "no memory"); - return 0; + if (stackTrash == NULL) { + new = calloc(1, sizeof(*new)); + } else { + new = stackTrash; + stackTrash = stackTrash->next; } - 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; + new->next = stack; + new->node = node; + stack = new; } -static int SctFindKeyword(char *name, char *keywords[]) { +Hdb *PopContext(void) { + ContextStack *c; + + c = stack; + stack = stack->next; + c->next = stackTrash; + stackTrash = c; + return c->node; +} + +static void SetProp(Hdb *node, char *key, char *value) { + if (value == NULL) { + if (GetHdbProperty(node, key, NULL, 0) > 0) { + SetHdbProperty(node, key, ""); + } + } else { + SetHdbProperty(node, key, value); + } +} + +int SctCommand(SConnection *con, SicsInterp *sics, void *object, + int argc, char *argv[]) { + ScriptContext *ctx = object; + static char value[1024]; + char *val; + Hdb *node = ctx->node; + + if (node == NULL) { + SCPrintf(con, eError, "ERROR: %s may be called only in proper context", + argv[0]); + return 0; + } + if (argc <= 1) { + GetHdbPath(node, value, sizeof value); + SCWrite(con, value, eValue); + return 1; + } + if (argc == 2) { /* get case */ + val = GetHdbProp(node, argv[1]); + if (val == NULL) { + SCPrintf(con, eError, "ERROR: %s %s not found", argv[0], argv[1]); + return 0; + } + SCWrite(con, val, eValue); + } else { /* set case */ + val = Arg2Tcl(argc-2, argv+2, value, sizeof value); + if (val != NULL) { + SetHdbProperty(node, argv[1], val); + if (val != value) free(val); + } + } + return 1; +} + +int SctCallInContext(SConnection *con, char *script, Hdb *node, + SctController *controller, char **resPtr) { + Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics); + int ret, l; + char *result = NULL; + int iRet = 1; + int verbose = controller->verbose; + + PushContext(nctx->node); + PushContext(cctx->node); + nctx->node = node; + cctx->node = controller->node; + if (verbose) { + SCPrintf(con, eInError, "\nscript: %s\n", script); + } + + l = strlen(script); + ret = Tcl_EvalEx(pTcl, script, l, 0); + result = (char *)Tcl_GetStringResult(pTcl); + if (ret != TCL_OK && result[0]!='\0') { + if (verbose) { + SCPrintf(con, eInError, "\nerror: %s\n", result); + } + iRet = 0; + } + *resPtr = result; + + cctx->node = PopContext(); + nctx->node = PopContext(); + return iRet; +} + +void SctKill(void *object) { + ScriptContext *ctx = object; + if (ctx == nctx) nctx = NULL; + if (ctx == cctx) cctx = NULL; + if (ctx->desc != NULL) { + DeleteDescriptor(ctx->desc); + } + free(object); +} + +ScriptContext *SctMake(char *name) { + ScriptContext *ctx; + + ctx = calloc(1, sizeof(*ctx)); + assert(ctx); + ctx->desc = CreateDescriptor("ScriptContext"); + ctx->node = NULL; + AddCommand(pServ->pSics, name, SctCommand, SctKill, ctx); + return ctx; +} + +static int SctMatch(void *data1, void *data2) { + SctData *a = data1; + SctData *b = data2; + + return a->node == b->node && strcasecmp(a->name, b->name) == 0; +} + +static char *SctActionHandler(void *actionData, char *lastReply) { + SctData *data = actionData; + Hdb *node = data->node; + SctController *controller = data->controller; + char *state; + char *result; + char *script; + char *send = NULL; 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 hdbCallbackReturn SctUpdateCallback(pHdb node, void *userData, - pHdbMessage message) { - SctParData *data = userData; SConnection *con; - char path[MAX_HDB_PATH]; - pDynString str; - pHdbPtrMessage pm = NULL; - pHdbDataMessage mm = NULL; - pHdbDataSearch dsm = NULL; - hdbValue value; - if((pm = GetKillPtrMessage(message)) != NULL){ - if(data->sct == pm->pPtr){ - return hdbKill; - } - return hdbContinue; + if (currentCon) { + con = SCStorePush(currentCon); + } else { + con = SCStorePush(controller->conn); } - - if((dsm = GetHdbDataSearchMessage(message)) != NULL){ - if(dsm->testFunc == SctUpdateCallback && dsm->testPtr == data->sct){ - dsm->result = userData; - return hdbAbort; - } - return hdbContinue; + SetProp(controller->node, "result", lastReply); + if (controller->verbose && lastReply != NULL && *lastReply != '\0') { + SCPrintf(con, eWarning, "reply : %s", lastReply); } - - if((mm = GetHdbUpdateMessage(message)) == NULL) { - return hdbContinue; + state = GetHdbProp(controller->node, "state"); + if (state == NULL || state[0] == '\0') { + state = data->name; + SetProp(controller->node, "state", state); } - value = *(mm->v); - - 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); + for (i = 0; i < 10; i++) { + SetProp(controller->node, "send", NULL); + script = GetHdbProp(node, state); + if (script == NULL) script = state; + + if (! SctCallInContext(con, script, node, data->controller, &result)) { + SCPrintf(con, eError, "ERROR: %s", result); + goto finish; + } + state = result; + if (strcasecmp(state, "idle") == 0) { + if (currentCon && ! data->answered) { + SCWrite(con, "o.k.", eValue); + } + if (data->conCtx != NULL) { + SCStoreFree(data->conCtx); + data->conCtx = NULL; + } + goto finish; + } + SetProp(controller->node, "state", state); + send = GetHdbProp(controller->node, "send"); + if (send != NULL && send[0] != '\0') { + if (controller->verbose) { + SCPrintf(con, eWarning, "send : %s", send); + } + goto quit; } } - return hdbContinue;; + SCPrintf(con, eError, "ERROR: too many quick scripts chained"); +finish: + SetProp(controller->node, "state", ""); +quit: + if (currentCon) { + SCStorePop(currentCon); + } else { + SCStorePop(controller->conn); + } + return send; } -static hdbCallbackReturn SctSetCallback(pHdb node, void *userData, - pHdbMessage message) { - SctParData *data = userData; - SConnection *oldCon; +static char *SctWriteHandler(void *actionData, char *lastReply) { + SctData *data = actionData; + SCStore *old; + char *result; + + old = currentCon; + currentCon = data->conCtx; + result = SctActionHandler(data, lastReply); + currentCon = old; + return result; +} + +static hdbCallbackReturn SctMainCallback(Hdb *node, void *userData, + hdbMessage *msg) { + SctController *controller = userData; + hdbDataSearch *dsm; + hdbDataMessage *mm; + hdbPtrMessage *pm; + hdbMessage *km; + ContextStack *s; + SConnection *con; + char *geterror; + + pm = GetKillPtrMessage(msg); + if (pm != NULL) { + if (controller == pm->pPtr) { + return hdbKill; + } + return hdbContinue; + } + dsm = GetHdbDataSearchMessage(msg); + if (dsm != NULL) { + if (dsm->testPtr == controller) { + dsm->result = controller; + return hdbAbort; + } + return hdbContinue; + } + km = GetHdbKillNodeMessage(msg); + if (km != NULL) { + /* clean context from killed nodes */ + for (s = stack; s != NULL; s = s->next) { + if (s->node == node) { + s->node = NULL; + } + } + if (cctx->node == node) cctx->node = NULL; + if (nctx->node == node) nctx->node = NULL; + return hdbContinue; + } + mm = GetHdbGetMessage(msg); + if (mm != NULL) { + con = mm->callData; + geterror = GetHdbProp(node, "geterror"); + if (geterror != NULL) { + SCPrintf(con, eError, "ERROR: %s", geterror); + return hdbAbort; + } + return hdbContinue; + } + + return hdbContinue; +} + +static hdbCallbackReturn SctActionCallback(Hdb *node, void *userData, + hdbMessage *msg) { + hdbDataSearch *dsm; + hdbDataMessage *mm; + hdbPtrMessage *pm; + SctData *data = userData; + Hdb *target; + char *script; + int l; + int iRet; + pDynString text; + DevPrio prio; + char *writeprio; + char *error; + SConnection *con; + SConnection *con2; char path[MAX_HDB_PATH]; - pDynString str; - pHdb target; - SConnection *conn = NULL; - pHdbPtrMessage pm = NULL; - pHdbDataMessage mm = NULL; - hdbValue value; - if((pm = GetKillPtrMessage(message)) != NULL){ - if(data->sct == pm->pPtr){ - return hdbKill; - } - return hdbContinue; + pm = GetKillPtrMessage(msg); + if (pm != NULL) { + if (data->controller == pm->pPtr) { + return hdbKill; + } + return hdbContinue; + } + dsm = GetHdbDataSearchMessage(msg); + if (dsm != NULL) { + if (dsm->testPtr == &actionCallback) { + if (strcasecmp(data->name, actionCallback.name) == 0) { + dsm->result = data; + } + return hdbAbort; + } + return hdbContinue; } - if((mm = GetHdbSetMessage(message)) == NULL) { - return hdbContinue; - } - value = *(mm->v); - conn = mm->callData; - - if (data->setChain) { - if (data->pending) { + mm = GetHdbSetMessage(msg); + if (mm != NULL && data->writable) { + con = mm->callData; + + /* call check script, if available */ + script = GetHdbProp(node, "check"); + if (script != NULL) { + if (SctCallInContext(con, script, node, data->controller, &error) == 0) { + SCPrintf(con, eError, "ERROR: %s", error); + return hdbAbort; + } + } + + /* set target value */ + text = formatValue(*(mm->v), node); + SetHdbProperty(node, "target", GetCharArray(text)); + + /* enqueue write action */ + writeprio = GetHdbProp(node, "writeprio"); + if (writeprio != NULL) { + prio = DevText2Prio(writeprio); + if (prio < 0) prio = WritePRIO; + } else { + prio = WritePRIO; + } + + if (data->conCtx != NULL) { + con2 = SCStorePush(data->conCtx); 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); + SCPrintf(con2, eValue, "%s target changed to %s before completion", + path, GetCharArray(text)); 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); + DeleteDynString(text); + data->conCtx = SCSave(con, data->conCtx); + data->answered = 0; + DevQueue(data->controller->devser, data, prio, + SctWriteHandler, SctMatch, NULL); + return hdbContinue; + } + + mm = GetHdbUpdateMessage(msg); + if (mm != NULL) { + if (currentCon) { /* update called from a write action */ + data->answered = 1; + GetHdbPath(node, path, sizeof path); + con = SCStorePush(currentCon); + text = formatValue(*(mm->v), node); + SCPrintf(con, eStatus, "%s = %s", path, + GetCharArray(text)); + DeleteDynString(text); + SCStorePop(currentCon); } return hdbContinue; } return hdbContinue; } -static hdbCallbackReturn SctReadCallback(pHdb node, void *userData, - pHdbMessage message) { - SctParData *data = userData; - pHdbPtrMessage pm = NULL; - - if((pm = GetKillPtrMessage(message)) != NULL){ - if(data->sct == pm->pPtr){ - return hdbKill; - } - return hdbContinue; - } +static char *ParText(Hdb *cmdNode, char *name) { + Hdb *par; - /* not used ? */ - return hdbContinue; + par = GetHipadabaNode(cmdNode, name); + if (par && par->value.dataType == HIPTEXT) { + return par->value.v.text; + } + return ""; } -int SctCallDynamicScript(Sct *sct, char *name, char *script) { - SctList *sl; - SctChain *sc; +static double ParValue(Hdb *cmdNode, char *name) { + Hdb *par; - 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; - } + par = GetHipadabaNode(cmdNode, name); + if (par) { + switch (par->value.dataType) { + case HIPINT: return par->value.v.intValue; + case HIPFLOAT: return par->value.v.doubleValue; } } - return 0; + return -999; } -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; +void SctKillData(void *d) { + SctData *data = d; + if (data->name) free(data->name); + if (data->conCtx) SCStoreFree(data->conCtx); + free(data); +} + +void SctAddPollNode(SctController *controller, Hdb *node, double interval, + DevPrio prio, char *action) { + SctData *data; + hdbCallback *cb; - 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; + if (! FindHdbCallbackData(node, controller)) { + cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); + assert(cb); + AppendHipadabaCallback(node, cb); } - 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