Files
sics/sicspoll.c

426 lines
11 KiB
C

/**
* 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 <stdlib.h>
#include <assert.h>
#include <sics.h>
#include <splitter.h>
#include "polldriv.h"
#include "splitter.h"
#include <sicspoll.h>
#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;
}