#include #include #include #include #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_complete_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", "complete", "chain", "next", "steplist", NULL }; static char *reset_type=""; typedef struct SctList SctList; typedef struct SctChain { struct SctChain *next; char *command; SctList *scriptList; pHdb relatedNode; 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 *scriptChain; int pending, inprogress; int refCnt; hdbValue target; SCStore conCtx; } SctParData; typedef struct SctObjectName { struct SctObjectName *next; char *name; } SctObjectName; /* define SctObjectNameList and their functions */ #define MC_NAME(T) SctObjectName##T #include "mclist.c" struct Sct { pObjectDescriptor desc; char *name; SctListList scriptLists; SctObjectNameList names; /* created with the "object" command, needed for kill only */ AsconPtr ascon; pHdb currentNode; SctParData *foundData; char *cmd; char *result; char *nextScript; SctChain *runningChain; SCStore debugConn; char *debugCommand; int sent; SctKeyword operation; int killTask; int killObj; int verbose; int taskSteps; }; 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); } } pHdb SctFindObjNode(char *name) { CommandList *cmd; pDummy obj; cmd = FindCommand(pServ->pSics, name); if (cmd == NULL) return NULL; obj = (void *)cmd->pData; if (obj == NULL || obj->pDescriptor->parNode==NULL) return NULL; return obj->pDescriptor->parNode; } /** \brief find the node with the relative path relpath * \param node the root node (where to start) * \param relpath an absolute or relative path * \param parentPtr a pointer to the found parent (may be NULL if not used) * \param nodePtr a pointer to the found node * \return the name of the node * * An abolute path starts with a slash, a relative path equal to "." * means that the input node is returned. A syntax like "..", "../down" * is also allowed, but the double dots must appear at the start of the path. * * This routine may also be used in order to find the parent node of * a node not yet existing. In this case the nodePtr will point * to a NULL pointer on return, and a pointer to the last element * in the path is returned. * * Nodes anchored in the sics object list are also found when * the path starts with "/sics/" */ char *SctFindNode(pHdb node, char *relpath, pHdb *parentPtr, pHdb *nodePtr) { char *path; char buffer[MAX_HDB_PATH]; pHdb root = NULL; char *name; char *slash; if (strlen(relpath) >= sizeof(buffer)) { *nodePtr = NULL; return NULL; /* relpath too long */ } strcpy(buffer, relpath); path = buffer; if (path[0] == '/') { if (strncmp(path, "/sics/", 6) == 0) { /* sics object case */ slash = strchr(path+6, '/'); if (slash != NULL) { *slash = '\0'; } node = SctFindObjNode(path); if (node == NULL) goto notFound; if (slash == NULL) goto nodeFound; path = slash+1; /* root node is sics object, path is relative to it */ } else { /* absolute path */ node = GetHipadabaRoot(); /* strip off first slash, root node is sics root */ path++; } } else if (path[0] == '.') { if (path[1] == '\0') { /* this path */ goto nodeFound; } /* upper node */ while (strncmp(path, "..", 2) == 0 && node != NULL) { node = node->mama; if (path[2] == '\0') goto nodeFound; path += 3; } if (node == NULL) goto notFound; } /* now go down in path */ while (node != NULL) { root = node; slash = strchr(path, '/'); if (slash != NULL) *slash='\0'; for (node = root->child; node != NULL; node = node->next) { if (strcasecmp(node->name, path) == 0) { if (slash == NULL) goto nodeFound; path = slash + 1; break; } } } if (slash == NULL) { if (parentPtr != NULL) { *parentPtr = root; } *nodePtr = NULL; /* the returned name must be taken from the end of relpath, as path is no longer valid */ name = relpath + (path - buffer); goto finish; } notFound: node = NULL; nodeFound: *nodePtr = node; if (node) { if (parentPtr != NULL) { *parentPtr = node->mama; } name = node->name; } else { if (parentPtr != NULL) { *parentPtr = NULL; } name = NULL; } finish: return name; } /** \brief get the absolute path of a node anchored in the * Hipadaba root or in a sics object * \param nodeArg the input node * \param path the result * \param pathlen length of the result * \return 1 on success, 0 on failure */ int SctGetPath(pHdb nodeArg, char *path, size_t pathlen) { pHdb node, root; int len, pos, l; static char *sics="/sics"; /* determine path length and root node */ root = nodeArg; len = 0; for (node = nodeArg; node != NULL; node = node->mama) { len += strlen(node->name) + 1; if (len >= pathlen) return 0; /* buffer overflow (may be recursize path?) */ root = node; } /* check root and add prefix */ path[0]='\0'; if (root != GetHipadabaRoot()) { /* not anchored in root */ if (!FindCommand(pServ->pSics, root->name)) { /* not a sicsobject, give up */ path[0]='\0'; return 0; } /* sics object case */ l = strlen(sics); len += l; if (len >= pathlen) return 0; strncpy(path, sics, l); } /* build the path backwards */ path[len]='\0'; pos = len; for (node = nodeArg; node != NULL; node = node->mama) { len = strlen(node->name); pos -= len; assert(pos>0); strncpy(path+pos, node->name, len); pos--; path[pos]='/'; } return 1; } 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->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; SctGetPath(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; if (data->scriptChain) { if (data->pending) { SctGetPath(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->scriptChain); copyHdbValue(&value, &data->target); return 0; } return 1; } static int SctReadCallback(void *user, void *conn, pHdb node, hdbValue value) { SctParData *data = user; data->sct->foundData = data; if (data->sct->operation == sct_take_keyword && data->pending) { data->pending = 0; data->inprogress = 1; return 0; } return 1; } static int SctExec(SConnection *con, SicsInterp *sics, void *object, int argc, char *argv[]) { SctKeyword keyword; SctList *sl; SctChain *sc; pHdb node; 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