Files
sicspsi/pardef.c

1810 lines
44 KiB
C

/*---------------------------------------------------------------------------
pardef.c
my way to define and handle object parameters
Markus Zolliker, March 2005
----------------------------------------------------------------------------
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <fortify.h>
#include <limits.h>
#include <ctype.h>
#include "logger.h"
#include "logsetup.h"
#include "pardef.h"
#include "sugar.h"
#include "dynstring.h"
#include "sicshipadaba.h"
#include "ease.h"
#define ILLNUM -2
#define ILLARGC -3
#define AMBIGUOS -4
#define ILLPRIV -5
#define BADLOG -6
#define UNKPAR -7
#define PARUNDEF -8
#define ERRCMD -9
Logger dummy_logger;
Logger *secop_logger = &dummy_logger; /* disable parlog, and do it with hdb logger */
typedef enum { NO_OP, FMT_OP, SET_OP, GET_OP, INIT_OP, UPD_OP } ParOp;
/* Context holds all the information needed during the pardef function */
typedef struct Context {
struct Context *next;
ParData *obj;
ParAct act; /* action called */
ParAct action; /* copy of act, but set to PAR_NOOP when name does not match */
char *parName;
int argc;
char **argv;
char *valueArg;
char *thisPar;
int returnValue;
SConnection *con;
char *fmt;
int access;
FILE *saveFile;
int doit;
char *listTail;
char **enumList;
char *logName;
time_t now;
ParInfo *par;
float value;
char combiName[80];
char *grpFmt;
int exact;
char *callName; /* the called name of the object (different from obj->name in case of an alias) */
int enumText; /* show enum text */
hdbValue *hvalue;
} Context;
static char *loggerDir = NULL;
static int showTime = 0;
static void *initObj; /* this object is in initialization mode */
static Context *ctx = NULL, *freeContexts = NULL;
void ParKill(void *object);
static ParClass parClass = { "Par", sizeof(ParData), 0, {&parClass}
};
/*----------------------------------------------------------------------------*/
void *ParCast(ParClass * class, void *object)
{
ParClass *t;
if (!object)
return NULL;
t = ((ParData *) object)->class;
assert(t->base[0] == &parClass);
if (t && t->base[class->level] == class) {
return object;
} else {
return NULL;
}
}
/*----------------------------------------------------------------------------*/
void *ParCheck(ParClass * class, void *object)
{
ParClass *t;
assert(object);
t = ((ParData *) object)->class;
assert(t->base[0] == &parClass);
assert(t && t->base[class->level] == class);
return object;
}
/*----------------------------------------------------------------------------*/
void *ParMakeClass(ParClass * class, ParClass * base)
{
int i;
if (class->base[0] == NULL) { /* initialize */
if (base == NULL) {
base = &parClass;
}
assert(class->size >= base->size);
for (i = 0; i <= base->level; i++) {
class->base[i] = base->base[i];
}
assert(i < PAR_LEVELS);
class->base[i] = class;
class->level = i;
} else if (base) { /* check */
for (i = 0; i <= base->level; i++) {
assert(class->base[i] == base->base[i]);
}
assert(class->base[i] == class);
assert(class->level == i);
}
return class;
}
/*--------------------------------------------------------------------------*/
int ParPrintf(void *object, int iOut, const char *fmt, ...)
{
va_list ap;
char buf0[128];
int l;
char *buf, *dyn = NULL;
SConnection *con;
ParData *pobj = object;
con = NULL;
if (pobj) {
if (ctx && pobj == ctx->obj && ctx->con) {
con = ctx->con;
} else {
con = pobj->conn;
}
} else if (ctx) {
con = ctx->con;
}
if (iOut < 0) {
if (!con)
return 0; /* no connection, no verbose output */
if (-iOut > pobj->verbose)
return 0; /* check verbosity level */
iOut = eLog;
}
va_start(ap, fmt);
l = vsnprintf(buf0, sizeof(buf0), fmt, ap);
va_end(ap);
if (l >= sizeof buf0) {
/* we have probably a C99 conforming snprintf and need a larger buffer */
dyn = malloc(l + 1);
if (dyn != NULL) {
va_start(ap, fmt);
vsnprintf(dyn, l + 1, fmt, ap);
va_end(ap);
}
buf = dyn;
} else {
buf = buf0;
}
if (con) {
SCWrite(con, buf, iOut); /* writes to command log when user or manager */
} else if (iOut >= 0) {
Log(INFO,"par", "%s", buf); /* no connection, write to commandlog only */
}
if (dyn)
free(dyn);
return 1;
}
/*-------------------------------------------------------------------------*/
static void ParBegin(void)
{
Context *p;
if (freeContexts) {
p = freeContexts;
freeContexts = p->next;
} else {
p = calloc(1, sizeof(*ctx));
assert(p);
}
p->next = ctx;
ctx = p;
ctx->callName = NULL;
}
/*-------------------------------------------------------------------------*/
static void ParEnd(void)
{
Context *p;
assert(ctx);
p = ctx->next;
ctx->next = freeContexts;
freeContexts = ctx;
ctx = p;
}
/*-------------------------------------------------------------------------*/
static void ParDo(SConnection * con, ParData * o, ParAct a, char *parName)
{
ctx->act = a;
ctx->action = a;
ctx->con = con;
if (parName && strcmp(parName, "*") == 0) {
ctx->thisPar = "";
} else {
ctx->thisPar = parName;
}
ctx->returnValue = 0;
ctx->par = NULL;
ctx->grpFmt = NULL;
ctx->obj = o;
if (!ctx->callName) {
ctx->callName = o->name;
}
o->pardef(o);
}
/*--------------------------------------------------------------------------*/
char *ParArg2Str(int argc, char *argv[], char *result, int maxsize)
{
int i, argsize;
char *p, *res;
argsize = 0;
if (result == NULL) {
maxsize = INT_MAX;
for (i = 0; i < argc; i++) {
argsize += strlen(argv[i]) + 1;
}
if (argsize == 0) argsize = 1;
result = malloc(argsize);
if (!result)
return NULL;
res = result;
} else {
res = result;
for (i = 0; i < argc; i++) {
argsize += strlen(argv[i]) + 1;
if (argsize > maxsize) {
argc = i;
res = NULL;
break;
}
}
}
p = result;
*p = '\0';
for (i = 0; i < argc; i++) {
if (i > 0) {
*p = ' ';
p++;
}
strcpy(p, argv[i]);
p += strlen(argv[i]);
}
return res;
}
/*-------------------------------------------------------------------------*/
static int ParSaveAll(void *object, char *name, FILE * fil)
{
ParData *o = ParCheck(&parClass, object);
int ret;
ParBegin();
ctx->saveFile = fil;
/*
* Markus: why is this? This killed SICS unexpectedly once for me.
* And it is not required: the code can work even if name and o->name
* do not match.
*
* assert(0 == strcasecmp(o->name, name));
*/
if (o->creationCmd) {
fprintf(fil, "%s\n", o->creationCmd);
}
ParDo(0, o, PAR_SAVE, NULL);
if (o->creationCmd) {
fprintf(fil, "%s endinit\n\n", name);
/* fprintf(fil, "put_secop_info %s\n\n", name); */
}
ctx->saveFile = NULL;
ret = ctx->returnValue;
ParEnd();
return ret;
}
/*--------------------------------------------------------------------------*/
FILE *ParSaveFile(void)
{
if (ctx) {
return ctx->saveFile;
}
return NULL;
}
/*--------------------------------------------------------------------------*/
void ParUpdateAll(void *object)
{
ParData *o = ParCheck(&parClass, object);
ParBegin();
ParDo(0, o, PAR_UPDATE, NULL);
ParEnd();
}
/*--------------------------------------------------------------------------*/
int ParLog(void *object)
{
ParData *o = ParCheck(&parClass, object);
int next;
if (o->desc == NULL) {
free(o);
return 0;
}
if (o->logPending == 2) {
o->logPending = 0;
return 0;
}
ParBegin();
ctx->now = time(NULL);
next = ctx->now - (o->logTime / o->period + 1) * o->period;
if (next < 0) {
return 1;
}
showTime = 1;
if (!secop_logger) {
ParDo(0, o, PAR_LOG, NULL);
o->logTime = ctx->now;
o->logPending = 0;
ParEnd();
}
ParUpdateAll(o);
return 0;
}
/*--------------------------------------------------------------------------*/
void ParLogForced(void *object)
{
ParData *o = ParCheck(&parClass, object);
int next;
ParBegin();
ctx->now = time(NULL);
showTime = 1;
if (!secop_logger) {
ParDo(0, o, PAR_LOG, NULL);
o->logTime = ctx->now;
if (o->logPending) {
o->logPending = 2; /* tell ParLog that we are done already, but task is still pending */
}
ParEnd();
}
ParUpdateAll(o);
}
/*-------------------------------------------------------------------------*/
static int ParCallBack(int event, void *eventData, void *userData)
{
char *pBuf = (char *) eventData;
SConnection *con = (SConnection *) userData;
/* check kill condition */
if (con == NULL || !SCisConnected(con)) {
return -1;
}
if (event == VALUECHANGE) {
SCWrite(con, pBuf, eValue);
return 1;
}
return 1;
}
/*----------------------------------------------------------------------------*/
static int ParOutError(SConnection * con, ParData * o)
{
switch (ctx->returnValue) {
case AMBIGUOS:
SCPrintf(con, eError, "ERROR: doubly defined parameter %s.%s", o->name,
ctx->thisPar);
break;
case ILLPRIV:
SCPrintf(con, eError, "ERROR: insufficient privilege for %s.%s",
o->name, ctx->thisPar);
break;
case ILLNUM:
SCPrintf(con, eError, "ERROR: illegal value %s %s", o->name, ctx->valueArg);
break;
case ILLARGC:
SCPrintf(con, eError, "ERROR: illegal number of arguments for %s %s",
o->name, ctx->thisPar);
break;
case BADLOG:
SCPrintf(con, eError, "ERROR: can not create log directory for %s %s",
o->name, ctx->thisPar);
break;
case UNKPAR:
SCPrintf(con, eError, "ERROR: %s %s is unknown", o->name,
ctx->thisPar);
break;
case PARUNDEF:
SCPrintf(con, eError, "ERROR: %s %s is undefined",
o->name, ctx->thisPar);
break;
default:
if (ctx->returnValue < 0) {
return -1;
} else {
return 1;
}
}
return -1;
}
/*----------------------------------------------------------------------------*/
static void ParListSugar(SConnection * con, ParData * o)
{
ParInfo *p;
char buf[512];
int l, i;
char *sugar;
p = o->infoList;
l = snprintf(buf, sizeof buf, "%s sugar =", o->name);
while (p != NULL) {
if (p->log) {
sugar = LoggerName(p->log);
if (strchr(sugar, '.') == NULL && strcmp(sugar, o->name) != 0) {
i = snprintf(buf + l, sizeof buf - l, " %s", sugar);
if (i <= 0)
break;
l += i;
if (l >= sizeof buf - 1)
break;
}
}
p = p->next;
}
SCWrite(con, buf, eValue);
}
/*----------------------------------------------------------------------------*/
void ParSaveConn(void *object, SConnection * con)
{
ParData *o = ParCheck(&parClass, object);
int rights;
rights = SCGetRights(con);
if (rights >= usMugger && rights <= usUser && con->sockHandle >= 0) {
if (o->conn != NULL) {
if (o->conn->ident != con->ident) {
SCDeleteConnection(o->conn);
o->conn = SCCopyConnection(con);
}
} else {
o->conn = SCCopyConnection(con);
}
SCsetMacro(o->conn, 0);
if (o->conn->write != SCNormalWrite) {
o->conn->write = SCNormalWrite;
}
}
}
/*----------------------------------------------------------------------------*/
static int ParExecute(SConnection * con, SicsInterp * sics, void *object,
int argc, char *argv[])
{
static char *empty[1] = { "" };
long id;
int iret, logIt = 0;
ParData *o = ParCheck(&parClass, object);
char *setArgv[2];
ParInfo *info;
/* debugging
int i;
printf("ParExecute");
for (i=0;i<argc;i++) {
printf(" %s", argv[i]);
}
printf("\n");
*/
if (argc >= 2 && 0 == strcasecmp(argv[1], "loggeditems")) {
pDynString dyn = CreateDynString(124,128);
for (info = o->infoList; info != NULL; info = info->next) {
if (info->log && (argc==2 || strcasecmp(argv[2], info->name) == 0)) {
DynStringConcat(dyn, " ");
DynStringConcat(dyn, argv[0]);
if (info->name[0] != '\0') {
DynStringConcat(dyn, ".");
DynStringConcat(dyn, info->name);
}
}
}
SCWrite(con, GetCharArray(dyn) + 1, eValue);
DeleteDynString(dyn);
return 1;
}
ParBegin();
ctx->callName = argv[0];
ParSaveConn(o, con);
if (argc > 1)
ctx->thisPar = argv[1];
if (argc >= 2 && 0 == strcasecmp(argv[1], "list")) {
ctx->argc = argc - 2;
ctx->argv = argv + 2;
ParDo(con, o, PAR_LIST, NULL);
ParEnd();
return 1;
}
if (argc == 1) { /* no args */
ctx->enumText = 0;
ParDo(con, o, PAR_SHOW, "");
if (ctx->returnValue == 0) {
SCSendOK(con);
ctx->returnValue = 1;
}
} else if ((0 == strcasecmp(argv[1], "log")
|| 0 == strcasecmp(argv[1], "unlog"))) {
if (argc < 3) {
ctx->returnValue = ILLARGC;
ctx->thisPar = argv[1];
} else if (!secop_logger) {
ctx->argc = argc - 3;
ctx->argv = argv + 3;
ctx->doit = toupper(argv[1][0]) != 'U';
ctx->exact = 0;
ParDo(con, o, PAR_LOGSWITCH, argv[2]);
}
} else if ((0 == strcasecmp(argv[1], "save")
|| 0 == strcasecmp(argv[1], "unsave"))) {
if (argc != 3) {
ctx->returnValue = ILLARGC;
} else if (!secop_logger) {
ctx->doit = toupper(argv[1][0]) != 'U';
ParDo(con, o, PAR_SAVESWITCH, argv[2]);
SCparChange(con);
}
} else if (strcmp(argv[1], "interest") == 0) {
if (!o->pCall) {
o->pCall = CreateCallBackInterface();
}
assert(o->pCall);
id = RegisterCallback(o->pCall, VALUECHANGE, ParCallBack,
SCCopyConnection(con), SCDeleteConnection);
SCSendOK(con);
ParEnd();
return 1;
} else if (strcmp(argv[1], "uninterest") == 0) {
if (o->pCall) {
RemoveCallbackCon(o->pCall, con);
}
SCSendOK(con);
ParEnd();
return 1;
} else if (strcmp(argv[1], "sugar") == 0) {
ParListSugar(con, o);
ParEnd();
return 1;
} else if (strcmp(argv[1], "enumText") == 0) {
if (argc == 3) {
ctx->enumText = 1;
ParDo(con, o, PAR_SHOW, argv[2]);
}
} else if (strcmp(argv[1], "endinit") == 0) {
initObj = NULL;
ParEnd();
return 1;
} else {
if (strcmp(argv[1], "=") == 0) {
ctx->argc = argc - 2;
ctx->argv = argv + 2;
ctx->valueArg = argv[2];
ParDo(con, o, PAR_SET, "");
logIt = 1;
} else {
if (argc == 2) {
ctx->enumText = 0;
ctx->argc = 0;
ParDo(con, o, PAR_SHOW, argv[1]);
} else {
ctx->argc = argc - 2;
ctx->argv = argv + 2;
ctx->valueArg = argv[2];
ParDo(con, o, PAR_SET, argv[1]);
logIt = 1;
}
/* parameter not found, try to use args as set value for pure object
not a very good idea: a typo in a parameter name will change pure value
--> use always '='
if (ctx->returnValue == 0) {
ctx->argc = argc - 1;
ctx->argv = argv + 1;
errPar = ctx->thisPar;
ParDo(con, o, PAR_SET, "");
if (ctx->returnValue <= 0) {
ctx->thisPar = errPar;
ctx->returnValue = UNKPAR;
}
}
*/
}
}
if (ctx->returnValue == 0) {
ctx->returnValue = UNKPAR;
}
iret = ParOutError(con, o);
if (logIt)
ParLogForced(o); /* log changes */
ParEnd();
return iret;
}
/*----------------------------------------------------------------------------*/
static void KillLogger(ParInfo * par)
{
if (par->log != NULL && par->log != secop_logger) {
LoggerKill(par->log);
if (par->sugarStatus == 1) {
RemoveCommand(pServ->pSics, LoggerName(par->log));
par->sugarStatus = 0;
}
par->log = NULL;
}
}
/*----------------------------------------------------------------------------*/
int ParSwitchLog(int on, char *name)
{
char buf[80], alias[80];
KillLogger(ctx->par);
if (on) {
if (ctx->par->name[0] == '\0') {
snprintf(buf, sizeof buf, "%s", ctx->obj->name);
} else {
snprintf(buf, sizeof buf, "%s.%s", ctx->obj->name, ctx->par->name);
}
if (name == NULL) {
name = buf;
}
if (loggerDir == NULL) {
loggerDir = IFindOption(pSICSOptions, "LoggerDir");
if (loggerDir == NULL)
loggerDir = "./";
LoggerSetDir(loggerDir);
}
if (!secop_logger) {
ctx->par->log = LoggerMake(name, ctx->obj->period, ctx->exact);
if (ctx->par->log == NULL) {
return BADLOG;
}
}
if (ctx->par->sugarStatus == 0 && name != buf
&& strcmp(name, buf) != 0) {
snprintf(alias, sizeof alias, "%s %s", ctx->obj->name,
ctx->par->name);
ctx->par->sugarStatus = SugarMake(name, alias);
}
}
return 1;
}
/*--------------------------------------------------------------------------*/
void ParFind(void)
{
ParInfo *p, **last, **endList;
assert(ctx);
if (ctx->par == NULL) {
last = &ctx->obj->infoList;
ctx->par = *last;
} else {
last = &ctx->par->next;
}
if (ctx->par) {
/* start search after the actual parameter */
p = ctx->par->next;
while (p != NULL && 0 != strcmp(p->name, ctx->parName)) {
p = p->next;
}
} else {
p = NULL;
}
if (p == NULL) { /* not found: search again from list head */
p = ctx->obj->infoList;
while (p != NULL && 0 != strcmp(p->name, ctx->parName)) {
if (p == ctx->par) {
p = NULL;
break;
}
p = p->next;
}
if (p == NULL) {
p = calloc(1, sizeof *p);
if (p == NULL)
return;
p->name = strdup(ctx->parName);
p->log = NULL;
p->saveIt = 0;
p->saveLog = 0;
p->state = PAR_ALWAYS_READY;
p->node = NULL;
p->next = *last;
*last = p;
}
}
ctx->par = p;
}
/*--------------------------------------------------------------------------*/
long ParText2Int(char *text)
{
long num = 0;
if (strcasecmp(text, "undefined") == 0 || text[0] == '\0') {
return PAR_LNAN;
}
if (ctx->enumList) {
while (ctx->enumList[num] != NULL) {
if (strcasecmp(ctx->enumList[num], text) == 0) {
return num;
}
num++;
}
}
return -1;
}
/*--------------------------------------------------------------------------*/
char *ParInt2Text(int num)
{
int i;
static char buf[12];
if (num == PAR_LNAN) {
return ""; /* undefined */
}
if (!ctx->enumList)
return NULL;
for (i = 0; i <= num; i++) {
if (ctx->enumList[i] == NULL) {
return NULL;
}
}
return ctx->enumList[num];
}
/*----------------------------------------------------------------------------*/
void ParGroup(char *groupFmt)
{
ctx->grpFmt = groupFmt;
}
/*----------------------------------------------------------------------------*/
void ParName(char *name)
{
if (ctx->grpFmt && *ctx->grpFmt) {
snprintf(ctx->combiName, sizeof ctx->combiName, ctx->grpFmt, name);
ctx->parName = ctx->combiName;
} else {
ctx->parName = name;
}
ParFind();
ctx->fmt = NULL;
ctx->action = ctx->act;
assert(ctx);
switch (ctx->act) {
case PAR_SHOW:
case PAR_SET:
case PAR_HDBSET:
ctx->enumList = NULL;
if (0 == strcasecmp(name, ctx->thisPar)) {
ctx->access = -1;
} else {
ctx->action = PAR_NOOP;
}
return;
case PAR_LIST:
ctx->enumList = NULL;
ctx->listTail = NULL;
ctx->doit = -1;
return;
case PAR_LOG:
return;
case PAR_LOGSWITCH:
if (0 == strcasecmp(name, ctx->thisPar)) {
ctx->access = -1;
} else {
ctx->action = PAR_NOOP;
}
return;
case PAR_SAVESWITCH:
if (0 == strcasecmp(name, ctx->thisPar)) {
ctx->access = -1;
} else {
ctx->action = PAR_NOOP;
}
return;
case PAR_INIT:
/* for save: use -1 as default value */
ctx->access = -1;
ctx->doit = -1;
ctx->logName = ""; /* log by default */
ctx->exact = 0; /* not exact by default */
return;
case PAR_GET:
if (0 != strcasecmp(name, ctx->thisPar)) {
ctx->action = PAR_NOOP;
}
return;
case PAR_UPDATE:
ctx->doit = 0; /* affects visibility */
break;
case PAR_SAVE:
case PAR_KILL:
case PAR_NOOP:
return;
}
}
/*----------------------------------------------------------------------------*/
static int RestoreMode(void)
{
assert(ctx);
if (initObj == ctx->obj) {
return 1;
}
initObj = NULL;
return 0;
}
/*----------------------------------------------------------------------------*/
ParOp ParWhat(int numeric)
{
static char buf[80];
char *sp, *lname;
int i;
ParOp op;
int on;
assert(ctx);
switch (ctx->action) {
case PAR_LIST:
if (ctx->doit < 0) {
if (ctx->listTail && ctx->argc == 0) {
ctx->doit = 1;
} else {
ctx->doit = 0;
}
}
if (ctx->doit
|| (ctx->argc > 0 && 0 == strcasecmp(ctx->argv[0], "all")))
return FMT_OP;
break;
case PAR_LOG:
if (ctx->par && ctx->par->log) {
return FMT_OP;
}
break;
case PAR_LOGSWITCH:
if (ctx->argc > 1) {
ctx->returnValue = ILLARGC;
break;
}
if (ctx->returnValue) {
ctx->returnValue = AMBIGUOS;
break;
}
if (ctx->par) {
ctx->returnValue = 1;
if (ctx->argc > 0) {
lname = ctx->argv[0];
} else {
lname = NULL;
}
ctx->par->saveLog = 1;
ParSwitchLog(ctx->doit, lname);
LoggerSetNumeric(ctx->par->log, numeric);
SCparChange(ctx->con);
return NO_OP;
}
break;
case PAR_SAVESWITCH:
if (ctx->argc > 1) {
ctx->returnValue = ILLARGC;
break;
}
if (ctx->returnValue) {
ctx->returnValue = AMBIGUOS;
break;
}
if (ctx->par) {
ctx->returnValue = 1;
ctx->par->saveIt = ctx->doit;
return NO_OP;
}
break;
case PAR_INIT:
if (!RestoreMode()) {
if (ctx->access < 0) {
ctx->access = usInternal;
}
if (ctx->doit < 0) {
if (ctx->access == usInternal) {
ctx->doit = 0;
} else {
ctx->doit = 1;
}
}
if (ctx->doit) { /* set save flag */
if (ctx->par) {
ctx->par->saveIt = 1;
}
}
}
if (ctx->logName) { /* set log */
if (ctx->par) {
if (ctx->logName[0] != '\0') {
lname = ctx->logName;
} else {
lname = NULL;
}
if (secop_logger) {
ctx->par->log = secop_logger;
} else {
ParSwitchLog(1, lname);
LoggerSetNumeric(ctx->par->log, numeric);
}
}
}
return INIT_OP;
case PAR_SHOW:
if (ctx->returnValue) {
ctx->returnValue = AMBIGUOS;
break;
}
ctx->returnValue = 1;
return FMT_OP;
case PAR_HDBSET:
case PAR_SET:
if (ctx->returnValue) {
ctx->returnValue = AMBIGUOS;
break;
}
if (ctx->access < 0) {
ctx->access = usInternal;
}
if (!RestoreMode() && ctx->access < SCGetRights(ctx->con)) {
ctx->returnValue = ILLPRIV;
break;
}
ctx->returnValue = 1;
return SET_OP;
case PAR_SAVE:
if (ctx->parName[0] == '\0') {
ctx->parName = "*";
}
if (ctx->par->saveLog) {
if (ctx->par->log) {
fprintf(ctx->saveFile, "%s log %s %s\n", ctx->obj->name,
ctx->parName, LoggerName(ctx->par->log));
} else {
fprintf(ctx->saveFile, "%s unlog %s\n", ctx->obj->name,
ctx->parName);
}
}
if (ctx->par && ctx->par->saveIt == 1) {
return FMT_OP;
}
break;
case PAR_GET:
if (ctx->returnValue) {
ctx->returnValue = AMBIGUOS;
break;
}
return GET_OP;
case PAR_UPDATE:
return UPD_OP;
default:
return NO_OP;
}
ctx->action = PAR_NOOP; /* for the result of ParActionIs */
return NO_OP;
}
/*----------------------------------------------------------------------------*/
void ParOut(char *buf)
{
int l, i, j, m, ln, lp, iret;
char *p;
char buffer[256];
char *endp;
char *pnam;
char *sep;
char *saved, *logged;
time_t last;
assert(ctx);
assert(ctx->fmt == NULL);
switch (ctx->action) {
case PAR_LIST:
/*
if (ctx->par->log) {
snprintf(buffer, sizeof buffer, "%s", LoggerName(ctx->par->log));
} else {
*/
if (ctx->parName[0] != '\0') {
snprintf(buffer, sizeof buffer, "%s.%s", ctx->callName,
ctx->parName);
} else {
snprintf(buffer, sizeof buffer, "%s", ctx->callName);
}
if (ctx->enumList) {
i = strtol(buf, &endp, 0);
if (endp != buf) {
ctx->listTail = ParInt2Text(i); /* overwrite ctx->listTail */
}
}
p = buf;
/* find dot or end of number */
while (*p == ' ')
p++; /* skip blanks */
i = 0;
if (p[i] == '-')
i++;
j = i;
while (p[i] >= '0' && p[i] <= '9')
i++;
l = strlen(p);
ln = strlen(buffer) - strlen(ctx->callName);
if (i != j && (buf[i] == '.' || buf[i] <= ' ')) {
l += 16 - i - ln; /* decimal point or end of number at least 16 chars after object name */
lp = strlen(p);
if (l < lp)
l = lp;
m = 23 - l - ln; /* unit/comment at least 23 chars after object name */
} else { /* non numeric value */
l = 21 - ln; /* text right justified if possible */
/* m = 1; */
m = 23 - l - ln;
}
if (m < 1)
m = 1;
if (ctx->listTail == NULL)
ctx->listTail = "";
m += strlen(ctx->listTail);
if (l <= 0)
l = 1;
saved = "";
logged = "";
if (ctx->argc > 0 && 0 == strcasecmp(ctx->argv[0], "all")) {
if (!ctx->par->log) {
logged = " (not logged)";
}
if (ctx->par->saveIt == 1) {
saved = " (saved)";
} else if (ctx->par->saveIt == 2) {
saved = " (saved by driver)";
}
}
ParPrintf(NULL, eWarning, "%s %*s%*s%s%s", buffer, l, p, m,
ctx->listTail, logged, saved);
break;
case PAR_SHOW:
if (ctx->enumText) {
i = strtol(buf, &endp, 0);
if (endp != buf) {
p = ParInt2Text(i);
} else {
p = NULL;
}
if (p == NULL)
p = ""; /* undefined */
ParPrintf(NULL, eValue, "%s", p);
break;
}
if (ctx->parName[0]) {
p = " ";
} else {
p = "";
}
ParPrintf(NULL, eValue, "%s%s%s = %s", ctx->callName,
p, ctx->parName, buf);
break;
case PAR_HDBSET:
case PAR_SET:
if (ctx->parName[0]) {
p = " ";
} else {
p = "";
}
ParPrintf(NULL, eValue, "%s%s%s = %s", ctx->callName, p, ctx->parName,
buf);
if (!ctx->obj->logPending) {
ctx->obj->logPending = 1;
TaskRegisterN(pServ->pTasker,"parlog", ParLog, NULL, NULL, ctx->obj, TASK_PRIO_LOW); /* schedule ParLog */
}
break;
case PAR_LOG:
if (ctx->par->log) {
if (ctx->par->state == PAR_NOW_READY) {
ctx->par->state = PAR_NOT_READY;
last = LoggerLastTime(ctx->par->log);
if (last != 0 && ctx->now - ctx->obj->period > last) {
LoggerWrite(ctx->par->log, ctx->now - ctx->obj->period,
ctx->obj->period, buf);
}
} else if (ctx->par->state != PAR_ALWAYS_READY) {
break;
}
if (showTime && ctx->obj->pCall) {
snprintf(buffer, sizeof buffer, "<time> = %ld", (long) ctx->now);
InvokeCallBack(ctx->obj->pCall, VALUECHANGE, buffer);
showTime = 0;
}
iret = LoggerWrite(ctx->par->log, ctx->now, ctx->obj->period, buf);
if (iret && ctx->obj->pCall) {
snprintf(buffer, sizeof buffer, "%s = %s",
LoggerName(ctx->par->log), buf);
InvokeCallBack(ctx->obj->pCall, VALUECHANGE, buffer);
}
}
break;
case PAR_SAVE:
if (strchr(buf, '\n') != NULL) {
fprintf(ctx->saveFile, "%s %s {%s}\n", ctx->obj->name,
ctx->parName, buf);
} else {
fprintf(ctx->saveFile, "%s %s %s\n", ctx->obj->name,
ctx->parName, buf);
}
break;
default:
break;
}
}
/*----------------------------------------------------------------------------*/
void ParLogReady(ReadyState state)
{
if (ctx->action == PAR_LOG && ctx->par) {
ctx->par->state = state;
}
}
/*----------------------------------------------------------------------------*/
void ParFmt(char *format)
{
ctx->fmt = format; /* used for various actions */
}
/*----------------------------------------------------------------------------*/
void ParAccess(int accessRights)
{
ctx->access = accessRights; /* used for PAR_SET & PAR_INIT */
}
/*----------------------------------------------------------------------------*/
void ParEnum(char *list[])
{
ctx->enumList = list; /* used for various actions */
}
/*----------------------------------------------------------------------------*/
void ParSave(int saveIt)
{
if (ctx->action == PAR_INIT) {
ctx->doit = saveIt;
} else if (ctx->action == PAR_SAVE && saveIt == 2 && ctx->par) {
ctx->par->saveIt = 2;
}
}
/*----------------------------------------------------------------------------*/
void ParList(char *group)
{
if (ctx->action == PAR_LIST) {
if (group == NULL && ctx->argc == 0) {
ctx->doit = 1;
} else if (ctx->argc > 0 && group != NULL
&& 0 == strcasecmp(ctx->argv[0], group)) {
ctx->doit = 1;
} else if (ctx->doit < 0) {
ctx->doit = 0;
}
} else if (ctx->action == PAR_UPDATE) {
ctx->doit = group == NULL || group[0] != '\0';
}
}
/*----------------------------------------------------------------------------*/
void ParTail(char *tail)
{
if (ctx->action == PAR_LIST) {
ctx->listTail = tail;
} else if (ctx->action == PAR_UPDATE) {
ctx->doit = 1; /* always visible */
}
}
/*----------------------------------------------------------------------------*/
void ParLogAs(char *name)
{
if (ctx->action == PAR_INIT) {
ctx->logName = name;
}
}
/*----------------------------------------------------------------------------*/
void ParLogExact(int arg)
{
ctx->exact = arg;
}
/*----------------------------------------------------------------------------*/
void ParHasChanged(void)
{
if (ctx->par) {
if (RestoreMode()) {
ctx->par->saveIt = 1;
};
if (ctx->par->saveIt) {
SCparChange(ctx->con);
}
}
}
/*----------------------------------------------------------------------------*/
static hdbCallbackReturn ParUpdateCB(pHdb node, void *userData, pHdbMessage msg) {
hdbDataMessage *setMsg;
if ((setMsg = GetHdbSetMessage(msg))){
ParBegin();
ctx->hvalue = setMsg->v;
ParDo(NULL, userData, PAR_HDBSET, node->name);
ParEnd();
}
return hdbContinue;
}
/*----------------------------------------------------------------------------*/
void ParUpdateNode(hdbValue value) {
pHdb node;
char sicscommand[128];
char path[128];
char secop_id[128];
int skip_unchanged = 1;
if (!ctx->obj->node) {
ctx->obj->secop_module = ctx->obj->name;
if (ctx->parName[0]) {
/* main node is not yet created -> assume it is of type NONE */
node = MakeHipadabaNode(ctx->obj->name, HIPNONE, 0);
if (ctx->obj->secop_module) {
SetHdbProperty(node, "secop_module", ctx->obj->secop_module);
}
} else {
node = MakeSICSHdbPar(ctx->obj->name, ctx->access, value);
if (ctx->par->log && ctx->obj->secop_module) {
snprintf(secop_id, sizeof secop_id, "%s:value", ctx->obj->secop_module);
SetHdbProperty(node, "secop_id", secop_id);
snprintf(path, sizeof path, "/%s", ctx->obj->name);
LogMakeInternal(node, path, 0); /* secop_module */
skip_unchanged = 0;
}
}
ctx->obj->node = node;
AddHipadabaChild(GetHipadabaRoot(), node, NULL);
SetHdbProperty(node, "sicscommand", ctx->obj->name);
SetHdbProperty(node, "group", "_pardef_");
}
node = ctx->par->node;
if (node == NULL) { /* node is not yet set */
if (ctx->parName[0]) {
if (strchr(ctx->parName, '/') != NULL) {
return; /* do not allow parameter names containing '/' */
}
node = AddSICSHdbPar(ctx->obj->node, ctx->parName, ctx->access, value);
if (ctx->par->log && ctx->obj->secop_module) {
snprintf(secop_id, sizeof secop_id, "%s:%s", ctx->obj->secop_module, ctx->parName);
SetHdbProperty(node, "secop_id", secop_id);
snprintf(path, sizeof path, "/%s/%s", ctx->obj->name, ctx->parName);
LogMakeInternal(node, path, 0);
skip_unchanged = 0;
}
ctx->par->node = node;
snprintf(sicscommand, sizeof sicscommand, "%s %s", ctx->obj->name, ctx->parName);
} else {
/* bare object (parameter "") */
node = ctx->obj->node;
ctx->par->node = node;
snprintf(sicscommand, sizeof sicscommand, "%s = ", ctx->obj->name);
}
SetHdbProperty(node, "sicscommand", sicscommand);
AppendHipadabaCallback(node, MakeHipadabaCallback(ParUpdateCB, ctx->obj, NULL));
}
if (ctx->action == PAR_UPDATE) {
char *visible = GetHdbProp(node, "visible");
if (ctx->doit) {
if (visible) {
SetHdbProperty(node, "visible", NULL);
}
} else {
if (!visible || strcasecmp(visible, "false") != 0) {
SetHdbProperty(node, "visible", "false");
}
}
if (GetHdbProp(node, "geterror")) skip_unchanged = 0;
switch (value.dataType) {
case HIPFLOAT:
if (value.v.doubleValue == PAR_NAN) {
if (GetHdbProp(node, "geterror") == NULL) {
SetHdbProperty(node, "geterror", "undefined");
value.v.doubleValue = 0;
UpdateHipadabaPar(node, value, ctx->con);
}
return;
}
if (skip_unchanged && node->value.v.doubleValue == value.v.doubleValue) return;
break;
case HIPINT:
if (value.v.intValue == PAR_LNAN) {
if (GetHdbProp(node, "geterror") == NULL) {
SetHdbProperty(node, "geterror", "undefined");
value.v.intValue = 0;
UpdateHipadabaPar(node, value, ctx->con);
}
return;
}
if (skip_unchanged && node->value.v.intValue == value.v.intValue) return;
break;
case HIPTEXT:
if (skip_unchanged && strcmp(node->value.v.text, value.v.text) == 0) return;
break;
default:
return;
}
}
SetHdbProperty(node, "geterror", NULL);
UpdateHipadabaPar(node, value, ctx->con);
}
/*----------------------------------------------------------------------------*/
void ParFloat(float *value, float defValue)
{
char *endp;
float f;
char buf[132];
switch (ParWhat(1)) {
case SET_OP:
if (ctx->action == PAR_HDBSET) {
*value = ctx->hvalue->v.doubleValue;
break;
}
if (ctx->argc > 1) {
ctx->returnValue = ILLARGC;
return;
}
if (ctx->valueArg) {
f = ParText2Int(ctx->valueArg);
if (f < 0) {
f = strtod(ctx->valueArg, &endp);
if (endp == ctx->valueArg) {
ctx->returnValue = ILLNUM;
break;
}
}
*value = f;
}
ParHasChanged();
/* fall through */
case FMT_OP:
if (*value == PAR_NAN) {
ctx->fmt = NULL;
/* ctx->returnValue = PARUNDEF; */
ParOut("");
} else {
if (ctx->fmt == NULL) {
ctx->fmt = "%.5g";
}
snprintf(buf, sizeof buf, ctx->fmt, *value);
ctx->fmt = NULL;
ParOut(buf);
}
break;
case GET_OP:
ctx->value = *value;
ctx->returnValue = 1;
break;
case INIT_OP:
*value = defValue;
ParUpdateNode(MakeHdbFloat(*value));
break;
case UPD_OP:
ParUpdateNode(MakeHdbFloat(*value));
break;
case NO_OP:
break;
}
}
/*----------------------------------------------------------------------------*/
void ParInt(int *value, int defValue)
{
char *endp;
int i;
char buf[132];
switch (ParWhat(1)) {
case SET_OP:
if (ctx->action == PAR_HDBSET) {
*value = ctx->hvalue->v.intValue;
break;
}
if (ctx->argc > 1) {
ctx->returnValue = ILLARGC;
return;
}
if (ctx->valueArg) {
i = ParText2Int(ctx->valueArg);
if (i < 0) {
i = strtol(ctx->valueArg, &endp, 0);
if (endp == ctx->valueArg) {
ctx->returnValue = ILLNUM;
break;
}
}
*value = i;
}
ParHasChanged();
/* fall through */
case FMT_OP:
if (*value == PAR_LNAN) {
/* ctx->returnValue = PARUNDEF; */
ParOut(""); /* undefined */
} else {
snprintf(buf, sizeof buf, "%d", *value);
ParOut(buf);
}
break;
case GET_OP:
ctx->value = *value;
ctx->returnValue = 1;
break;
case INIT_OP:
*value = defValue;
ParUpdateNode(MakeHdbInt(defValue));
break;
case UPD_OP:
ParUpdateNode(MakeHdbInt(*value));
break;
default:
break;
}
}
/*----------------------------------------------------------------------------*/
void ParStr(char **value, char *defValue)
{
static char *empty = "";
char *to_free;
switch (ParWhat(0)) {
case SET_OP:
to_free = *value;
if (ctx->action == PAR_HDBSET) {
if (to_free) free(to_free);
*value = strdup(ctx->hvalue->v.text);
break;
}
if (to_free) free(to_free);
if (ctx->argc > 0 && strcmp(ctx->argv[0], "=") == 0) {
*value = ParArg2Str(ctx->argc - 1, ctx->argv + 1, NULL, 0);
} else {
*value = ParArg2Str(ctx->argc, ctx->argv, NULL, 0);
}
ParHasChanged();
/* fall through */
case FMT_OP:
if (*value == NULL) {
ParOut(empty);
} else {
ParOut(*value);
}
break;
case INIT_OP:
ctx->exact = 1;
if (defValue == NULL) {
ParUpdateNode(MakeHdbText(""));
} else{
*value = strdup(defValue);
ParUpdateNode(MakeHdbText(*value));
}
break;
case UPD_OP:
if (*value == NULL) {
ParUpdateNode(MakeHdbText(""));
} else {
ParUpdateNode(MakeHdbText(*value));
}
break;
default:
if (ctx->act == PAR_KILL && *value != NULL) {
free(*value);
*value = NULL;
}
break;
}
}
/*----------------------------------------------------------------------------*/
void ParFixedStr(char *value, int maxsize, char *defValue)
{
switch (ParWhat(0)) {
case SET_OP:
if (ctx->action == PAR_HDBSET) {
snprintf(value, maxsize, "%s", ctx->hvalue->v.text);
break;
}
ParArg2Str(ctx->argc, ctx->argv, value, maxsize);
ParHasChanged();
/* fall through */
case FMT_OP:
if (value == NULL)
value = "";
ParOut(value);
break;
case INIT_OP:
ctx->exact = 1;
if (defValue == NULL) {
ParUpdateNode(MakeHdbText(""));
} else{
snprintf(value, maxsize, "%s", defValue);
ParUpdateNode(MakeHdbText(value));
}
break;
case UPD_OP:
ParUpdateNode(MakeHdbText(value));
break;
default:
break;
}
}
/*----------------------------------------------------------------------------*/
int ParCmd(ParCommand cmd, void *userarg)
{
int iret;
if (ctx->action != PAR_SHOW && ctx->action != PAR_SET)
return 0;
if (ctx->access < 0)
ctx->access = usUser;
if (!RestoreMode() && ctx->access < SCGetRights(ctx->con)) {
ctx->returnValue = ILLPRIV;
ctx->action = PAR_NOOP;
return 0;
}
iret = cmd(ctx->obj, userarg, ctx->argc, ctx->argv);
if (iret == 0) {
ctx->returnValue = ERRCMD;
return -1;
}
ctx->returnValue = iret;
return 1;
}
/*----------------------------------------------------------------------------*/
int ParActionIs(ParAct a)
{
if (ctx->action == a)
return 1;
if (ctx->act == a)
return -1;
return 0;
}
/*----------------------------------------------------------------------------*/
char *ParGetValueArg()
{
char *ret;
if (ctx->action == PAR_SET) {
ret = ctx->valueArg;
ctx->valueArg = NULL;
return ret;
} else {
return NULL;
}
}
/*----------------------------------------------------------------------------*/
void *ParObject(void)
{
return ctx->obj;
}
/*----------------------------------------------------------------------------*/
void ParStdDef(void)
{
ParName("verbose");
ParAccess(usUser);
ParSave(0);
ParLogAs(NULL);
ParInt(&ctx->obj->verbose, 1);
ParName("driver");
ParLogAs(NULL);
ParStr(&ctx->obj->desc->name, NULL);
ParName("creationCmd");
ParLogAs(NULL);
ParStr(&ctx->obj->creationCmd, NULL);
}
/*----------------------------------------------------------------------------*/
void ParGetFloat(SConnection * con, void *object, char *name, float *value)
{
ParData *o = ParCheck(&parClass, object);
ParBegin();
ParDo(con, o, PAR_GET, name);
if (ctx->returnValue == 1) {
*value = ctx->value;
} else {
if (ctx->returnValue == 0) {
ctx->returnValue = UNKPAR;
}
if (con != NULL) {
ParOutError(con, o);
}
}
ParEnd();
}
/*----------------------------------------------------------------------------*/
void ParKillThisPar(ParInfo *p)
{
p->next = NULL;
if (p->node) {
RemoveSICSPar(p->node, NULL);
}
KillLogger(p);
if (p->name) {
free(p->name);
}
free(p);
}
/*----------------------------------------------------------------------------*/
void ParKillPar(void *object, char *name)
{
ParData *o = object;
ParInfo *p, **last;
last = &o->infoList;
for (p=o->infoList; p!=NULL; last=&p->next, p=p->next) {
if (strcasecmp(name, p->name) == 0) {
*last = p->next;
ParKillThisPar(p);
return;
}
}
}
/*----------------------------------------------------------------------------*/
void ParKill(void *object)
{
ParData *o = ParCheck(&parClass, object);
ParInfo *p, *q;
ParBegin();
ParDo(NULL, o, PAR_KILL, NULL); /* free dynamic strings and custom content */
if (o->name)
free(o->name);
if (o->creationCmd)
free(o->creationCmd);
if (o->pCall)
DeleteCallBackInterface(o->pCall);
if (o->desc)
DeleteDescriptor(o->desc);
if (o->conn)
SCDeleteConnection(o->conn);
p = o->infoList;
while (p) {
q = p->next;
if (p->node == o->node) {
p->node = NULL; /* avoid removing the object node, this is done below */
}
ParKillThisPar(p);
p = q;
}
if (o->node) {
RemoveSICSPar(o->node, NULL); /* this deletes also all children */
o->node = NULL;
}
if (o->logPending) { /* will be free in scheduled ParLog function */
o->desc = NULL;
} else {
free(o);
}
ParEnd();
if (ctx == NULL && pServ->pSics->iDeleting) { /* finish, no more contexts nested -> free all of them */
while (freeContexts) {
ctx = freeContexts->next;
free(freeContexts);
freeContexts = ctx;
}
ctx = NULL;
}
}
/*----------------------------------------------------------------------------*/
void ParInitPar(void *object, char *name, int logged)
{
ParBegin();
ctx->obj = object;
ctx->parName = name;
ctx->par = NULL;
ParFind();
if (logged) {
if (secop_logger) {
if (ctx->par) {
ctx->par->log = secop_logger;
}
} else {
ParSwitchLog(1, NULL);
}
}
ParEnd();
}
/*----------------------------------------------------------------------------*/
void *ParMake(SConnection * con, char *name, ParClass * class,
ParDef pardef, char *creationCmd)
{
ParData *o;
int i;
assert(pardef);
o = FindCommandData(pServ->pSics, name, class->name);
if (o != NULL) {
SCPrintf(con, eWarning, "overwrite '%s'", name);
RemoveCommand(pServ->pSics, name);
}
o = calloc(1, class->size);
assert(o);
o->class = class;
ParCheck(&parClass, o);
if (!AddCommand(pServ->pSics, name, ParExecute, ParKill, o)) {
SCPrintf(con, eError, "'%s' already exists as different object", name);
free(o);
return NULL;
}
o->name = strdup(name);
assert(o->name);
o->desc = CreateDescriptor(class->name);
assert(o->desc);
o->desc->SaveStatus = ParSaveAll;
SetDescriptorKey(o->desc, "pardef", "1");
o->creationCmd = creationCmd;
o->pCall = NULL;
o->logTime = 0;
o->period = 5;
o->infoList = NULL;
o->verbose = 0;
o->logPending = 0;
o->conn = NULL;
o->node = NULL;
ParSaveConn(o, con);
o->pardef = pardef;
ParBegin();
ParDo(con, o, PAR_INIT, NULL);
SCparChange(con);
initObj = o;
ParEnd();
return o;
}
/*----------------------------------------------------------------------------*/
void ParStartup(void)
{
}