/** * This is a generalized polling module for SICS. With this module * SICS variables can be polled regulary for updates. For different types of * SICS variables different polling mechanisms are required. In order to cope with * this requirement a polling interface and different drivers are defined in the * sister module polldriv.h and polldriv.c. This module implements the interface * to configure polling and the SICS task to run polling. * * Copyright: see COPYRIGHT * * Mark Koennecke, November-December 2006 * * Implemented exponential backoff up to 6 minutes when polling fails. This in order to * reduce the number of error messages in the logs if something is MIA * * Mark Koennecke, November 2016 */ #include #include #include #include #include "polldriv.h" #include "splitter.h" #include #include "lld.h" /*================== data structure =====================================*/ static SConnection *defCon = NULL; struct __SICSPOLL { pObjectDescriptor pDes; int pollList; /* list with polled objects */ int listDirty; /* a flag to set when the list has been modified. This will cause the list polling task to go back to the start. */ SConnection *pCon; /* connection to use for polling */ int iEnd; /* flag ending this */ int nPoll; /* how many to poll in one run */ long taskID; }; /*-----------------------------------------------------------------------*/ void killSicsPoll(void *data) { pSicsPoll self = (pSicsPoll) data; int status; pPollDriv poll = NULL; self->iEnd = 1; status = LLDnodePtr2First(self->pollList); while (status != 0) { poll = LLDnodePtr(self->pollList); if (poll != NULL) { deletePollDriv(poll); } status = LLDnodePtr2Next(self->pollList); } LLDdelete(self->pollList); if (self->pDes) { DeleteDescriptor(self->pDes); } free(self); if (defCon != NULL) { SCDeleteConnection(defCon); } } /*----------------- list access -----------------------------------------*/ static pPollDriv locateObject(int list, char *objectIdentifier) { int status; pPollDriv data = NULL; status = LLDnodePtr2First(list); while (status != 0) { data = (pPollDriv) LLDnodePtr(list); if (data != NULL) { if (strcmp(data->objectIdentifier, objectIdentifier) == 0) { return data; } } status = LLDnodePtr2Next(list); } return NULL; } /*===================== task function ==================================*/ static int incrList(int list) { int status; if (LLDcheck(list) == LIST_EMPTY) { return 0; } status = LLDnodePtr2Next(list); if (status == 0) { status = LLDnodePtr2First(list); } return status; } /*---------------------------------------------------------------------------*/ void SicsPollSignal(void *pData, int iSignal, void *pSigData) { pSicsPoll self = NULL; int *iInt; self = (pSicsPoll) pData; if (iSignal == SICSINT) { iInt = (int *) pSigData; if (*iInt == eEndServer) { self->iEnd = 1; } } } /*---------------------------------------------------------------------- This function implements the exponential backoff when there is a failure in polling ------------------------------------------------------------------------*/ static void advancePoll(pPollDriv poll, int status) { if(status == 1) { /* success */ poll->actualPollIntervall = poll->pollIntervall; } else { /* poll error, backoff */ if(poll->actualPollIntervall < poll->pollIntervall){ poll->actualPollIntervall = 2 * poll->pollIntervall; } else { poll->actualPollIntervall = 2 * poll->actualPollIntervall; /* poll at least every 6 minutes */ if(poll->actualPollIntervall > 360){ poll->actualPollIntervall = 360; } } } poll->nextPoll = time(NULL) + poll->actualPollIntervall; } /*----------------------------------------------------------------------*/ static int PollTask(void *data) { pSicsPoll self = (pSicsPoll) data; pPollDriv poll = NULL; int status, i; time_t now = time(NULL); if (self == NULL || self->iEnd == 1) { return 0; } if (LLDcheck(self->pollList) == LIST_EMPTY) { return 1; } /* * increment list */ if (self->listDirty == 1) { self->listDirty = 0; status = LLDnodePtr2First(self->pollList); } /* * actually do poll */ for (i = 0; i < self->nPoll; i++) { status = incrList(self->pollList); poll = (pPollDriv) LLDnodePtr(self->pollList); if (status != 0 && poll != NULL) { if (poll->isDue(poll, now, self->pCon)) { status = poll->poll(poll, self->pCon); advancePoll(poll,status); } } } return 1; } /*==================== interface functions ==============================*/ int removePollObject(pSicsPoll self, char *objectIdentifier) { pPollDriv target = NULL; self->listDirty = 1; target = locateObject(self->pollList, objectIdentifier); if (target != NULL) { LLDnodeDelete(self->pollList); deletePollDriv(target); return 1; } else { return 0; } } /*------------------------------------------------------------------------*/ int addPollObject(SicsPoll * self, SConnection * pCon, char *objectIdentifier, char *driver, int argc, char *argv[]) { int status; pPollDriv driv = NULL; driv = makePollDriver(pCon, driver, objectIdentifier, argc, argv); if (driv == NULL) { return 0; } LLDnodeAppend(self->pollList, &driv); return 1; } /*-----------------------------------------------------------------------*/ static void printPollList(pSicsPoll self, SConnection * pCon) { int status; pPollDriv driv = NULL; char buffer[512]; char tbuf[256]; status = LLDnodePtr2First(self->pollList); while (status != 0) { driv = (pPollDriv) LLDnodePtr(self->pollList); if (driv != NULL) { ctime_r(&driv->nextPoll, tbuf); snprintf(buffer, 512, "%30s %3d %30s", driv->objectIdentifier, driv->pollIntervall, tbuf); SCWrite(pCon, buffer, eValue); } status = LLDnodePtr2Next(self->pollList); } } /*================== interpreter interface ===============================*/ int SICSPollWrapper(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pSicsPoll self = (pSicsPoll) pData; pPollDriv driv = NULL; int status, iVal; char buffer[512]; pDynString txt = NULL; SConnection *dummy = NULL; assert(self != NULL); if (argc < 2) { SCWrite(pCon, "ERROR: Not enough arguments", eError); return 0; } strtolower(argv[1]); if (strcmp(argv[1], "del") == 0) { if (argc < 3) { SCWrite(pCon, "ERROR: Not enough arguments", eError); return 0; } if (!SCMatchRights(pCon, usMugger)) { return 0; } status = removePollObject(self, argv[2]); if (status == 0) { SCWrite(pCon, "ERROR: object to remove from poll list not found", eError); return 0; } else { SCSendOK(pCon); return 1; } } else if (strcmp(argv[1], "add") == 0) { if (argc < 4) { SCWrite(pCon, "ERROR: Not enough arguments", eError); return 0; } if (!SCMatchRights(pCon, usMugger)) { return 0; } driv = makePollDriver(pCon, argv[3], argv[2], argc - 4, &argv[4]); if (driv != NULL) { LLDnodeAppend(self->pollList, &driv); SCSendOK(pCon); return 1; } else { return 0; } } else if (strcmp(argv[1], "npoll") == 0) { if (argc < 3) { snprintf(buffer, 512, "%s.%s = %d", argv[0], "npoll", self->nPoll); } else { if (!SCMatchRights(pCon, usMugger)) { return 0; } status = sscanf(argv[2], "%d", &self->nPoll); if (status != 1) { snprintf(buffer, 512, "ERROR: failed to convert %s to int", argv[2]); SCWrite(pCon, buffer, eError); return 0; } else { SCSendOK(pCon); return 1; } } } else if (strcmp(argv[1], "listen") == 0) { self->pCon = pCon; SCSendOK(pCon); return 1; } else if (strcmp(argv[1], "unlisten") == 0) { self->pCon = defCon; SCSendOK(pCon); return 1; } else if (strcmp(argv[1], "intervall") == 0) { if (argc < 3) { SCWrite(pCon, "ERROR: Not enough arguments", eError); return 0; } if (!SCMatchRights(pCon, usMugger)) { return 0; } driv = locateObject(self->pollList, argv[2]); if (driv == NULL) { SCWrite(pCon, "ERROR: object not in polling list", eError); return 0; } if (argc > 3) { status = sscanf(argv[3], "%d", &iVal); if (status != 1) { snprintf(buffer, 511, "ERROR: failed to convert %s to int", argv[3]); SCWrite(pCon, buffer, eError); return 0; } if (iVal < 0) { SCWrite(pCon, "ERROR: new value for intervall out of range", eError); return 0; } driv->pollIntervall = iVal; driv->actualPollIntervall = iVal; SCSendOK(pCon); return 1; } else { snprintf(buffer, 511, "%s.intervall = %d", driv->objectIdentifier, driv->pollIntervall); SCWrite(pCon, buffer, eValue); return 1; } } else if (strcmp(argv[1], "list") == 0) { dummy = SCCreateDummyConnection(pSics); if(dummy == NULL){ SCWrite(pCon,"ERROR: out of memory listing polling data",eError); return 0; } SCStartBuffering(dummy); printPollList(self, dummy); txt = SCEndBuffering(dummy); if (txt != NULL) { SCWrite(pCon, GetCharArray(txt), eValue); SCDeleteConnection(dummy); } return 1; } else if (strcmp(argv[1], "poll") == 0) { if (argc < 3) { SCWrite(pCon, "ERROR: Not enough arguments", eError); return 0; } driv = locateObject(self->pollList, argv[2]); if (driv == NULL) { SCWrite(pCon, "ERROR: object not in polling list", eError); return 0; } status = driv->poll(driv, pCon); advancePoll(driv,status); if (status != 1) { SCWrite(pCon, "ERROR: polling object", eError); return 0; } SCWrite(pCon, "Object polled OK", eError); return 1; } return 1; } /*------------------------------------------------------------------------*/ int InstallSICSPoll(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pSicsPoll pNew = NULL; pNew = malloc(sizeof(SicsPoll)); if (pNew == NULL) { return 0; } memset(pNew, 0, sizeof(SicsPoll)); pNew->pDes = CreateDescriptor("SicsPoll"); pNew->pollList = LLDcreate(sizeof(void *)); defCon = SCCreateDummyConnection(pSics); if (pNew->pDes == NULL || pNew->pollList < 0 || defCon == NULL) { SCWrite(pCon, "ERROR: out of memory creating SicsPoll", eError); return 0; } pNew->pCon = defCon; pNew->nPoll = 3; TaskRegisterN(pServ->pTasker,"sicspoll", PollTask, SicsPollSignal, NULL, pNew, TASK_PRIO_HIGH); if (argc > 1) { AddCommand(pSics, argv[1], SICSPollWrapper, killSicsPoll, pNew); } else { AddCommand(pSics, "sicspoll", SICSPollWrapper, killSicsPoll, pNew); } return 1; }