diff --git a/ascon.c b/ascon.c index 8bcdb39e..c05e94c7 100644 --- a/ascon.c +++ b/ascon.c @@ -1,8 +1,5 @@ #include #include -#include -#include -#include #include #include #include @@ -13,69 +10,11 @@ #include "splitter.h" #include "ascon.i" #include "uselect.h" +#include "socketaddr.h" static double lastClose = 0; /* time of last close operation */ static AsconProtocol *protocols = NULL; -static int MakeSocketAdr( - struct sockaddr *sockaddrPtr, /* socket address */ - char *hostname, /* name or ip of host. NULL implies INADDR_ANY */ - int port, /* port number */ - char dotted_ip[16]) { /* resolved ip adr */ - /* - Workaround for the following case: - switching off and on a LakeShore 336 does acquire a new address, - but the value in the cache (for gethostbyname) is not updated. - node: dig seems to be recommended over nslookup - */ - - struct hostent *hostent; /* Host database entry */ - struct sockaddr_in *sadr = (struct sockaddr_in *)sockaddrPtr; - FILE *fil; - char line[256]; - int l; - - (void) memset(sadr, 0, sizeof(*sadr)); - if (dotted_ip) { /* default value: failure */ - dotted_ip[0] = 0; - } - sadr->sin_family = AF_INET; - sadr->sin_port = htons((unsigned short)port); - if (hostname == NULL) { - /* do not need to copy, as INADDR_ANY is all zero bytes */ - return 1; - } - if (inet_pton(AF_INET, hostname, &sadr->sin_addr) == 1) { - /* resolved as dotted numbers notation */ - return 1; - } - hostent = gethostbyname(hostname); - if (hostent == 0) { - /* we assume that when gethostname gets no entry, dig will also fail. - That way, dig will not be called repeatedly for no reason */ - return 0; - } - /* copy the address: in case dig fails, we use the cached value */ - memcpy(&sadr->sin_addr, hostent->h_addr_list[0], 4); - /* we use hostent->h_name instead of hostname here, as this has already - the proper domain added */ - snprintf(line, sizeof line, "timeout 1 dig +short %s", hostent->h_name); - fil = popen(line, "r"); - if (fil != NULL) { - if (fgets(line, sizeof(line), fil) != NULL) { - l = strlen(line); - if (line[l-1] <= ' ') line[l-1] = 0; /* strip off newline */ - /* silently ignore return value, if it fails, we take the cached value */ - inet_pton(AF_INET, line, &sadr->sin_addr); - } - fclose(fil); - } - if (dotted_ip) { - inet_ntop(AF_INET, &sadr->sin_addr, dotted_ip, 16); - } - return 1; -} - void AsconError(Ascon *a, char *msg, int errorno) { static char *stateText[]={ @@ -171,7 +110,7 @@ static void AsconConnect(Ascon * a) return; } *colon = '\0'; - ret = MakeSocketAdr(&adr, a->hostport, port, a->ip); + ret = MakeSocketAddr(&adr, a->hostport, port, a->ip); *colon = ':'; if (ret == 0) { AsconError(a, "bad host specification", 0); @@ -548,6 +487,22 @@ int AsconStdHandler(Ascon * a) return AsconBaseHandler(a); } +static void checkTerminator(char *term) { + int c, l; + + if (term == NULL) return; + l = strlen(term); + if (l > 1 && term[0] == '"' && term[l-1] == '"') { + memmove(term, term+1, l-2); + term[l-2] = 0; + } + if (strncmp(term,"0x",2) == 0) { + sscanf(term,"%x",&c); + term[0] = (char)c; + term[1] = '\0'; + } +} + /** * Treat hex strings as terminators right. Note that this * is limited to single character terminators. @@ -558,18 +513,10 @@ int AsconStdHandler(Ascon * a) */ void AsconCheckTerminators(Ascon *a) { - int c, i, l; + int i, l; - if (a->sendTerminator != NULL && strncmp(a->sendTerminator,"0x",2) == 0) { - sscanf(a->sendTerminator,"%x",&c); - a->sendTerminator[0] = (char)c; - a->sendTerminator[1] = '\0'; - } - if (a->replyTerminator != NULL && strncmp(a->replyTerminator,"0x",2) == 0) { - sscanf(a->replyTerminator,"%x",&c); - a->replyTerminator[0] = (char)c; - a->replyTerminator[1] = '\0'; - } + checkTerminator(a->sendTerminator); + checkTerminator(a->replyTerminator); a->compositeTerminator = 0; if (a->replyTerminator != NULL && a->replyTerminator[0] == '\'') { l = strlen(a->replyTerminator); diff --git a/hipadaba.c b/hipadaba.c index 5eb6666d..40966c28 100644 --- a/hipadaba.c +++ b/hipadaba.c @@ -1157,6 +1157,10 @@ void SetHdbProperty(pHdb node, char *key, char *value) hdbPropertyChange propMes; if (node != NULL && key != NULL && node->properties != NULL) { + propMes.type = propertyChange; + propMes.key = key; + propMes.value = value; + InvokeCallbackChain(node,(pHdbMessage)&propMes); if (value == NULL) { StringDictDelete(node->properties, key); } else if (StringDictExists(node->properties, key)) { @@ -1164,10 +1168,6 @@ void SetHdbProperty(pHdb node, char *key, char *value) } else { StringDictAddPair(node->properties, key, value); } - propMes.type = propertyChange; - propMes.key = key; - propMes.value = value; - InvokeCallbackChain(node,(pHdbMessage)&propMes); } } diff --git a/logger.c b/logger.c index 454a6317..e0922366 100644 --- a/logger.c +++ b/logger.c @@ -49,6 +49,12 @@ Logger *LoggerFind(const char *name) return NULL; } +/*--------------------------------------------------------------------------*/ +Logger *LoggerList(void) +{ + return list; +} + /*--------------------------------------------------------------------------*/ #define LASTLOGTXT "#last logging entry at:\n" @@ -158,8 +164,13 @@ int LoggerWrite0(Logger * log, time_t now, int period, char *value) strftime(path + l, sizeof path - l, "%m-%d.log", &tm); strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", &tm); - if (period <= 0) - period = 1; + if (period <= 0) { + if (log->period > 0) { + period = log->period; + } else { + period = 1; + } + } writeInfo = (tm.tm_isdst != lasttm.tm_isdst || tm.tm_yday != lasttm.tm_yday || (period != log->period && log->numeric)); @@ -373,7 +384,7 @@ void LoggerKill(Logger * log) } /*--------------------------------------------------------------------------*/ -static int LoggerMakeDir(char *path) +int LoggerMakeDir(char *path) { static char buffer[4096]; struct stat st; @@ -473,6 +484,10 @@ void LoggerFreeAll(void) free(p->name); if (p->old) free(p->old); + if (p->secop_old) + free(p->secop_old); + if (p->secop_id) + free(p->secop_id); free(p); p = next; } diff --git a/logger.h b/logger.h index 2f9ab189..39caa1d9 100644 --- a/logger.h +++ b/logger.h @@ -16,12 +16,16 @@ typedef struct Logger { int numeric; int period; int exact; + void *histWriter; + char *secop_old; + char *secop_id; /* private: */ char *old; int oldsize; time_t last, lastWrite, omitTime; float omitValue; struct Logger *next; + int category; /* 0: unimportant, 1: important */ } Logger; @@ -39,5 +43,6 @@ void LoggerChange(Logger * log, int period, char *newname); int LoggerVarPath(char *dir, char *path, int pathLen, char *name, struct tm *t); void LoggerFreeAll(void); - +int LoggerMakeDir(char *path); +Logger *LoggerList(void); #endif diff --git a/logreader.c b/logreader.c index f3258fae..68a3deaf 100644 --- a/logreader.c +++ b/logreader.c @@ -10,7 +10,7 @@ #define LOGGER_NAN -999999. #define ONE_YEAR (366*24*3600) -#define LLEN 1024 +#define LLEN 4096 /* max. number of dirs in path */ #define MAX_DIRS 16 diff --git a/logsetup.c b/logsetup.c index c5da6a9d..bf56035c 100644 --- a/logsetup.c +++ b/logsetup.c @@ -1,8 +1,161 @@ #include "logger.h" +#include "logsetup.h" #include "sics.h" #include "sicshipadaba.h" static char *loggerID = "loggerID"; +static char *histDir = NULL; +/* static char basepath[1024] = "/"; */ + +typedef struct _HistWriter { + char *name; + int old_hour; + FILE *fd; + time_t timestamp; +} HistWriter; + +HistWriter mainWriter={""}; + +int FlushTask(void *data) { + HistWriter *wr = data; + + if (wr->fd) fflush(wr->fd); + return 1; +} + +/*--------------------------------------------------------------------------*/ +int HistPath(char *dir, char *path, int pathLen, struct tm *t) +{ + int l; + + l = strlen(dir); + if (l + 17 >= pathLen) { + path[0] = '\0'; + return 0; + } + strcpy(path, dir); + strftime(path + l, pathLen - l, "/%Y/%Y-%m-%d/", t); + l += 17; + path[l] = '\0'; + return l; +} + +/*--------------------------------------------------------------------------*/ +static void HistWriteType(Logger *logger) +{ + HistWriter *wr = logger->histWriter; + if (logger->numeric) { + fprintf(wr->fd, "#NUM %s %s\n", logger->secop_id, logger->name); + } else { + fprintf(wr->fd, "#STR %s %s\n", logger->secop_id, logger->name); + } +} +/*--------------------------------------------------------------------------*/ +static void HistWrite(Logger *logger, pHdb node, time_t t, char *value) +{ + HistWriter *wr = logger->histWriter; + char path[1024]; + time_t now; + int l; + struct tm tm, tm0; + FILE *fd; + Logger *lg; + char *secop_id; + time_t current_hour; + int hour_of_day; + int reopen = 0; + + secop_id = GetHdbProp(node, "secop_id"); + if (!secop_id || !secop_id[0]) { + if (logger->secop_old) { + free(logger->secop_old); + logger->secop_old = NULL; + } + return; + } + if (!logger->secop_id) logger->secop_id = strdup(""); + if (strcmp(secop_id, logger->secop_id) != 0) { + free(logger->secop_id); + logger->secop_id = strdup(secop_id); + } + time(&now); + tm = *localtime(&now); + current_hour = now / 3600; + if (current_hour != wr->old_hour) { + if (wr->fd) { + fprintf(wr->fd, "# %ld\n#END\n", (wr->timestamp / 3600 + 1) * 3600); + fclose(wr->fd); + wr->fd = NULL; + } + reopen = 1; + } + if (!wr->fd) { + if (histDir == NULL || reopen) { + histDir = IFindOption(pSICSOptions, "HistoryDir"); + if (histDir == NULL) goto failed; + HistPath(histDir, path, sizeof path, &tm); + if (LoggerMakeDir(path) == 0) goto failed; + } else if (!*histDir) { + /* history writing not configured, or failed for current hour */ + return; + } + /* last midnight: */ + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + hour_of_day = (now - mktime(&tm)) / 3600; + l = HistPath(histDir, path, sizeof path, &tm); + strftime(path + l, sizeof path - l, "%Y-%m-%d", &tm); + l += 10; + if (l + 12 >= sizeof path) goto failed; + snprintf(path + l, sizeof path - l, "_%02d_hist.txt", hour_of_day); + fd = fopen(path, "r"); + if (!fd) { + wr->fd = fopen(path, "w"); + if (!wr->fd) goto failed; /* can not create file */ + } else { + fclose(fd); + wr->fd = fopen(path, "a"); + if (!wr->fd) goto failed; /* no write access to file */ + } + fprintf(wr->fd, "#FRAPPY_HISTORY\n"); + if (!wr->old_hour) { + TaskRegisterP(pServ->pTasker, "flush", FlushTask, 0, 0, wr, 0, 0.1, 0.1); + } + wr->old_hour = current_hour; + fprintf(wr->fd, "# %ld\n", t); + wr->timestamp = t; + for (lg = LoggerList(); lg != NULL; lg = lg->next) { + if (lg->secop_old) { + HistWriteType(lg); + fprintf(wr->fd, "%s %s\n", lg->secop_id, lg->secop_old); + } + } + if (logger->secop_old) free(logger->secop_old); + logger->secop_old = NULL; + } + if (logger->secop_old == NULL) { + if (value[0] == '\0') { + return; + } + HistWriteType(logger); + logger->secop_old = strdup(""); + } + if (strcmp(logger->secop_old, value) != 0) { + if (t != wr->timestamp) { + fprintf(wr->fd, "# %ld\n", t); + wr->timestamp = t; + } + fprintf(wr->fd, "%s %s\n", secop_id, value); + free(logger->secop_old); + logger->secop_old = strdup(value); + } + return; +failed: + histDir = ""; +} + static hdbCallbackReturn LoggerUpdateCallback(pHdb node, void *userData, @@ -14,7 +167,9 @@ static hdbCallbackReturn LoggerUpdateCallback(pHdb node, hdbValue value; pHdbDataMessage mm = NULL; pHdbDataSearch dsm = NULL; + pHdbMessage killmsg = NULL; time_t now; + char *strvalue; if ((dsm = GetHdbDataSearchMessage(message)) != NULL) { if (dsm->testPtr == loggerID) { @@ -24,6 +179,18 @@ static hdbCallbackReturn LoggerUpdateCallback(pHdb node, return hdbContinue; } + if ((killmsg = GetHdbKillNodeMessage(message)) != NULL) { + if (logger->histWriter) { + time(&now); + HistWrite(logger, node, now, ""); + if (logger->secop_old) { + free(logger->secop_old); + logger->secop_old = NULL; + } + } + return hdbContinue; + } + if ((mm = GetHdbUpdateMessage(message)) == NULL) { return hdbContinue; } @@ -31,66 +198,43 @@ static hdbCallbackReturn LoggerUpdateCallback(pHdb node, value = *(mm->v); time(&now); + + if (GetHdbProp(node, "geterror") == NULL) { + str = formatValue(value, node); + strvalue = GetCharArray(str); + } else { + str = NULL; + strvalue = ""; + } /* testwise >= */ if (now >= LoggerLastTime(logger)) { /* never write more than once per second */ - if (GetHdbProp(node, "geterror") == NULL) { - str = formatValue(value, node); - LoggerWrite(logger, time(NULL), LoggerPeriod(logger), GetCharArray(str)); - DeleteDynString(str); - } else { - LoggerWrite(logger, time(NULL), LoggerPeriod(logger), ""); - } + LoggerWrite(logger, now, LoggerPeriod(logger), strvalue); + } + if (logger->histWriter) { + HistWrite(logger, node, now, strvalue); + } + if (str) { + DeleteDynString(str); } return hdbContinue; } -static int LogSetup(SConnection * pCon, SicsInterp * pSics, void *pData, - int argc, char *argv[]) -{ - pHdb node; - pHdbCallback cb; - static char basepath[1024] = "/"; +int LogMakeInternal(pHdb node, char *path, int period) { char buf[1024]; - char *p, *name; - static char *loggerDir = NULL; - int numeric, period; + char secop_id[1024]; + char secop_par[1024]; + char *module; + int pos, l; + pHdb nd; + int numeric; Logger *logger; + char *p, *name; + pHdbCallback cb; - if (argc < 2) { - SCPrintf(pCon, eError, - "ERROR: should be: logsetup [ []]"); - /* or logsetup clear */ - return 0; - } - if (strcasecmp(argv[1], "basepath") == 0) { - if (argc > 2) { - snprintf(basepath, sizeof basepath, "%s", argv[2]); - } - SCPrintf(pCon, eValue, "%s", basepath); - return 1; - } - if (loggerDir == NULL) { - loggerDir = IFindOption(pSICSOptions, "LoggerDir"); - if (loggerDir == NULL) - loggerDir = "./"; - LoggerSetDir(loggerDir); - } - if (strcasecmp(argv[1], "directory") == 0) { - if (argc > 2) { - loggerDir = strdup(argv[2]); - } - SCPrintf(pCon, eValue, "%s", loggerDir); - return 1; - } - node = FindHdbNode(basepath, argv[1], pCon); - if (node == NULL) { - SCPrintf(pCon, eError, "ERROR: %s not found", argv[1]); - return 0; - } - if (argc > 3) { - snprintf(buf, sizeof buf, "%s", argv[3]); + if (strncmp(path, "/sics/", 6) == 0) { + snprintf(buf, sizeof buf, "%s", path+5); } else { - snprintf(buf, sizeof buf, "%s", argv[1]); + snprintf(buf, sizeof buf, "%s", path); } for (p = buf; *p != '\0'; p++) { if (*p == '/') @@ -101,29 +245,22 @@ static int LogSetup(SConnection * pCon, SicsInterp * pSics, void *pData, } else { name = buf; } - if (node->value.dataType == HIPFLOAT) { + if (node->value.dataType == HIPFLOAT || node->value.dataType == HIPINT) { numeric = 1; } else { numeric = 0; } logger = FindHdbCallbackData(node, loggerID); - period = 0; - if (argc > 2) { - if (logger != NULL && strcasecmp(argv[2], "clear") == 0) { - LoggerWrite(logger, time(NULL), LoggerPeriod(logger), ""); - return 1; - } - period = atoi(argv[2]); - } if (logger != 0) { /* logger exists already */ LoggerChange(logger, period, name); } else { logger = LoggerMake(name, period, !numeric); /* If that failed, we cannot continue - it crashes in the callback */ if (logger == NULL) { - SCPrintf(pCon, eError, "ERROR: logger %s not created", argv[1]); return 0; } + + logger->histWriter = &mainWriter; LoggerSetNumeric(logger, numeric); SetHdbProperty(node, "logger_name", name); cb = MakeHipadabaCallback(LoggerUpdateCallback, logger, @@ -131,7 +268,70 @@ static int LogSetup(SConnection * pCon, SicsInterp * pSics, void *pData, assert(cb); AppendHipadabaCallback(node, cb); } + return 1; +} +static int LogSetup(SConnection * pCon, SicsInterp * pSics, void *pData, + int argc, char *argv[]) +{ + pHdb node; + static char *loggerDir = NULL; + int period; + Logger *logger; + char *p; + pDynString result; + time_t now; + + if (argc < 2 || argc > 3) { + SCPrintf(pCon, eError, + "ERROR: should be: logsetup []"); + /* or logsetup clear */ + return 0; + } + /* seems not used + if (strcasecmp(argv[1], "basepath") == 0) { + if (argc > 2) { + snprintf(basepath, sizeof basepath, "%s", argv[2]); + } + SCPrintf(pCon, eValue, "%s", basepath); + return 1; + } + */ + if (loggerDir == NULL) { + loggerDir = IFindOption(pSICSOptions, "LoggerDir"); + if (loggerDir == NULL) + loggerDir = "./"; + LoggerSetDir(loggerDir); + } + if (strcasecmp(argv[1], "directory") == 0) { + /* not used ? M.Z. 10.2020 */ + if (argc > 2) { + loggerDir = strdup(argv[2]); + } + SCPrintf(pCon, eValue, "%s", loggerDir); + return 1; + } + node = FindHdbIntern(argv[1]); + if (node == NULL) { + SCPrintf(pCon, eError, "ERROR: %s not found", argv[1]); + return 0; + } + period = 0; /* default period */ + if (argc > 2 && argv[2][0]) { + if (strcasecmp(argv[2], "clear") == 0) { + logger = FindHdbCallbackData(node, loggerID); + time(&now); + if (logger) { /* silently ignore clear on a missing logger */ + LoggerWrite(logger, now, LoggerPeriod(logger), ""); + HistWrite(logger, node, now, ""); + } + return 1; + } + period = atoi(argv[2]); + } + if (!LogMakeInternal(node, argv[1], period)) { + SCPrintf(pCon, eError, "ERROR: logger %s not created", argv[1]); + } return 1; } diff --git a/logsetup.h b/logsetup.h new file mode 100644 index 00000000..6bb8f45c --- /dev/null +++ b/logsetup.h @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------- +logsetup.h + +Markus Zolliker, Oct 2020 +---------------------------------------------------------------------------- +*/ + +#ifndef LOGSETUP_H +#define LOGSETUP_H + +#include "hipadaba.h" + +int LogMakeInternal(pHdb node, char *path, int period); +#endif diff --git a/make_gen b/make_gen index 5674ddd7..a86c9107 100644 --- a/make_gen +++ b/make_gen @@ -8,53 +8,61 @@ #EPICSOBJ=epicsmotor.o EPICSOBJ= -COBJ = Sclient.o network.o ifile.o intcli.o $(FORTIFYOBJ) -SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ - sicvar.o nserver.o SICSmain.o motorlist.o\ +COREOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ + sicvar.o nserver.o SICSmain.o motorlist.o\ sicsexit.o costa.o task.o $(FORTIFYOBJ) testprot.o\ - macro.o ofac.o obpar.o obdes.o drive.o status.o intserv.o \ + macro.o obpar.o obdes.o drive.o status.o intserv.o \ devexec.o mumo.o mumoconf.o selector.o selvar.o fupa.o lld.o \ - lld_blob.o strrepl.o lin2ang.o fomerge.o loglisten.o \ + lld_blob.o strrepl.o lin2ang.o loglisten.o \ script.o o2t.o alias.o stringdict.o sdynar.o \ - histmem.o histdriv.o histsim.o interface.o callback.o \ + interface.o callback.o socketaddr.o \ event.o emon.o evcontroller.o evdriver.o simev.o perfmon.o \ - danu.o nxdict.o varlog.o stptok.o nread.o trigd.o cell.o\ - scan.o fitcenter.o telnet.o token.o wwildcard.o hklmot.o\ - tclev.o hkl.o integrate.o optimise.o dynstring.o nxutil.o \ + danu.o varlog.o stptok.o nread.o trigd.o \ + scan.o fitcenter.o telnet.o token.o wwildcard.o \ + tclev.o integrate.o optimise.o dynstring.o \ uubuffer.o udpquieck.o fourtable.o hexString.o\ - rmtrail.o help.o nxupdate.o confvirtualmot.o vector.o\ - simchop.o choco.o chadapter.o trim.o scaldate.o tasub.o\ - xytable.o exebuf.o exeman.o ubfour.o ubcalc.o\ - circular.o maximize.o sicscron.o scanvar.o tasublib.o\ + rmtrail.o help.o confvirtualmot.o \ + simchop.o choco.o chadapter.o trim.o scaldate.o \ + xytable.o exebuf.o exeman.o \ + circular.o maximize.o sicscron.o scanvar.o \ d_sign.o d_mod.o tcldrivable.o stdscan.o diffscan.o \ - synchronize.o definealias.o oscillate.o tasdrive.o \ - hmcontrol.o userscan.o rs232controller.o lomax.o tasscanub.o \ - fourlib.o motreg.o motreglist.o anticollider.o nxdataset.o \ - s_rnge.o sig_die.o gpibcontroller.o $(NIOBJ) mcreader.o mccontrol.o\ - hmdata.o nxscript.o tclintimpl.o sicsdata.o mcstascounter.o \ - mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \ - sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \ + synchronize.o definealias.o oscillate.o \ + userscan.o rs232controller.o \ + motreg.o motreglist.o anticollider.o \ + s_rnge.o sig_die.o gpibcontroller.o $(NIOBJ) \ + tclintimpl.o sicsdata.o \ + initializer.o remob.o tclmotdriv.o protocol.o \ + sicslist.o hipadaba.o sicshipadaba.o statistics.o \ ascon.o scriptcontext.o logger.o logreader.o logsetup.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 frame.o syncedprot.o\ - nxcopy.o nxinterhelper.o nxinter_wrap.o arrayutil.o \ - sctdriveadapter.o sctdriveobj.o reflist.o singlex.o fourmess.o \ - sgclib.o sgfind.o sgio.o sgsi.o sghkl.o singlediff.o singlebi.o \ - singlenb.o simindex.o simidx.o uselect.o singletas.o motorsec.o \ + moregress.o multicounter.o regresscter.o \ + sicshdbadapter.o polldriv.o sicspoll.o statemon.o \ + nwatch.o asyncqueue.o asyncprotocol.o sicsobj.o syncedprot.o\ + arrayutil.o \ + sctdriveadapter.o sctdriveobj.o reflist.o uselect.o motorsec.o \ rwpuffer.o asynnet.o background.o countersec.o hdbtable.o velosec.o \ - histmemsec.o sansbc.o sicsutil.o strlutil.o genbinprot.o trace.o\ - singlebinb.o taskobj.o sctcomtask.o tasmono.o multicountersec.o\ - lscprot.o secopprot.o\ + sicsutil.o strlutil.o genbinprot.o trace.o\ + taskobj.o sctcomtask.o multicountersec.o\ + lscprot.o secopprot.o secopprot3.o\ messagepipe.o sicsget.o remoteobject.o pmacprot.o charbychar.o binprot.o \ - cnvrt.o tclClock.o tclDate.o tclUnixTime.o stack_trace.o logv2.o outcode.o - + cnvrt.o tclClock.o tclDate.o tclUnixTime.o stack_trace.o logv2.o outcode.o + MOTOROBJ = motor.o simdriv.o COUNTEROBJ = countdriv.o simcter.o counter.o VELOOBJ = velo.o velosim.o -OBJ = $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) $(VELOOBJ) $(DIFIL) $(EXTRA) $(EPICSOBJ) +SICSOBJ = ofac.o fomerge.o \ + histmem.o histdriv.o histsim.o histregress.o hmslave.o fourmess.o histmemsec.o \ + hmcontrol.o fourlib.o lomax.o hmdata.o frame.o singlebinb.o sansbc.o \ + cell.o vector.o hklmot.o hkl.o ubfour.o ubcalc.o cone.o \ + singlex.o sgclib.o sgfind.o sgio.o sgsi.o sghkl.o singlediff.o singlebi.o \ + singlenb.o simindex.o simidx.o singletas.o \ + tasdrive.o tasscanub.o tasub.o tasublib.o tasmono.o \ + nxupdate.o nxdict.o nxutil.o nxdataset.o nxscript.o nxcopy.o nxinterhelper.o nxinter_wrap.o \ + mcreader.o mccontrol.o mcstascounter.o mcstashm.o \ + $(COREOBJ) $(MOTOROBJ) $(COUNTEROBJ) $(VELOOBJ) $(DIFIL) $(EXTRA) $(EPICSOBJ) + +SEAOBJ = sea_ofac.o sea_extra.o $(COREOBJ) $(EXTRA) $(MOTOROBJ) $(COUNTEROBJ) $(VELOOBJ) .SUFFIXES: .SUFFIXES: .tcl .htm .c .o .tc @@ -62,16 +70,24 @@ OBJ = $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) $(VELOOBJ) $(DIFIL) $(EXTRA) $(EPICSOBJ) .tc.c: tjxp $*.tc $*.c +all: seaserver +#all: sicserver + +seaserver: libseapsi SeaServer + +sicserver: libpsi libhlib libmat SICServer + #--- This .SECONDARY. target is necessary to preserve generated .c files for debugging .SECONDARY.: sicspoll.c polldriv.c -all: libmat libhlib libpsi SICServer - # use this target when some of the libraries SUBLIBS might be incomplete full: purge all -SICServer: $(OBJ) $(SUBLIBS) - $(CC) $(DBG) -o SICServer $(OBJ) $(LIBS) +SICServer: libmat libhlib libpsi $(SICSOBJ) $(SUBLIBS) + $(CC) $(DBG) -o SICServer $(SICSOBJ) $(LIBS) + +SeaServer: libseapsi $(SEAOBJ) $(SEASUBLIBS) + $(CC) $(DBG) -o SeaServer $(SEAOBJ) $(SEALIBS) matrix/libmatrix.a: libmat @@ -88,6 +104,9 @@ $(SICSROOT)/sicspsi/libpsi.a: libpsi libpsi: cd $(SICSROOT)/sicspsi; make $(MFLAGS) libpsi.a +libseapsi: + cd $(SICSROOT)/sicspsi; make $(MFLAGS) libseapsi.a + purge: rm -f SICServer $(SUBLIBS) diff --git a/makefile_rhel7 b/makefile_rhel7 index fd6bff90..e3dfcad6 100644 --- a/makefile_rhel7 +++ b/makefile_rhel7 @@ -34,6 +34,11 @@ LIBS = -L$(EXTRAROOT)/lib -L$(EXTRAROOT)/lib $(SUBLIBS) $(NILIB) $(EPICSLIBS) -ltcl -lNeXus /usr/lib64/libhdf5.so \ $(EXTRAROOT)/lib/libjson-c.a \ -ldl -lz -lmxml $(EXTRAROOT)/lib/libghttp.a -lm -lc -lpthread -lsqlite3 -lbson-1.0 -lmongoc-1.0 +SEASUBLIBS = $(SICSROOT)/sicspsi/libseapsi.a +SEALIBS = -L$(EXTRAROOT)/lib -L$(EXTRAROOT)/lib $(SEASUBLIBS) \ + -ltcl \ + $(EXTRAROOT)/lib/libjson-c.a \ + -ldl -lz -lm -lc -lpthread include make_gen diff --git a/network.c b/network.c index f0d75550..53249915 100644 --- a/network.c +++ b/network.c @@ -53,6 +53,7 @@ #include #include "sics.h" #include "uselect.h" +#include "socketaddr.h" #define PORT 1 #define SOCKET 2 @@ -78,43 +79,6 @@ static void NetError(const char pText[]) Log(severity,"io","%s", pText); } -/* ---------------------------- Local ------------------------------------ - CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout -*/ - -static int CreateSocketAdress(struct sockaddr_in *sockaddrPtr, /* Socket address */ - char *host, /* Host. NULL implies INADDR_ANY */ - int port) -{ /* Port number */ - struct hostent *hostent; /* Host database entry */ - struct in_addr addr; /* For 64/32 bit madness */ - - (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); - sockaddrPtr->sin_family = AF_INET; - sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); - if (host == NULL) { - addr.s_addr = INADDR_ANY; - } else { - hostent = gethostbyname(host); - if (hostent != NULL) { - memcpy((char *) &addr, - (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); - } else { - addr.s_addr = inet_addr(host); - if (addr.s_addr == (unsigned int) -1) { - return 0; /* error */ - } - } - } - /* - * There is a rumor that this assignment may require care on - * some 64 bit machines. - */ - - sockaddrPtr->sin_addr.s_addr = addr.s_addr; - return 1; -} - /*-------------------------------------------------------------------------*/ mkChannel *NETOpenPort(int iPort) diff --git a/ofac.c b/ofac.c index 56a1f8d2..746d51d1 100644 --- a/ofac.c +++ b/ofac.c @@ -53,6 +53,7 @@ static void InitGeneral(void) INIT(AddCharByCharProtocoll); INIT(AddLscProtocol); INIT(AddSecopProtocol); + INIT(AddSecop3Protocol); INIT(MakeTrace); INIT(InitTaskOBJ); INIT(RemoteObjectInit); diff --git a/protocol.c b/protocol.c index 139110b6..391e83ce 100644 --- a/protocol.c +++ b/protocol.c @@ -469,10 +469,11 @@ struct json_object *mkJSON_Object(SConnection * pCon, char *pBuffer, char pError[256]; pError[0] = '\0'; - + /* if (strlen(pBuffer) == 0) { return 0; } + */ if (!SCVerifyConnection(pCon)) { return 0; @@ -518,9 +519,14 @@ struct json_object *mkJSON_Object(SConnection * pCon, char *pBuffer, } if (iOut == eHdbValue || iOut == eHdbEvent) { tmp_json = json_tokener_parse(pBuffer); - if (tmp_json == NULL) { - linenum = __LINE__; - goto reporterr; + if (strcmp(pBuffer, "null") != 0) { + if (tmp_json == NULL) { + tmp_json = json_object_new_string(pBuffer); + } + if (tmp_json == NULL) { + linenum = __LINE__; + goto reporterr; + } } } else { /* Strip \r and \n */ @@ -561,9 +567,11 @@ int SCWriteJSON_String(SConnection * pCon, char *pBuffer, int iOut) char pBueffel[MAXMSG], errBuff[MAXMSG]; int iRet, errLen = MAXMSG; + /* if (strlen(pBuffer) == 0) return 1; - + */ + /* log it for any case */ iRet = SCGetSockHandle(pCon); diff --git a/script.c b/script.c index df584b58..646050db 100644 --- a/script.c +++ b/script.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "fortify.h" #include "sics.h" #include "fupa.h" @@ -583,7 +584,7 @@ Tcl_Obj *json2tcl(struct json_object *jobj) { char buf[100]; int i, len; Tcl_Obj **list; - Tcl_Obj *dict, *kobj, *vobj; + Tcl_Obj *result, *kobj, *vobj; Tcl_Obj *val; const char *key; const char *str; @@ -605,18 +606,20 @@ Tcl_Obj *json2tcl(struct json_object *jobj) { list[i] = json2tcl(json_object_array_get_idx(jobj, i)); if (!list[i]) return NULL; } - return Tcl_NewListObj(len, list); + result = Tcl_NewListObj(len, list); + free(list); + return result; case json_type_object: - dict = Tcl_NewDictObj(); - if (!dict) return NULL; + result = Tcl_NewDictObj(); + if (!result) return NULL; json_object_object_foreach(jobj, key, val) { kobj = Tcl_NewStringObj(key, strlen(key)); vobj = json2tcl(val); if (kobj && vobj) { - Tcl_DictObjPut(NULL, dict, kobj, vobj); + Tcl_DictObjPut(NULL, result, kobj, vobj); } } - return dict; + return result; } return Tcl_NewStringObj("None", 4); } diff --git a/scriptcontext.c b/scriptcontext.c index fb5c6596..bb9c0d4e 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -344,14 +344,16 @@ int SctCallInContext(SConnection * con, char *script, Hdb * node, char path[MAX_HDB_PATH]; PushContext(node, controller); - if (controller->debugConn) { - GetHdbPath(node, path, sizeof path); - SCPf(SCPureSockWrite, controller->debugConn, eLog, - "%6.3f script: (on %s) %s", secondsOfMinute(), path, script); - } - if (controller->fd != NULL) { - GetHdbPath(node, path, sizeof path); - fprintf(controller->fd,"%6.3f script: (on %s) %s\n", secondsOfMinute(), path, script); + if (script[strlen(script)-1] != '_') { /* suppress these debug messages */ + if (controller->debugConn) { + GetHdbPath(node, path, sizeof path); + SCPf(SCPureSockWrite, controller->debugConn, eLog, + "%6.3f script: (on %s) %s", secondsOfMinute(), path, script); + } + if (controller->fd != NULL) { + GetHdbPath(node, path, sizeof path); + fprintf(controller->fd,"%6.3f script: (on %s) %s\n", secondsOfMinute(), path, script); + } } MacroPush(con); @@ -366,12 +368,18 @@ int SctCallInContext(SConnection * con, char *script, Hdb * node, result++; } } - if (controller->debugConn) { - SCPf(SCPureSockWrite, controller->debugConn, eLog, - "%6.3f error: %s", secondsOfMinute(), result); - } - if(controller->fd != NULL){ + if (script[strlen(script)-1] != '_') { /* suppress these error messages */ + if (controller->debugConn) { + SCPf(SCPureSockWrite, controller->debugConn, eLog, + "%6.3f error: %s", secondsOfMinute(), result); + } else { + /* just return to tcl */ + SCPf(SCNotWrite, controller->conn, eLog, + "%6.3f error: %s", secondsOfMinute(), result); + } + if(controller->fd != NULL){ fprintf(controller->fd, "%6.3f error: %s\n", secondsOfMinute(), result); + } } iRet = 0; } @@ -390,6 +398,11 @@ static int SctMatch(void *data1, void *data2) return a->node == b->node && strcasecmp(a->name, b->name) == 0; } +static int NoMatch(void *data1, void *data2) +{ + return 0; +} + /* * This routine is running the script chain. It is called repeatedly * with response data from the device serializer (devser). This function @@ -622,8 +635,16 @@ static char *SctActionHandler(void *actionData, char *lastReply, data->name, path, emsg); SetHdbProperty(node, eprop, NULL); } - snprintf(timeKey, sizeof timeKey, "%s_time", data->name); + blank = strchr(data->name, ' '); + if (blank != NULL) { + l = blank - data->name; + } else { + l = strlen(data->name); + } + /* omit arguments, in case action is a script */ + snprintf(timeKey, sizeof timeKey, "%.*s_time", (int)l, data->name); snprintf(timeVal, sizeof timeVal, "%.3f", DoubleTime()); + SetHdbProperty(node, timeKey, timeVal); send = NULL; free(script); @@ -644,17 +665,19 @@ static char *SctActionHandler(void *actionData, char *lastReply, send = GetProp(node, controller->node, "send"); if (send == NULL) send = ""; - if (controller->debugConn) { - SCPf(SCPureSockWrite, controller->debugConn, eLog, - "%6.3f send : %s", secondsOfMinute(), send); - } - if (controller->fd != NULL) { - fprintf(controller->fd, "%6.3f send : %s\n", secondsOfMinute(), send); - } - if(data != NULL && data->controller != NULL){ - traceIO(data->controller->node->name, "send:%s", send); - } else { - traceIO("sctunknown", "send:%s", send); + if (*send != '\0') { + if (controller->debugConn) { + SCPf(SCPureSockWrite, controller->debugConn, eLog, + "%6.3f send : %s", secondsOfMinute(), send); + } + if (controller->fd != NULL) { + fprintf(controller->fd, "%6.3f send : %s\n", secondsOfMinute(), send); + } + if(data != NULL && data->controller != NULL){ + traceIO(data->controller->node->name, "send:%s", send); + } else { + traceIO("sctunknown", "send:%s", send); + } } return send; } @@ -852,6 +875,7 @@ static hdbCallbackReturn SctActionCallback(Hdb * node, void *userData, SCPrintf(con, eError, "ERROR: %s", error); /* nicer for the user */ SetHdbProperty(node, "target", NULL); SetHdbProperty(node, "requested", NULL); + DeleteDynString(text); return hdbAbort; } } @@ -1194,7 +1218,7 @@ static int SctWriteCmd(pSICSOBJ ccmd, SConnection * con, } void SctQueueNode(SctController * controller, Hdb * node, - DevPrio prio, char *action, SConnection * con) + DevPrio prio, char *action, int replace, SConnection * con) { SctData *data; hdbCallback *cb; @@ -1215,8 +1239,9 @@ void SctQueueNode(SctController * controller, Hdb * node, data->syncid = SyncedIncr(0); data->busy = 1; - if (DevQueue(data->controller->devser, data, prio, - SctWriteHandler, SctMatch, SctKillData, SctDataInfo)) { + if (DevQueue(data->controller->devser, data, prio, SctWriteHandler, + replace ? SctMatch : NoMatch, + SctKillData, SctDataInfo)) { if (con != NULL) { data->conCtx = SCCopyConnection(con); } @@ -1257,7 +1282,8 @@ static int SctQueueCmd(pSICSOBJ ccmd, SConnection * con, } action = ParText(cmdNode, "action", nPar, "write"); - SctQueueNode(controller, node, prio, action, con); + SctQueueNode(controller, node, prio, action, + strcmp(cmdNode->name, "que") != 0, con); return 1; } @@ -1970,13 +1996,18 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, "write", usMugger, MakeSICSFunc(SctWriteCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); - cmd = AddSICSHdbPar(controller->node, "queue", usMugger, MakeSICSFunc(SctQueueCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("write")); AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("write")); + cmd = AddSICSHdbPar(controller->node, + "que", usMugger, MakeSICSFunc(SctQueueCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("write")); + AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("write")); + cmd = AddSICSHdbPar(controller->node, "updatescript", usMugger, MakeSICSFunc(SctUpdatescriptCmd)); AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); diff --git a/scriptcontext.h b/scriptcontext.h index a2dc4114..656abd39 100644 --- a/scriptcontext.h +++ b/scriptcontext.h @@ -14,10 +14,11 @@ typedef struct SctController SctController; * \param node the node * \param prio the priority * \param action the initial state + * \param replace 0 queue does not replace, 1: entries with the same node and action are replaced * \param con an optional connection to be used by the action scripts */ void SctQueueNode(SctController * controller, Hdb * node, - DevPrio prio, char *action, SConnection * con); + DevPrio prio, char *action, int replace, SConnection * con); /** \brief call a script and configure the sct command to be used * in connection with the given node and controller diff --git a/sctdriveadapter.c b/sctdriveadapter.c index 08b872d2..b0f1cc4a 100644 --- a/sctdriveadapter.c +++ b/sctdriveadapter.c @@ -56,7 +56,7 @@ static int SCTDRIVHalt(void *data) self = (pSctDrive) data; if (GetHdbProperty(self->node, "halt", dummy, sizeof dummy)) { - SctQueueNode(self->c, self->node, HaltPRIO, "halt", NULL); + SctQueueNode(self->c, self->node, HaltPRIO, "halt", 1, NULL); } else if (GetHdbProperty(self->node, "status", dummy, sizeof dummy)) { SetHdbProperty(self->node, "status", "idle"); } diff --git a/sctdriveobj.c b/sctdriveobj.c index 4c69deff..3198a124 100644 --- a/sctdriveobj.c +++ b/sctdriveobj.c @@ -79,7 +79,7 @@ static int SCTDRIVHalt(void *data) self = (pSICSOBJ) data; pPriv = (pDrivObjPriv) self->pPrivate; if (GetHdbProperty(self->objectNode, "halt", dummy, sizeof dummy)) { - SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", + SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", 1, pPriv->pCon); } else if (GetHdbProperty(self->objectNode, "status", dummy, sizeof dummy)) @@ -177,7 +177,8 @@ static int SCTDRIVCheckStatus(void *data, SConnection * pCon) Tcl_Interp *pTcl = NULL; char *result; SConnection *con; - + int ret; + self = (pSICSOBJ) data; pPriv = (pDrivObjPriv) self->pPrivate; @@ -186,7 +187,8 @@ static int SCTDRIVCheckStatus(void *data, SConnection * pCon) */ if (GetHdbProperty(self->objectNode, "writestatus", script, 1024)) { if (strcmp(script, "start") == 0) { - return HWBusy; + ret = HWBusy; + goto Return; } } @@ -198,7 +200,9 @@ static int SCTDRIVCheckStatus(void *data, SConnection * pCon) SCWrite(pCon, "ERROR: configuration problem: no checkstatus script!", eError); - return HWFault; + ret = HWFault; + result = "error"; + goto Return; } result = script; } else { @@ -206,7 +210,9 @@ static int SCTDRIVCheckStatus(void *data, SConnection * pCon) pPriv->c, &result); if (status == 0) { SCPrintf(pCon, eError, " script %s returned %s", script, result); - return HWFault; + ret = HWFault; + result = "error"; + goto Return; } if (SctDebugConn(pPriv->c)) { SCPf(SCPureSockWrite, SctDebugConn(pPriv->c), eError, @@ -214,20 +220,28 @@ static int SCTDRIVCheckStatus(void *data, SConnection * pCon) } } if (strstr(result, "run") != NULL) { - return HWBusy; + ret = HWBusy; } else if (strstr(result, "posfault") != NULL) { - return HWPosFault; + ret = HWPosFault; } else if (strstr(result, "error") != NULL) { - return HWFault; + ret = HWFault; } else if (strstr(result, "idle") != NULL) { - return HWIdle; + ret = HWIdle; } else { SCPrintf(pCon, eError, "ERROR: invalid status code %s returned from checkstatus script", result); - return HWFault; + ret = HWFault; } - return HWFault; +Return: + if (GetHdbProperty(self->objectNode, "updatestatus", script, 1024)) { + status = SctCallInContext(pCon, script, self->objectNode, + pPriv->c, &result); + if (status == 0) { + SCPrintf(pCon, eError, " %s returned %s", script, result); + } + } + return ret; } /*---------------------------------------------------------------- diff --git a/sea_extra.c b/sea_extra.c new file mode 100644 index 00000000..ffa7ac7f --- /dev/null +++ b/sea_extra.c @@ -0,0 +1,145 @@ +/* + * Extracts from other SICS code and dummy definitions for SEA + * + * Markus Zolliker Jun 2020 + */ + +#include +#include +#include +#include +#include +#include +#include + +pCounterDriver NewMcStasCounter(char *name) { + return NULL; +} + +float GetHistPreset(pHistMem self) {return 0;}; +int SetHistPreset(pHistMem self, float fVal) {return 0;}; +CounterMode GetHistCountMode(pHistMem self) {return 0;}; +int SetHistCountMode(pHistMem self, CounterMode eNew) {return 0;}; +long GetHistMonitor(pHistMem self, int i, SConnection *pCon) {return 0;}; +const float *GetHistTimeBin(pHistMem self, int *iLength) {return 0;}; +int GetHistLength(pHistMem self) {return 0;}; +int GetHistDim(pHistMem self, int iDim[MAXDIM], int *nDim) {return 0;}; +float GetHistCountTime(pHistMem self,SConnection *pCon) {return 0;}; +int GetHistogram(pHistMem self, SConnection *pCon, + int i,int iStart, int iEnd, HistInt *lData, int iDataLen) {return 0;}; +HistInt *GetHistogramPointer(pHistMem self,SConnection *pCon) {return 0;}; +int GetHistogramDirect(pHistMem self, SConnection *pCon, + int i, int iStart, int iEnd, + HistInt *lData, int iDataLen) {return 0;}; +void HistDirty(pHistMem self) {}; +HistInt *subSample(pHMdata self, char *command, char *error, int errLen) {return 0;}; +int Nxinter_SafeInit(Tcl_Interp * pTcl) {return 0;}; +int NXcopy_Init(Tcl_Interp * pTcl) {return 0;}; +int isNXScriptWriting(void) {return 0;}; + +/* alternative implementation in order to avoid nexus stuff */ + +void SNXFormatTime(char *result, int size) { + time_t timeStamp; + time(&timeStamp); + strftime(result, size, "%Y-%m-%d %H:%M:%S", localtime(&timeStamp)); +} + +/* from nxscript.c: */ + +/*------------------------------------------------------------------------*/ +char *makeFilename(SicsInterp * pSics, SConnection * pCon) +{ + pSicsVariable pPath = NULL, pPref = NULL, pEnd = NULL; + char *pRes = NULL; + int iLen, iNum, iYear, thousand; + char pNumText[10], pBueffel[256]; + CommandList *pCom = NULL; + DIR *dir = NULL; + + /* Try, get all the Variables */ + pPath = FindVariable(pSics, "sicsdatapath"); + pPref = FindVariable(pSics, "sicsdataprefix"); + pCom = FindCommand(pSics, "sicsdatanumber"); + pEnd = FindVariable(pSics, "sicsdatapostfix"); + + if ((!pPath) || (!pPref) || (!pCom) || (!pEnd)) { + SCWrite(pCon, + "ERROR: cannot read variables for automatic data file name creation", + eError); + SCWrite(pCon, + "ERROR: This is a VERY, VERY, VERY serious installation problem", + eError); + SCWrite(pCon, "ERROR: your data will be dumped into emergency.hdf", + eError); + return NULL; + } + + /* find length */ + iLen = strlen(pPath->text) + 4; /* extra 4 for dir number */ + iLen += strlen(pPref->text); + iLen += 10; /* for number + year */ + iLen += strlen(pEnd->text); + iLen += 10; /* safety margin */ + + /* allocate memory */ + pRes = (char *) malloc(iLen * sizeof(char)); + if (!pRes) { + SCWrite(pCon, "ERROR: no memory in makeFilename", eError); + return NULL; + } + memset(pRes, 0, iLen); + + /* increment the data file number */ + iNum = IncrementDataNumber(pCom->pData, &iYear); + if (iNum < 0) { + SCWrite(pCon, "ERROR: cannot increment data number!", eError); + SCWrite(pCon, "ERROR: your data will be dumped to emergency.hdf", + eError); + free(pRes); + return NULL; + } + strcpy(pRes, pPath->text); + thousand = (int) floor(iNum / 1000.); + snprintf(pNumText, 9, "%3.3d", thousand); + strcat(pRes, pNumText); + + /* + check for existence of directory and create if neccessary + */ + dir = opendir(pRes); + if (dir == NULL) { + mkdir(pRes, S_IRWXU | S_IRGRP | S_IXGRP); + snprintf(pBueffel, 255, "Creating dir: %s", pRes); + SCWrite(pCon, pBueffel, eLog); + } else { + closedir(dir); + } + + /* + build the rest of the filename + */ + strcat(pRes, "/"); + strcat(pRes, pPref->text); + snprintf(pNumText,sizeof(pNumText)-1, "%4.4d", iYear); + strcat(pRes, pNumText); + strcat(pRes, "n"); + snprintf(pNumText,sizeof(pNumText)-1, "%6.6d", iNum); + strcat(pRes, pNumText); + strcat(pRes, pEnd->text); + + return pRes; +} + +/*---------------------------------------------------------------------*/ +void changeExtension(char *filename, char *newExtension) +{ + char *pPtr = NULL; + + pPtr = strrchr(filename, (int) '.'); + assert(pPtr != NULL); + + pPtr++; + assert(strlen(pPtr) >= strlen(newExtension)); + strcpy(pPtr, newExtension); +} diff --git a/sea_macro.c b/sea_macro.c new file mode 100644 index 00000000..e330847e --- /dev/null +++ b/sea_macro.c @@ -0,0 +1,1068 @@ +/*-------------------------------------------------------------------------- + + All you need to evaluate macros with SICS + + The implementation for the macro stuff is complex and non intuitive. + This is the price to pay for adding the extremly powerful and + strong Tcl-interpreter to SICS. The problem is that Tcl does not + know anything about connections and our error handling. We have + to transport this around it. This is done via the unknown mechanism. + The unknown-command is called any time Tcl does not find a command + in its command list. Our unknown calls Sics-objects than and + provides proper connection and Sics-information. As this unknown is + going to hold the connection info, we need one interpreter per + connection. In order to transport interrupt conditions (motor + going bang) we need an interrupt code field in Connection. + + Mark Koennecke, November 1996 + + Mark Koennecke, April 1999 + where code added. + + Mark Koennecke, December 1999 + InternalFileEval added + + Mark Koennecke, May 2004 + Added protected exec called sys. + + Markus Zolliker, May 2020 + reduced version for SEA only + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +--------------------------------------------------------------------------*/ +#include "fortify.h" +#include +#include +#include +#include +#include +#include +#include "status.h" +#include "macro.h" +#include "splitter.h" +#include "ifile.h" +#include "Dbg.h" +#include "sicsglobal.h" +#include "stringdict.h" +#include "exeman.h" + +#define SICSERROR "005567SICS" +/*---------------------------------------------------------------------------- + SICS Macro uses the Tcl "unknown" mechanism to access the Sics-objects. + The Tcl algorithm is to call a command called unknown when it cannot + match a command with the Tcl-built ins. Sics will now define the unknown + to call the Sics object invocation mechanism. In order to do this + properly the unknown needs to know about the connection invoking the + command and the SicsInterpreter to use. A suitable datastructure will + be defined below. And a function which be planted into Tcl to do the + trick. +-----------------------------------------------------------------------------*/ +#define MAXSTACK 50 + + +struct __SicsUnknown { + SConnection *pCon[MAXSTACK]; + char *lastUnknown[MAXSTACK]; + int iStack; + SicsInterp *pInter; +}; + +static struct __SicsUnknown *pUnbekannt = NULL; +/*---------------------------------------------------------------------------*/ +int MacroPush(SConnection * pCon) +{ + assert(pUnbekannt); + pUnbekannt->iStack++; + if (pUnbekannt->iStack >= MAXSTACK) { + SCWrite(pCon, "ERROR: Out of Stack in macro.c", eError); + return 0; + } + pUnbekannt->pCon[pUnbekannt->iStack] = pCon; + return 1; +} + +/*-------------------------------------------------------------------------*/ +int MacroPop(void) +{ + assert(pUnbekannt); + pUnbekannt->iStack--; + if (pUnbekannt->iStack < 1) { + pUnbekannt->iStack = 0; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +SConnection *MacroPeek(void) +{ + assert(pUnbekannt); + return pUnbekannt->pCon[pUnbekannt->iStack]; +} +/*---------------------------------------------------------------------------*/ +static int SicsUnknownProc(ClientData pData, Tcl_Interp * pInter, + int argc, char *argv[]) +{ + struct __SicsUnknown *pSics = NULL; + char **myarg = NULL; + int margc; + SicsInterp *pSinter = NULL; + SConnection *pCon = NULL; + CommandList *pCommand = NULL; + char *lastCommand = NULL, comBuffer[132]; + int iRet = 0, i; + int iMacro; + Statistics *old; + + /* get the datastructures */ + pSics = (struct __SicsUnknown *) pData; + assert(pSics); + + if(pSics->iStack >= MAXSTACK -1) { + Tcl_SetResult(pInter,"ERROR: cyclic call or to deep a nesting of SICSUnknown", + TCL_VOLATILE); + return 0; + } + pSinter = pSics->pInter; + pCon = pSics->pCon[pSics->iStack]; + lastCommand = pSics->lastUnknown[pSics->iStack]; + + assert(pSinter); + assert(pCon); + + + /* shorten the argc, argv by one and invoke */ + margc = argc - 1; + myarg = &argv[1]; + + /* find object */ + if (margc < 1) { + Tcl_SetResult(pInter, "No command found", TCL_VOLATILE); + return TCL_ERROR; + } + pCommand = FindCommand(pSinter, myarg[0]); + if (!pCommand) { + Tcl_AppendResult(pInter, "Object ", myarg[0], " not found", NULL); + return TCL_ERROR; + } + + /* invoke */ + SCSetSicsError(pCon,0); + iMacro = SCinMacro(pCon); + SCsetMacro(pCon, 1); + old = StatisticsBegin(pCommand->stat); + iRet = pCommand->OFunc(pCon, pSinter, pCommand->pData, margc, myarg); + StatisticsEnd(old); + SCsetMacro(pCon, iMacro); + + /* + lastUnkown gets deeply stacked with each SICS command exec'd. + This is not reflected in code. However, lastUnknown has already + done its job here, so it is safe to do it the way it is done + */ + if (pSics->lastUnknown[pSics->iStack] != NULL) { + free(pSics->lastUnknown[pSics->iStack]); + pSics->lastUnknown[pSics->iStack] = NULL; + } + + /* finish */ + if (iRet == 1) { + Arg2Text(argc-1,&argv[1],comBuffer,sizeof(comBuffer)); + /* + suppress the sct commands: there is no point in having them in + the history + if(strstr(argv[1],"sct") == NULL){ + Log(DEBUG,"history","%s",comBuffer); + } + */ + return TCL_OK; + } else { + Tcl_SetVar(pInter, SICSERROR, "yes", TCL_GLOBAL_ONLY); + SCSetSicsError(pCon,1); + return TCL_ERROR; + } +} + + /*-----------------------------------------------------------------------*/ +static void UnknownKill(ClientData pData) +{ + struct __SicsUnknown *pU = NULL; + + pU = (struct __SicsUnknown *) pData; + if (pU->pCon[0]) { + SCDeleteConnection(pU->pCon[0]); + } + free(pData); + pUnbekannt = NULL; +} + + /*-----------------------------------------------------------------------*/ +void KillSicsUnknown(void) +{ + if (pUnbekannt) { + UnknownKill(pUnbekannt); + pUnbekannt = NULL; + } +} + +/*------------------------------------------------------------------------ + Implementation of a protected exec command + --------------------------------------------------------------------------*/ +static pStringDict allowedCommands = NULL; +/*----------------------------------------------------------------------*/ +int AllowExec(SConnection * pCon, SicsInterp * pSics, void *pData, + int argc, char *argv[]) +{ + if (argc < 2) { + SCWrite(pCon, "ERROR: not enough arguments to allowexec", eError); + return 0; + } + if (!SCMatchRights(pCon, usInternal)) { + return 0; + } + if (allowedCommands == NULL) { + allowedCommands = CreateStringDict(); + if (allowedCommands == NULL) { + SCWrite(pCon, + "ERROR: not enough memory for list of allowed system commands", + eError); + return 0; + } + } + StringDictAddPair(allowedCommands, argv[1], "Allowed!"); + return 1; +} + +/*--------------------------------------------------------------------*/ +static void KillExec(ClientData data) +{ + if (allowedCommands != NULL) { + DeleteStringDict(allowedCommands); + allowedCommands = NULL; + } +} + +/*-----------------------------------------------------------------------*/ +/* static Tcl_ObjCmdProc oldExec = NULL; */ +static int (*oldExec)(ClientData clientData,Tcl_Interp *interp, + int objc,Tcl_Obj *const objv[] ) = NULL; +/*-----------------------------------------------------------------------*/ +static int ProtectedExec(ClientData clientData, Tcl_Interp * interp, + int objc, Tcl_Obj * CONST objv[]) +{ + char *test = NULL; + + if (objc < 2) { + return oldExec(clientData, interp, objc, objv); + } + + test = Tcl_GetStringFromObj(objv[1], NULL); + if (allowedCommands != NULL) { + if (StringDictExists(allowedCommands, test)) { + return oldExec(clientData, interp, objc, objv); + } + } + + /* + if we are here, we are not allowed to invoke this command + */ + Tcl_AppendResult(interp, "System command NOT allowed!", NULL); + return TCL_ERROR; +} + +/*-------------------------------------------------------------------------- + initialises a Tcl-Interpreter, installs SICS unknown mechanism and kills + a few dangerous commands from the normal Tcl command set +----------------------------------------------------------------------------*/ + +Tcl_Interp *MacroInit(SicsInterp * pSics) +{ + Tcl_Interp *pInter = NULL; + struct __SicsUnknown *pUnknown = NULL; + char *pPtr = NULL; + char *pPtr2 = NULL; + FILE *fp = NULL; + char pBueffel[512]; + int iRet; + Tcl_CmdInfo execInfo; + + assert(pSics); + + /* create interpreter and unknown */ + pInter = Tcl_CreateInterp(); + pUnknown = (struct __SicsUnknown *) malloc(sizeof(struct __SicsUnknown)); + if ((!pInter) || (!pUnknown)) { + return NULL; + } + + /* install unknown command */ + memset(pUnknown, 0, sizeof(struct __SicsUnknown)); + pUnknown->pCon[0] = SCCreateDummyConnection(pSics); + pUnknown->iStack = 0; + pUnknown->pInter = pSics; + pUnbekannt = pUnknown; + /* the cast before SicsUnknwonProc is to avoid a warning + Tcl_CmdProc has a const char instead of char as argument M.Z. */ + Tcl_CreateCommand(pInter, "unknown", (Tcl_CmdProc *) SicsUnknownProc, + pUnknown, UnknownKill); + + /* delete dangers */ + Tcl_DeleteCommand(pInter, "exit"); + Tcl_DeleteCommand(pInter, "socket"); + Tcl_DeleteCommand(pInter, "vwait"); + Tcl_GetCommandInfo(pInter,"exec",&execInfo); + oldExec = execInfo.objProc; + Tcl_DeleteCommand(pInter, "exec"); + + /* + install protected exec command + */ + Tcl_CreateObjCommand(pInter, "exec", ProtectedExec, NULL, KillExec); + + return pInter; +} + +/*--------------------------------------------------------------------------*/ +void MacroDelete(Tcl_Interp * pInter) +{ + Tcl_DeleteInterp(pInter); +} + +/*------------------------------------------------------------------------- + Find the first word of a command string +*/ +static void FirstWord(char *pSource, char *pTarget) +{ + int i, iLength, iStart, iPos; + + iLength = strlen(pSource); + + /* find start */ + for (i = 0, iStart = 0; i < iLength; i++) { + if (pSource[i] != ' ') { + break; + } else { + iStart++; + } + } + + /* do a quick check */ + if (iStart >= iLength - 2) { + pTarget[0] = '\0'; + return; + } + + /* do the copy */ + for (i = iStart, iPos = 0; i < iLength; i++) { + if (pSource[i] != ' ') { + pTarget[iPos] = pSource[i]; + iPos++; + } else { + pTarget[iPos] = '\0'; + return; + } + } + pTarget[iPos] = '\0'; + return; +} + +/*-------------------------------------------------------------------------*/ +static char *pWhere = NULL; +static char *pFile = NULL; + +int MacroWhere(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + assert(pCon); + + if (pWhere) { + SCWrite(pCon, pWhere, eValue); + } + return 1; +} + +/*-------------------------------------------------------------------------*/ +void WhereKill(void *pData) +{ + + if (pWhere) { + free(pWhere); + pWhere = NULL; + } + if (pFile) { + free(pFile); + pFile = NULL; + } + + if (pData) + KillDummy(pData); + +} + +/*--------------------------------------------------------------------------*/ +int MacroFileEvalNew(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + void *pCom = NULL; + pCom = FindCommandData(pInter, "exe", "ExeManager"); + assert(pCom != NULL); + + if (argc < 2) { + SCWrite(pCon, "ERROR: no batch buffer to execute specified", eError); + return 0; + } + return runExeBatchBuffer(pCom, pCon, pInter, argv[1]); +} + +/*----------------------------------------------------------------------*/ +int MacroFileEval(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + FILE *fp = NULL; + char pBueffel[512]; + int iChar; + int i, iRun; + Tcl_DString command; + char *pCom = NULL; + int iRet; + Status eOld; + Tcl_Interp *pTcl = NULL; + int iLine = 0; + + assert(pCon); + assert(pInter); + pTcl = InterpGetTcl(pInter); + + /* check authorisation: only users permitted here */ + if (!SCMatchRights(pCon, usUser)) { + SCWrite(pCon, "ERROR: Insufficient Privilege to do FileEval", eError); + return 0; + } + + /* open filename */ + if (argc < 2) { + SCWrite(pCon, "ERROR: No filename specified ", eError); + return 0; + } + fp = fopen(argv[1], "r"); + if (!fp) { + snprintf(pBueffel,sizeof(pBueffel)-1, " Failed to open file -> %s <- ", argv[1]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + + /* handle status first */ + + Log(INFO,"com","Evaluating %s in MacroFileEval", argv[1]); + if (pFile) { + free(pFile); + } + pFile = strdup(argv[1]); + + /* cycle through file and execute complete commands */ + i = 0; + Tcl_DStringInit(&command); + iRun = 1; + while (iRun) { + iChar = fgetc(fp); + if (iChar == EOF) { + iChar = (int) '\n'; + iRun = 0; + } + if (iChar == (int) '\n') { + iLine++; + pBueffel[i] = (char) iChar; + pBueffel[i + 1] = '\0'; + Tcl_DStringAppend(&command, pBueffel, -1); + pCom = Tcl_DStringValue(&command); + if (Tcl_CommandComplete(pCom)) { + FirstWord(pCom, pBueffel); + if (FindCommand(pInter, pBueffel) != NULL) { + snprintf(pBueffel,sizeof(pBueffel)-1, "%s:%d>> %s", argv[1], iLine, pCom); + SCWrite(pCon, pBueffel, eLog); + if (pWhere != NULL) { + free(pWhere); + } + pWhere = strdup(pBueffel); + } + iRet = Tcl_Eval(pTcl, pCom); + if (iRet != TCL_OK) { + /* write TCL error and check for total interrupt */ + if (Tcl_GetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY) == NULL) { /* Tcl error */ + if (strlen(pTcl->result) > 2) { + /* + local copy in order to resolve a valgrind error + */ + strlcpy(pBueffel, pTcl->result, 511); + SCWrite(pCon, pBueffel, eError); + } + pCom = Tcl_DStringValue(&command); + SCWrite(pCon, "ERROR: in Tcl block:", eError); + SCWrite(pCon, pCom, eError); + SCWrite(pCon, "ERROR: end of Tcl error block", eError); + } else { /* SICS error */ + + Tcl_UnsetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY); + /* SCWrite(pCon,pTcl->result,eError); */ + } + } + if (SCGetInterrupt(pCon) >= eAbortBatch) { + fclose(fp); + Tcl_DStringFree(&command); + SCWrite(pCon, "ERROR: batch processing interrupted", eError); + return 0; + } else { + SCSetInterrupt(pCon, eContinue); + } + Tcl_DStringFree(&command); + } + i = 0; + } else { + pBueffel[i] = (char) iChar; + i++; + } + } /* end while */ + + + /* clean up */ + fclose(fp); + Tcl_DStringFree(&command); + SCSendOK(pCon); + return 1; +} + +/*---------------------------------------------------------------------- + InternalFileEval evaluates a file but on a dummy connection which + only writes log files. It also configures a log file for output + which will be the name of the input file but with .log appended. + This is here in order to support the evaluation of command files + generated in a specific directory from the WWW-interface. If the latter + does not get through, this can safely be deleted. +*/ + +int InternalFileEval(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + SConnection *pIntern = NULL; + int iRet; + char *pFil = NULL, *pExt = NULL; + + + /* we need a filename */ + if (argc < 2) { + SCWrite(pCon, "ERROR: No filename specified ", eError); + return 0; + } + + /* allocate a dummy connection for this */ + pIntern = SCCreateDummyConnection(pInter); + if (!pInter) { + SCWrite(pCon, "ERROR: out of memory in InternalFileEval", eError); + return 0; + } + SCnoSock(pIntern); + + + /* invoke the fileeval */ + MacroPush(pIntern); + iRet = MacroFileEval(pIntern, pInter, pData, argc, argv); + MacroPop(); + + /* remove our internal connection */ + SCDeleteConnection(pIntern); + return iRet; +} + +/*-------------------------------------------------------------------------- + ClientPut is installed as a command to write data to the client from + a server script. Syntax: + ClientPut text outputcode + The output code is optional and and defaults to eLog. +---------------------------------------------------------------------------*/ + +#include "outcode.h" + +int ClientPut(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + int eOut; + int i = 0, iCode; + size_t iLen; + char *pMessage = NULL; + + assert(pCon); + assert(pInter); + + if (argc < 2) { + SCWrite(pCon, "Insufficient arguments to ClientPut", eError); + return 0; + } + + /* handle optional I/O codes */ + if (argc > 2) { + /* the last one must be the code */ + iCode = argc - 1; + eOut = OutCodeFromText(argv[iCode], NULL); + if (eOut < 0) { /* invalid code: assume it is content */ + iCode = argc; + } + } else { + eOut = eLog; + iCode = argc; + } + + /* recombine the message */ + /* find length */ + iLen = 1; + for (i = 1; i < iCode; i++) { + iLen += strlen(argv[i]) + 1; + } + pMessage = (char *) malloc(iLen); + if (!pMessage) { + SCWrite(pCon, "ERROR: out of memory in clientput", eLogError); + return 0; + } + memset(pMessage, 0, iLen); + Arg2Text(iCode - 1, &argv[1], pMessage, iLen); + + /* + as the outcode is optional we have to test the message in order to get + proper outcodes for the log + */ + if (eOut < 0) { /* code not given */ + eOut = eLog; + if(strstr(pMessage,"ERROR") != NULL){ + eOut = eLogError; + } + if(strstr(pMessage,"WARNING") != NULL){ + eOut = eWarning; + } + } + + SCWrite(pCon, pMessage, eOut); + if (pMessage) { + free(pMessage); + } + return 1; +} +/*----------------------------------------------------------------- + ClientLog writes to the socket and the command log only + -------------------------------------------------------------------*/ +int ClientLog(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + OutCode eOut = eLog; + int i = 0, iLen; + char *pMessage = NULL; + + assert(pCon); + assert(pInter); + + if (argc < 2) { + SCWrite(pCon, "Insufficient arguments to ClientLog", eError); + return 0; + } + + + /* recombine the message */ + /* find length */ + iLen = 0; + for (i = 1; i < argc; i++) { + iLen += strlen(argv[i]); + } + pMessage = (char *) malloc((iLen + 100) * sizeof(char)); + if (!pMessage) { + SCWrite(pCon, "ERROR: out of memory in clientlo", eLogError); + return 0; + } + memset(pMessage, 0, (iLen + 100) * sizeof(char)); + Arg2Text(argc - 1, &argv[1], pMessage, (iLen + 100) * sizeof(char)); + + SCLogWrite(pCon,pMessage, eLog); + free(pMessage); + return 1; +} +/*-----------------------------------------------------------------------*/ +int GumPut(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + OutCode eOut = eWarning; + int i = 0, iCode, iLen; + int iMacro; + char *ppCode; + char *pMessage = NULL; + SConnection *conCon = NULL; + + assert(pCon); + assert(pInter); + + if (argc < 2) { + SCWrite(pCon, "Insufficient arguments to ClientPut", eLogError); + return 0; + } + + /* handle optional I/O codes */ + if (argc > 2) { + /* the last one must be the code */ + iCode = argc - 1; + ppCode = strdup(argv[iCode]); + strtolower(ppCode); + while (pCode[i] != NULL) { + if (strcmp(pCode[i], ppCode) == 0) { + break; + } + i++; + } + if (ppCode) { + free(ppCode); + } + } else { + i = 10; + iCode = argc; + } + + switch (i) { + case 0: + eOut = eInternal; + break; + case 1: + eOut = eCommand; + break; + case 2: + eOut = eHWError; + break; + case 3: + eOut = eInError; + break; + case 4: + eOut = eStatus; + break; + case 5: + eOut = eValue; + break; + case 6: + eOut = eWarning; + break; + case 7: + eOut = eFinish; + break; + case 8: + eOut = eEvent; + break; + case 9: + eOut = eWarning; + break; + case 10: + eOut = eError; + break; + case 11: + eOut = eLog; + break; + case 12: + eOut = eLogError; + break; + default: + eOut = eLog; + iCode = argc; + break; + } + + /* recombine the message */ + /* find length */ + iLen = 0; + for (i = 1; i < iCode; i++) { + iLen += strlen(argv[i]); + } + pMessage = (char *) malloc((iLen + 100) * sizeof(char)); + if (!pMessage) { + SCWrite(pCon, "ERROR: out of memory in clientput", eError); + return 0; + } + memset(pMessage, 0, (iLen + 100) * sizeof(char)); + Arg2Text(iCode - 1, &argv[1], pMessage, (iLen + 100) * sizeof(char)); + + /* now write, thereby tunneling macro flag in order to get proper + write to client and not into interpreter. We also make sure that the device + is gumput + */ + conCon = SCCopyConnection(pCon); + if (conCon == NULL) { + SCWrite(pCon, "ERROR: out of memory in gumput", eError); + } + strcpy(conCon->deviceID, "gumput"); + SCWrite(conCon, pMessage, eOut); + SCDeleteConnection(conCon); + if (pMessage) { + free(pMessage); + } + return 1; +} + +/*----------------------------------------------------------------------*/ +int Broadcast(SConnection * pCon, SicsInterp * pInter, void *pData, + int argc, char *argv[]) +{ + int iMacro; + char pBueffel[256]; + + assert(pCon); + assert(pInter); + + if (argc < 2) { + SCWrite(pCon, "Insufficient arguments to Broadcast", eLog); + return 0; + } + + + /* now write, thereby tunneling macro flag in order to get proper + write to client and not into interpreter + */ + Arg2Text(argc - 1, &argv[1], pBueffel, 255); + ServerWriteGlobal(pBueffel, eLog); + return 1; +} + +/*--------------------------------------------------------------------------- + This implements a scheme to provide Tcl commands to Tcl. The Tcl commands + (either procedures or objects) must be defined in a separate file. Than + you call: + Publish command UserRights + This will make the command known to Sics, and user with user rights matching + the rights initialized may use it. + + Below the datastructure to hold for each command +----------------------------------------------------------------------------*/ +typedef struct { + pObjectDescriptor pDes; + char *command; + int iUser; +} PubTcl, *pPubTcl; +/*--------------------------------------------------------------------------*/ +static pPubTcl CreatePublish(char *name, int iUser) +{ + pPubTcl pRes = NULL; + + pRes = (pPubTcl) malloc(sizeof(PubTcl)); + if (!pRes) { + return NULL; + } + + pRes->pDes = CreateDescriptor("Macro"); + if (!pRes->pDes) { + free(pRes); + return NULL; + } + + pRes->command = strdup(name); + pRes->iUser = iUser; + return pRes; +} + +/*-------------------------------------------------------------------------*/ +static void DeletePublish(void *pData) +{ + pPubTcl self = NULL; + + self = (pPubTcl) pData; + assert(self); + + if (self->pDes) { + DeleteDescriptor(self->pDes); + } + if (self->command) { + free(self->command); + } + free(self); +} + +/*-------------------------------------------------------------------------- + TclAction checks the user rights and than invokes the arguments as a Tcl + command + */ + +static int TclAction(SConnection * pCon, SicsInterp * pSics, void *pData, + int argc, char *argv[]) +{ + char pBueffel[1024]; + char *pCommand; + pPubTcl self = NULL; + int iRet, length; + char *pPtr; + Tcl_Interp *pTcl = NULL; + + self = (pPubTcl) pData; + assert(pCon); + assert(pSics); + assert(self); + pTcl = InterpGetTcl(pSics); + + if (!SCMatchRights(pCon, self->iUser)) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to invoke %s", + argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + + /* make a string */ + pCommand = + Arg2Tcl0(argc - 1, argv + 1, pBueffel, sizeof(pBueffel), + self->command); + if (!pCommand) { + SCWrite(pCon, "ERROR: no more memory", eError); + return 0; + } + Tcl_ResetResult(pTcl); + iRet = Tcl_Eval(pTcl, pCommand); + if (iRet == TCL_OK) { + if (strlen(pTcl->result) > 0) { + SCPrintf(pCon, eValue, "%s", pTcl->result); + } + if (pCommand != pBueffel) + free(pCommand); + return 1; + } else { + if (Tcl_GetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY) != NULL) { + Tcl_UnsetVar(pTcl, SICSERROR, TCL_GLOBAL_ONLY); + } + if (strlen(pTcl->result) > 0) { + SCPrintf(pCon, eError, "ERROR: Tcl reported %s in %s", pTcl->result, + pCommand); + } + if (pCommand != pBueffel) + free(pCommand); + return 0; + } + return 1; /* not reached */ +} + +/*--------------------------------------------------------------------------*/ +int TclPublish(SConnection * pCon, SicsInterp * pSics, void *pData, + int argc, char *argv[]) +{ + pPubTcl pNew = NULL; + char pBueffel[132]; + int iUser, i, iRet; + + /* check no of args */ + if (argc < 3) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: Insufficient no of arguments to %s", + argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + + /* try convert last parameter to user code */ + iUser = decodeSICSPriv(argv[2]); + if (iUser < 0) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: cannot identify %s as a valid user code", + argv[2]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + + /* check if the macro already exists */ + pNew = FindCommandData(pSics, argv[1], "Macro"); + if (pNew) { /* yes -> overwrite access code */ + if (pNew->iUser == iUser) { + return 1; + } + /* check user rights */ + if (!SCMatchRights(pCon, usMugger)) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to use %s", argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + pNew->iUser = iUser; + return 1; + } + + /* check user rights */ + if (!SCMatchRights(pCon, usMugger)) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: you are not authorised to use %s", argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + + /* do a job ! */ + pNew = CreatePublish(argv[1], iUser); + if (!pNew) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: memory error in %s", argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + iRet = + AddCommand(pSics, argv[1], TclAction, DeletePublish, (void *) pNew); + if (!iRet) { + snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: duplicate command %s not created", argv[1]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + return 1; + +} + +/*------------------------------------------------------------------------- + Transact executes a command and sends a TRANSACTIONFINISHED string + at the end. This is to permit clients to search for this string in + order to find out when a command has finished. + + MK, May 2016 + + I changed this to write the TRANSACT strings only to the socket and not to the + log. Because this is clogging the log with useless stuff. If you want to + debug the protocol you still can sit on the line with sockspy. But I am unsure + of this change... +*/ + + + +int TransactAction(SConnection * pCon, SicsInterp * pSics, void *pData, + int argc, char *argv[]) +{ + char pBuffer[1024]; + char pStartBuffer[1088]; + char *pCommand; + int iRet; + + pCommand = Arg2Tcl(argc - 1, &argv[1], pBuffer, sizeof(pBuffer)); + if (!pCommand) { + SCWrite(pCon, "ERROR: no memory", eError); + return 0; + } + strtolower(argv[0]); + if (strcmp(argv[0], "fulltransact") == 0) { + snprintf(pStartBuffer, sizeof(pStartBuffer), "TRANSACTIONSTART %s", pCommand); + SCPureSockWrite(pCon, pStartBuffer,eLog); + } + iRet = InterpExecute(pSics, pCon, pCommand); + if (pCommand != pBuffer) + free(pCommand); + SCPureSockWrite(pCon, "TRANSACTIONFINISHED", eLog); + return iRet; +} diff --git a/sea_ofac.c b/sea_ofac.c new file mode 100644 index 00000000..387462cb --- /dev/null +++ b/sea_ofac.c @@ -0,0 +1,243 @@ +/** + * Startup commands and definitions + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke 1996 - ? + * heavy modifications to separate PSI specific commands into a + * separate library. Mark Koennecke, June 2003 + * + * moved some functions to sicsutil.c + * Markus Zolliker Jan 2010 + * + * reduced version for SEA only, June 2020 + */ + +#include "ofac.h" +#include "exeman.h" +#include "statusfile.h" +#include "site.h" +#include "sicshipadaba.h" +#include "sicsglobal.h" + +static unsigned int killStartupCommands = 1; +int isDuringInitialization; + + +extern void DevExecInit(void); /* devexec.c */ +/*--------------------------------------------------------------------------*/ +static void InitGeneral(void) +{ +#define INIT(F) { void F(void); F(); } + +/* insert here initialization routines ... */ + + INIT(StatisticsInit); + INIT(InitializerInit); + INIT(SaveHdbInit); /* must be after InitializerInit */ + INIT(DefineAliasInit); + INIT(SctInit); + INIT(SctDriveAdapterInit); + INIT(SctDriveObjInit); + INIT(SctDriveAdapterInit); + INIT(SICSGetInit); + INIT(LogReaderInit); + INIT(LogSetupInit); + INIT(InstallBackground); + INIT(MakeProtocol); + INIT(UdpInit); + INIT(HelpInit); + INIT(AddTestProt); + INIT(AddGenBinProtocoll); + INIT(AddSyncedProt); + INIT(AddBinProtocol); + INIT(AddPMACProtocoll); + INIT(AddCharByCharProtocoll); + INIT(AddLscProtocol); + INIT(AddSecopProtocol); + INIT(AddSecop3Protocol); + INIT(MakeTrace); + INIT(InitTaskOBJ); + INIT(RemoteObjectInit); + INIT(Logv2Init); + INIT(LogListenInit); + INIT(SiteInit); /* site specific initializations */ +} + +static void InitIniCommands(SicsInterp * pInter) +{ + +/* declare and add permanent command */ +#define PCMD(NAME, FUN) { \ + int FUN(SConnection * pCon, SicsInterp * pSics, void *pData, \ + int argc, char *argv[]); \ + AddCommandWithFlag(pInter, NAME, FUN, NULL, NULL, 0); \ + } + +/* declare and add startup command */ +#define SCMD(NAME, FUN) { \ + int FUN(SConnection * pCon, SicsInterp * pSics, void *pData, \ + int argc, char *argv[]); \ + AddCommandWithFlag(pInter, NAME, FUN, NULL, NULL, 1); \ + } + + /* permanent commands in alphabetic order */ + PCMD("alias", MakeAlias); + PCMD("broadcast", Broadcast); + PCMD("ClientPut", ClientPut); + PCMD("ClientLog", ClientLog); + PCMD("config", ConfigCon); + PCMD("db", SICSDebug); + PCMD("Dir", ListObjects); + PCMD("dolater", MakeCron); + PCMD("DrivableInvoke", TclDrivableInvoke); + PCMD("DynSicsObj", InstallSICSOBJ); + PCMD("EVFactory", EVControlFactory); + PCMD("FileEval", MacroFileEval); + PCMD("findalias", LocateAliasAction); + PCMD("fulltransact", TransactAction); + PCMD("GetInt", GetSICSInterrupt); + PCMD("GumPut", GumPut); + PCMD("InternEval", InternalFileEval); + PCMD("kill_command", SICSKill); + PCMD("list", SicsList); + PCMD("MakeAsyncProtocol", AsyncProtocolFactory); + PCMD("MakeAsyncQueue", AsyncQueueFactory); + PCMD("MakeMulti", MakeMulti); + PCMD("MakeOptimise", MakeOptimiser); + PCMD("MakeSecMotor", SecMotorFactory); + PCMD("Publish", TclPublish); + PCMD("Remob", RemobCreate); + PCMD("ResetServer", ResetStatus); + PCMD("scriptcallback", CallbackScript); + PCMD("SetInt", SetSICSInterrupt); + PCMD("SetStatus", SetSICSStatus); + PCMD("SICSBounds", SICSBounds); + PCMD("sicscron", MakeCron); + PCMD("sicsdatafactory", SICSDataFactory); + PCMD("sicsdescriptor", SICSDescriptor); + PCMD("SICSLog", LogOutput); + PCMD("sicsprompt", SicsPrompt); + PCMD("SICSStatus", SICSStatus); + /* PCMD("sicstime", SICSTime); */ + PCMD("doubletime", SICSDoubleTime); + PCMD("SICSType", SICSType); + PCMD("Sics_Exitus", SicsExit); + PCMD("silent", SICSSilent); + PCMD("json2tcl", SICSjson2tcl); + PCMD("status", UserStatus); + PCMD("TclReplaceDrivable", TclReplaceDrivable); + PCMD("transact", TransactAction); + PCMD("wait", UserWait); + PCMD("yield", UserYield); + PCMD("checksum", CheckSum); + PCMD("loguserinfo", LogUserInfoWrapper); + + /* startup commands in alphabetic order */ + SCMD("allowexec", AllowExec); + SCMD("AntiCollisionInstall", AntiColliderFactory); + SCMD("ChopperAdapter", CHAdapterFactory); + SCMD("MakeBatchManager", MakeExeManager); + SCMD("MakeChopper", ChocoFactory); + SCMD("MakeConfigurableMotor", MakeConfigurableVirtualMotor); + SCMD("MakeCounter", MakeCounter); + SCMD("MakeDataNumber", DNFactory); + SCMD("MakeDiffScan", MakeDiffScan); + SCMD("MakeDrive", MakeDrive); + SCMD("MakeEnergy", MakeEnergyVar); + SCMD("MakeGPIB", MakeGPIB); + SCMD("MakeHdbTable", HdbTableFactory); + SCMD("MakeLin2Ang", MakeLin2Ang); + SCMD("MakeMaximize", MaximizeFactory); + SCMD("MakeMono", MonoInit); + SCMD("MakeMultiCounter", MakeMultiCounter); + SCMD("MakeMultiSec", MakeMultiSec); + SCMD("MakeO2T", CreateO2T); + SCMD("MakeOscillator", MakeOscillator); + SCMD("MakePeakCenter", FitFactory); + SCMD("MakeProxy", ProxyFactory); + SCMD("MakeRefList", MakeReflectionList); + SCMD("MakeRS232Controller", RS232Factory); + SCMD("MakeScanCommand", ScanFactory); + SCMD("MakeSecCounter", MakeSecCter); + SCMD("MakeSicsObj", InstallSICSOBJ); + SCMD("MakeSicsPoll", InstallSICSPoll); + SCMD("MakeStateMon", StateMonFactory); + SCMD("MakeSync", MakeSync); + SCMD("MakeTclInt", MakeTclInt); + SCMD("MakeWaveLength", MakeWaveLengthVar); + SCMD("MakeXYTable", XYFactory); + SCMD("Motor", MotorCreate); + SCMD("ServerOption", IFServerOption); + SCMD("SicsAlias", SicsAlias); + SCMD("SicsUser", PWSicsUser); + SCMD("TokenInit", TokenInit); + SCMD("VarMake", VarFactory); + SCMD("VelocitySelector", VelSelFactory); + +} + +/*--------------------------------------------------------------------------*/ +int InitObjectCommands(pServer pServ, char *file) +{ + SConnection *pCon = NULL; + char pBueffel[1024]; + int iRet; + SicsInterp *pSics; + pExeList pExe; + pEnvMon pEnv = NULL; + pSite site = NULL; + + pSics = pServ->pSics; + assert(pSics); + + InitGeneral(); + + /* general initialization */ + /* create a connection */ + pCon = SCCreateDummyConnection(pSics); + if (!pCon) { + return 0; + } + MakeExeManager(pCon, pSics, NULL, 1, NULL); + + pExe = CreateExeList(pServ->pTasker); + pServ->pExecutor = pExe; + pEnv = CreateEnvMon(); + DevExecInit(); + + assert(pExe); + assert(pEnv); + + InitIniCommands(pSics); + /* + install site specific commands + */ + site = getSite(); + if (site != NULL) { + site->AddSiteCommands(pServ->pSics); + } + + InstallBckRestore(pCon, pSics); + InstallSICSHipadaba(pCon, pSics,NULL,0,NULL); + + /* evaluate the file */ + snprintf(pBueffel,sizeof(pBueffel)-1, "fileeval %s", file); + iRet = InterpExecute(pSics, pCon, pBueffel); + + /* done */ + SCDeleteConnection(pCon); + if (site != NULL && site->RemoveSiteCommands != NULL) { + site->RemoveSiteCommands(pSics); + } + if(killStartupCommands){ + RemoveStartupCommands(); + } + isDuringInitialization = 0; + return 1; +} +/*---------------------------------------------------------------------------------*/ +void KeepStartupCommands() +{ + killStartupCommands = 0; +} diff --git a/secopprot3.c b/secopprot3.c new file mode 100644 index 00000000..f47fe44e --- /dev/null +++ b/secopprot3.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include "uselect.h" +#include "ascon.h" +#include "ascon.i" +#include "script.h" + +/* + * protocol for SECoP + * + * Markus Zolliker Feb 2019 + */ + +/* the first item in this linked list contains a partial or empty buffer. + all the following are the messages in the queue */ + +typedef struct secop_msg { + struct secop_msg *next; + char *message; +} SecopMsg; + +typedef struct secop_buf { + SecopMsg *queue; + pDynString buffer; +} SecopBuf; + +/*----------------------------------------------------------------------------*/ +void SecopKillPrivate(void *private) { + SecopBuf *msgbuf = private; + SecopMsg *msg, *next; + + for (msg = msgbuf->queue; msg != NULL; msg = next) { + next = msg->next; + if (msg->message) { + free(msg->message); + } + free(msg); + } + DeleteDynString(msgbuf->buffer); + free(msgbuf); +} +/*----------------------------------------------------------------------------*/ +int Secop3ReadChars(int fd, char *bytes, int maxbytes) +{ + fd_set rmask; + struct timeval tmo = { 0, 0 }; + int ret; + + FD_ZERO(&rmask); + FD_SET(fd, &rmask); + ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo); + if (ret <= 0) + return ret; + ret = recv(fd, bytes, maxbytes, 0); + /* PrintChar(*chr); fflush(stdout); */ + if (ret > 0) + return ret; + if (ret == 0) { + errno = ECONNRESET; + return ASCON_DISCONNECTED; + } + return ASCON_RECV_ERROR; +} + +/*----------------------------------------------------------------------------*/ +int Secop3ProtHandler(Ascon *a) { + int ret; + char *result; + char *space; + char *json; + char *specifier; + Tcl_Obj *tcllist[3], *tclobj; + int l, l1, l2; + struct json_object *jobj; + char buffer[1024]; + char *bytes; + char *str; + SecopBuf *msgbuf; + SecopMsg *msg, **msgp; + + if (a->state <= AsconConnectDone) { + /* handle connect states */ + return AsconStdHandler(a); + } + /* treat incoming messages always */ + msgbuf = a->private; + if (msgbuf == NULL) { + msgbuf = calloc(1, sizeof *msgbuf); + a->private = msgbuf; + a->killPrivate = SecopKillPrivate; + msgbuf->buffer = CreateDynString(60, 65536); + } + ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1); + while (ret > 0) { + assert(ret < sizeof buffer); + buffer[ret] = '\0'; + bytes = buffer; + str = strchr(bytes, '\n'); + while (str) { + l = str - bytes; + DynStringConcatBytes(msgbuf->buffer, bytes, l); + /* append at end of queue */ + for (msgp = &msgbuf->queue; *msgp != NULL; msgp = &(*msgp)->next); + msg = calloc(1, sizeof *msg); + *msgp = msg; + msg->message = strdup(GetCharArray(msgbuf->buffer)); + DynStringClear(msgbuf->buffer); + bytes += l + 1; + str = strchr(bytes, '\n'); + } + DynStringConcat(msgbuf->buffer, bytes); + ret = Secop3ReadChars(a->fd, buffer, sizeof buffer - 1); + } + if (ret < 0) { + AsconError(a, "SECOP.1", errno); + } + switch (a->state) { + case AsconWriting: + /* do not skip garbage */ + l = GetDynStringLength(a->wrBuffer) - a->wrPos; + ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); + if (ret < 0) { + AsconError(a, "SECOP.2", errno); /* sets state to AsconFailed */ + } else { + a->wrPos += ret; + if (a->wrPos >= GetDynStringLength(a->wrBuffer)) { + a->state = AsconWriteDone; + } + } + return 1; + case AsconWriteStart: + if (GetDynStringLength(a->wrBuffer) == 0) { + /* do not send empty message */ + a->state = AsconWriteDone; + return 1; + } + if (a->sendTerminator) { + DynStringConcat(a->wrBuffer, a->sendTerminator); + } + a->wrPos = 0; + a->state = AsconWriting; + break; + case AsconReadStart: + a->state = AsconReadDone; + msgbuf = a->private; + if (msgbuf->queue == NULL) { + DynStringClear(a->rdBuffer); + return 1; + } + msg = msgbuf->queue; + msgbuf->queue = msg->next; + space = strchr(msg->message, ' '); + jobj = NULL; + specifier = ""; + if (space) { + l1 = space - msg->message; + specifier = space + 1; + json = strchr(specifier, ' '); + if (json) { + l2 = json - specifier; + json++; + jobj = json_tokener_parse(json); + } else { + l2 = strlen(specifier); + } + } else { + l1 = strlen(msg->message); + l2 = 0; + } + tcllist[0] = Tcl_NewStringObj(msg->message, l1); + tcllist[1] = Tcl_NewStringObj(specifier, l2); + if (jobj) { + tcllist[2] = json2tcl(jobj); + tclobj = Tcl_NewListObj(3, tcllist); + } else { + tclobj = Tcl_NewListObj(2, tcllist); + } + DynStringCopy(a->rdBuffer, Tcl_GetString(tclobj)); + a->responseValid = 1; + json_object_put(jobj); /* free jobj */ + free(msg->message); + free(msg); + break; + } + return 1; +} + +/*----------------------------------------------------------------------------*/ +void AddSecop3Protocol() { + static AsconProtocol secopprot; + secopprot.name = "SECoP3"; + secopprot.handler = Secop3ProtHandler; + secopprot.init = AsconStdInit; + AsconInsertProtocol(&secopprot); +} diff --git a/sicshipadaba.c b/sicshipadaba.c index 19809423..c7b52a9a 100644 --- a/sicshipadaba.c +++ b/sicshipadaba.c @@ -41,6 +41,7 @@ #include "arrayutil.h" #include "HistMem.h" #include "asynnet.h" +#include #define MAX_HDB_PATH 1024 @@ -455,6 +456,7 @@ int formatNameValue(Protocol protocol, char *name, char *value, pDynString result, int hdtype) { char *char_arr, *ptr; + char token[8], ch; switch (protocol) { case normal_protocol: @@ -465,6 +467,7 @@ int formatNameValue(Protocol protocol, char *name, char *value, case json_protocol: switch (hdtype) { case HIPNONE: + DynStringCopy(result, "null"); break; case HIPINT: case HIPFLOAT: @@ -478,7 +481,27 @@ int formatNameValue(Protocol protocol, char *name, char *value, DynStringCopy(result, "{\""); DynStringConcat(result, name); DynStringConcat(result, "\": \""); - DynStringConcat(result, value); + ptr = NULL; + for (ch=*value; ch!=0; ch=*++value) { + switch (ch) { + case '"': strcpy(token, "\\\""); + case '\\': strcpy(token, "\\\\"); + default: + if (ch > 127 || ch < 32) { + snprintf(token, sizeof token, "\\u%4.4x", (unsigned char)ch); + } else { + if (!ptr) ptr = value; + continue; + } + } + if (ptr) { + DynStringConcatBytes(result, ptr, value - ptr); + } + DynStringConcat(result, token); + } + if (ptr) { + DynStringConcatBytes(result, ptr, value - ptr); + } DynStringConcat(result, "\"}"); break; case HIPINTAR: @@ -593,8 +616,11 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData, pHdbIDMessage idm = NULL; pHdbPtrMessage cmm = NULL; pHdbDataMessage mm = NULL; + hdbPropertyChange *propm = NULL; + hdbValue *hvalue; SConnection *tstCon; char updatePath[1024]; + char *geterror; cbInfo = (HdbCBInfo *) userData; @@ -650,9 +676,20 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData, } /* - * Deal with update messages + * Deal with update and geterror property messages */ - if ((mm = GetHdbUpdateMessage(message)) == NULL) { + + if ((propm = GetPropertyChangeMessage(message)) != NULL) { + if (strcmp(propm->key, "geterror") != 0) { + /* this is a property change of an other property */ + return hdbContinue; + } + geterror = propm->value; + hvalue = &node->value; + } else if ((mm = GetHdbUpdateMessage(message)) != NULL) { + geterror = GetHdbProp(node, "geterror"); + hvalue = mm->v; + } else { return hdbContinue; } @@ -663,19 +700,26 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData, else outCode = eEvent; + if (geterror && *geterror) { + /* send geterror */ + strlcat(updatePath, ".geterror", sizeof updatePath); + formatNameValue(protocol, updatePath, geterror, result, HIPTEXT); + SCPureSockWrite(cbInfo->pCon, Dyn2Cstring(result), outCode); + return hdbContinue; + } /* else normal notify */ /** * if transfer = zip always transfer data in zipped form */ if (GetHdbProperty(node, "transfer", value, 80) == 1) { if (strstr(value, "zip") != NULL || strstr(value,"bin") != NULL) { - status = sendZippedNodeData(node, *(mm->v), cbInfo->pCon); + status = sendZippedNodeData(node, *hvalue, cbInfo->pCon); DeleteDynString(result); return hdbContinue; } } - if (mm->v->arrayLength < 100) { - printedData = formatValue(*(mm->v), node); + if (hvalue->arrayLength <= 400) { + printedData = formatValue(*hvalue, node); if (printedData == NULL || result == NULL) { SCWrite(cbInfo->pCon, "ERROR: out of memory formatting data", eEvent); @@ -686,7 +730,7 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData, return hdbContinue; } formatNameValue(protocol, updatePath, GetCharArray(printedData), result, - mm->v->dataType); + hvalue->dataType); #ifdef SITE_ANSTO SCWrite(cbInfo->pCon, GetCharArray(result), outCode); #else @@ -792,8 +836,10 @@ static hdbCallbackReturn TreeChangeCallback(pHdb node, void *userData, outCode = eHdbEvent; else outCode = eEvent; + /* formatNameValue(protocol, "treechange", path, result, + node->value.dataType); */ formatNameValue(protocol, "treechange", path, result, - node->value.dataType); + HIPTEXT); SCWrite(cbInfo->pCon, GetCharArray(result), outCode); DeleteDynString(result); free(path); @@ -814,7 +860,9 @@ pHdbCallback MakeTreeChangeCallback(SConnection * pCon, int id) if (cbInfo->pCon == NULL) { return NULL; } + SCsetMacro(cbInfo->pCon, 0); cbInfo->ID = id; + cbInfo->internalID = -1; return MakeHipadabaCallback(TreeChangeCallback, cbInfo, cbKill); } @@ -3024,11 +3072,12 @@ static pDynString formatPlainList(pHdb node) } /*---------------------------------------------------------------------------*/ -static pDynString formatJSONList(pHdb node) +static pDynString formatJSONList(pHdb node, int withValues) { pHdb current; pDynString result = NULL; pDynString data = NULL; + char *str; if (node->child == NULL) return NULL; @@ -3037,22 +3086,44 @@ static pDynString formatJSONList(pHdb node) return NULL; } - if (node->child->value.dataType == HIPNONE) - DynStringCopy(result, "["); - else + if (withValues) DynStringCopy(result, "{"); + else + DynStringCopy(result, "["); current = node->child; while (current != NULL) { - DynStringConcat(result, "\""); + DynStringConcatChar(result, '"'); DynStringConcat(result, current->name); - DynStringConcat(result, "\""); - if (current->value.dataType != HIPNONE) { - data = formatValue(current->value, current); - if (data != NULL) { - DynStringConcat(result, ": "); - DynStringConcat(result, GetCharArray(data)); - DeleteDynString(data); + DynStringConcatChar(result, '"'); + if (withValues) { + DynStringConcat(result, ": "); + if (current->value.dataType == HIPTEXT) { + DynStringConcatChar(result, '"'); + data = formatValue(current->value, current); + if (data != NULL) { + /* lazy approach: remove \ and " */ + for (str = GetCharArray(data); *str != 0; str++) { + if (*str == '\\' || *str == '"') { + *str = '_'; + } + } + DynStringConcat(result, GetCharArray(data)); + DeleteDynString(data); + } + DynStringConcatChar(result, '"'); + } else { + if (current->value.dataType != HIPINT && current->value.dataType != HIPFLOAT) { + data = NULL; + } else { + data = formatValue(current->value, current); + } + if (data != NULL) { + DynStringConcat(result, GetCharArray(data)); + DeleteDynString(data); + } else { + DynStringConcat(result, "null"); + } } } if (current->next != NULL) @@ -3060,10 +3131,10 @@ static pDynString formatJSONList(pHdb node) current = current->next; } - if (node->child->value.dataType == HIPNONE) - DynStringConcat(result, "]"); - else + if (withValues) DynStringConcat(result, "}"); + else + DynStringConcat(result, "]"); return result; } @@ -3197,31 +3268,34 @@ static int ListHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData, return 0; } + /* do not modify arguments! if (pathArg == 2) { strtolower(argv[1]); } - if (strcmp(argv[1], "-val") == 0) { - listData = formatListWithVal(node); - } else if (strcmp(argv[1], "-cli") == 0) { + */ + outCode = eValue; + if (strcasecmp(argv[1], "-val") == 0) { + if (isJSON(pCon, 0)) { + listData = formatJSONList(node, 1); + outCode = eHdbEvent; + } else { + listData = formatListWithVal(node); + } + } else if (strcasecmp(argv[1], "-cli") == 0) { listData = formatClientList(node); } else { if ((protocol = isJSON(pCon, 0)) == 1) { - listData = formatJSONList(node); + listData = formatJSONList(node, 0); outCode = eHdbEvent; } else { listData = formatPlainList(node); - outCode = eValue; } } if (listData == NULL) { SCWrite(pCon, "ERROR: failed to format list", eError); return 0; } - if ((strcmp(argv[1], "-val") == 0) || (strcmp(argv[1], "-cli") == 0)) { - SCWrite(pCon, GetCharArray(listData), eValue); - } else { - SCWrite(pCon, GetCharArray(listData), outCode); - } + SCWrite(pCon, GetCharArray(listData), outCode); DeleteDynString(listData); return 1; } diff --git a/socketaddr.c b/socketaddr.c new file mode 100644 index 00000000..a44bbc13 --- /dev/null +++ b/socketaddr.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include + +int MakeSocketAddr( + struct sockaddr *sockaddrPtr, /* socket address */ + char *hostname, /* name or ip of host. NULL implies INADDR_ANY */ + int port, /* port number */ + char dotted_ip[16]) { /* resolved ip addr */ + /* + Workaround for cases, where the local cache is not updated (yet) + often, gethostbyname gets a cached value, but dig will get a fresh one + note after an internet research: dig is recommended over nslookup + */ + + struct hostent *hostent; /* Host database entry */ + struct sockaddr_in *sadr = (struct sockaddr_in *)sockaddrPtr; + FILE *fil; + char line[256]; + int l; + + (void) memset(sadr, 0, sizeof(*sadr)); + if (dotted_ip) { /* default value: failure */ + dotted_ip[0] = 0; + } + sadr->sin_family = AF_INET; + sadr->sin_port = htons((unsigned short)port); + if (hostname == NULL) { + /* do not need to copy, as INADDR_ANY is all zero bytes */ + return 1; + } + if (inet_pton(AF_INET, hostname, &sadr->sin_addr) == 1) { + /* resolved as dotted numbers notation */ + return 1; + } + hostent = gethostbyname(hostname); + if (hostent == 0) { + /* we assume that when gethostname gets no entry, dig will also fail. + That way, dig will not be called repeatedly for no reason */ + return 0; + } + /* copy the address: in case dig fails, we use the cached value */ + memcpy(&sadr->sin_addr, hostent->h_addr_list[0], 4); + /* we use hostent->h_name instead of hostname here, as this has already + the proper domain added */ + snprintf(line, sizeof line, "timeout 1 dig +short %s", hostent->h_name); + fil = popen(line, "r"); + if (fil != NULL) { + if (fgets(line, sizeof(line), fil) != NULL) { + l = strlen(line); + if (line[l-1] <= ' ') line[l-1] = 0; /* strip off newline */ + /* silently ignore return value, if it fails, we take the cached value */ + inet_pton(AF_INET, line, &sadr->sin_addr); + } + fclose(fil); + } + if (dotted_ip) { + inet_ntop(AF_INET, &sadr->sin_addr, dotted_ip, 16); + } + return 1; +} + +int CreateSocketAdress(struct sockaddr_in *sockaddrPtr, /* Socket address */ + char *host, /* Host. NULL implies INADDR_ANY */ + int port) +{ + return MakeSocketAddr((struct sockaddr *)sockaddrPtr, host, port, NULL); +} diff --git a/socketaddr.h b/socketaddr.h new file mode 100644 index 00000000..106fbd10 --- /dev/null +++ b/socketaddr.h @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include +#include + +int MakeSocketAddr( + struct sockaddr *sockaddrPtr, /* socket address */ + char *hostname, /* name or ip of host. NULL implies INADDR_ANY */ + int port, /* port number */ + char dotted_ip[16]); /* resolved ip addr */ + /* + Workaround for cases, where the local cache is not updated (yet) + often, gethostbyname gets a cached value, but dig will get a fresh one + note after an internet research: dig is recommended over nslookup + */ + +/* compatibility for network.c */ +int CreateSocketAdress(struct sockaddr_in *sockaddrPtr, /* Socket address */ + char *host, /* Host. NULL implies INADDR_ANY */ + int port);