- use dig for resolving host names - ascon.c: fix terminator parsing - property callback: change property before callback - logger.c:default for logger period must be the old value instead of 1 - add frappy type history writing - increase max. logreader line length - HIPNONE returns "null" with json protocol - encode strings properly in formatNameValue - fix memory leak in json2tcl - scriptcontext: do not show debug messages when script starts with underscore or when the "send" property is empty - scriptcontext: remove args for action timestamp - scriptcontext: "que" function will replace an already queued action, e.g. for 'halt - introduced updatestatus script
847 lines
19 KiB
C
847 lines
19 KiB
C
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include "sics.h"
|
|
#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;
|
|
|
|
void AsconError(Ascon *a, char *msg, int errorno)
|
|
{
|
|
static char *stateText[]={
|
|
"not connected",
|
|
"connect start",
|
|
"connecting",
|
|
"connect done",
|
|
"write start",
|
|
"writing",
|
|
"write done",
|
|
"read start",
|
|
"reading",
|
|
"read done",
|
|
"idle",
|
|
"failed",
|
|
"no response"
|
|
};
|
|
char *state;
|
|
char num[8];
|
|
|
|
if ( a->state >= AsconMaxState) {
|
|
state = "bad state";
|
|
} else {
|
|
state = stateText[a->state];
|
|
}
|
|
DynStringCopy(a->errmsg, "ASCERR: ");
|
|
if (errorno == 0) {
|
|
DynStringConcat(a->errmsg, msg);
|
|
DynStringConcat(a->errmsg, " (");
|
|
DynStringConcat(a->errmsg, state);
|
|
DynStringConcat(a->errmsg, " state)");
|
|
DynStringConcat(a->errmsg, " on ");
|
|
DynStringConcat(a->errmsg, a->hostport);
|
|
} else {
|
|
DynStringConcat(a->errmsg, strerror(errorno));
|
|
DynStringConcat(a->errmsg, " (");
|
|
DynStringConcat(a->errmsg, state);
|
|
DynStringConcat(a->errmsg, " state, ");
|
|
DynStringConcat(a->errmsg, msg);
|
|
DynStringConcat(a->errmsg, ")");
|
|
DynStringConcat(a->errmsg, " on ");
|
|
DynStringConcat(a->errmsg, a->hostport);
|
|
}
|
|
a->state = AsconFailed;
|
|
}
|
|
|
|
static void AsconConnect(Ascon * a)
|
|
{
|
|
/* input state: AsconConnectStart
|
|
output state: AsconFailed or AsconConnecting */
|
|
int ret;
|
|
struct sockaddr adr;
|
|
char *colon;
|
|
int port;
|
|
int oldopts;
|
|
char ipaddress[16];
|
|
|
|
/* wait 0.5 sec before connecting again after a close
|
|
2 reasons for that:
|
|
- it seems that connecting immediately to a closed port fails.
|
|
We will avoid some "Connection refused" error messages.
|
|
- a bug in the lantronix terminal: if a channel is closed and reopened
|
|
within short time the connect may be succesful, but a message will be
|
|
sent on the channel!
|
|
In principle we need only to wait when connecting to the same address
|
|
and port, but bookkeeping would be too complicated.
|
|
*/
|
|
if (DoubleTime() < lastClose + 0.5) return;
|
|
|
|
if (a->fd < 0) {
|
|
a->fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (a->fd < 0) {
|
|
AsconError(a, "ASC1", errno);
|
|
return;
|
|
}
|
|
}
|
|
if (a->hostport == NULL) {
|
|
AsconError(a, "no host:port given", 0);
|
|
return;
|
|
}
|
|
colon = strchr(a->hostport, ':');
|
|
if (colon == NULL) {
|
|
if (strcmp(a->hostport, "unconnected") == 0) {
|
|
AsconError(a, "disconnected", 0);
|
|
} else {
|
|
AsconError(a, "expected 'host:port' or 'unconnected'", 0);
|
|
}
|
|
return;
|
|
}
|
|
port = atoi(colon + 1);
|
|
if (port <= 0) {
|
|
AsconError(a, "bad port number", 0);
|
|
return;
|
|
}
|
|
*colon = '\0';
|
|
ret = MakeSocketAddr(&adr, a->hostport, port, a->ip);
|
|
*colon = ':';
|
|
if (ret == 0) {
|
|
AsconError(a, "bad host specification", 0);
|
|
return;
|
|
}
|
|
oldopts = fcntl(a->fd, F_GETFL, 0);
|
|
fcntl(a->fd, F_SETFL, oldopts | O_NONBLOCK);
|
|
ret = connect(a->fd, &adr, sizeof(struct sockaddr_in));
|
|
if (ret < 0) {
|
|
switch (errno) {
|
|
case EINPROGRESS:
|
|
case EALREADY:
|
|
case EISCONN:
|
|
a->state = AsconConnecting;
|
|
break;
|
|
default:
|
|
AsconError(a, "ASC2", errno);
|
|
return;
|
|
}
|
|
}
|
|
a->state = AsconConnecting;
|
|
return;
|
|
}
|
|
|
|
|
|
int AsconReadGarbage(int fd)
|
|
{
|
|
fd_set rmask;
|
|
struct timeval tmo = { 0, 0 };
|
|
int l, ret, result;
|
|
char garbage[100];
|
|
|
|
FD_ZERO(&rmask);
|
|
result = 0;
|
|
do {
|
|
FD_SET(fd, &rmask);
|
|
ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo);
|
|
if (ret > 0) {
|
|
l = recv(fd, garbage, sizeof garbage - 1, 0);
|
|
if (l > 0) {
|
|
/* swallow */
|
|
garbage[l] = '\0';
|
|
/* printf("(((%s)))\n", garbage); */
|
|
result += l;
|
|
} else if (l == 0) {
|
|
errno = ECONNRESET;
|
|
return -2;
|
|
} else if (l < 0) {
|
|
return -2;
|
|
}
|
|
}
|
|
} while (ret > 0);
|
|
return result;
|
|
}
|
|
|
|
void PrintChar(char chr)
|
|
{
|
|
if (chr <= 32 || chr >= 127) {
|
|
printf("%2.2x ", chr);
|
|
} else {
|
|
printf(" %c ", chr);
|
|
}
|
|
}
|
|
|
|
int AsconConnectSuccess(int fd)
|
|
{
|
|
fd_set wmask, rmask;
|
|
struct timeval tmo = { 0, 0 };
|
|
int oldopts;
|
|
int ret;
|
|
|
|
oldopts = fcntl(fd, F_GETFL, 0);
|
|
assert(oldopts | O_NONBLOCK); /* fd must be in non-blocking mode */
|
|
|
|
FD_ZERO(&wmask);
|
|
FD_ZERO(&rmask);
|
|
FD_SET(fd, &wmask);
|
|
FD_SET(fd, &rmask);
|
|
ret = uselect(fd + 1, &rmask, &wmask, NULL, &tmo);
|
|
if (ret > 0) {
|
|
/**
|
|
* MK:
|
|
* This assertion triggered for some reason: as writing is only done later
|
|
* I moved the test for ISSET(wmask) down there
|
|
* assert(FD_ISSET(fd, &wmask));
|
|
* MZ:
|
|
* I remember having found this logic on the www. Anyway, your way must be o.k. too
|
|
*/
|
|
|
|
if (FD_ISSET(fd, &rmask)) { /* there may already be data for read */
|
|
if (recv(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
|
|
ret = ASCON_RECV_ERROR; /* first recv failed */
|
|
}
|
|
} else {
|
|
if (FD_ISSET(fd,&wmask)) {
|
|
if (send(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
|
|
ret = ASCON_SEND_ERROR; /* first send failed */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fcntl(fd, F_SETFL, oldopts & ~O_NONBLOCK); /* reset to blocking mode */
|
|
return ret;
|
|
}
|
|
|
|
int AsconReadChar(int fd, char *chr)
|
|
{
|
|
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, chr, 1, 0);
|
|
/* PrintChar(*chr); fflush(stdout); */
|
|
if (ret > 0)
|
|
return 1;
|
|
if (ret == 0) {
|
|
errno = ECONNRESET;
|
|
return ASCON_DISCONNECTED;
|
|
}
|
|
return ASCON_RECV_ERROR;
|
|
}
|
|
|
|
int AsconWriteChars(int fd, char *data, int length)
|
|
{
|
|
fd_set wmask;
|
|
struct timeval tmo = { 0, 0 };
|
|
int ret;
|
|
|
|
if (length <= 0)
|
|
return 0;
|
|
|
|
/* { int i;
|
|
for (i=0; i<length; i++) {
|
|
PrintChar(data[i]);
|
|
}
|
|
}
|
|
printf("<written\n");
|
|
*/
|
|
|
|
FD_ZERO(&wmask);
|
|
FD_SET(fd, &wmask);
|
|
ret = uselect(fd + 1, NULL, &wmask, NULL, &tmo);
|
|
if (ret <= 0)
|
|
return ASCON_SELECT_ERROR;
|
|
ret = send(fd, data, length, 0);
|
|
if (ret > 0)
|
|
return ret;
|
|
if (ret == 0) {
|
|
errno = ECONNRESET;
|
|
return ASCON_DISCONNECTED;
|
|
}
|
|
return ASCON_SEND_ERROR;
|
|
}
|
|
|
|
static double lastCall = 0;
|
|
|
|
int AsconBaseHandler(Ascon * a)
|
|
{
|
|
int ret;
|
|
int l;
|
|
char chr;
|
|
double now = DoubleTime();
|
|
|
|
if (now > lastCall + 0.5) { /* AsconBaseHandler was not called since a long time (0.5 sec) */
|
|
if (lastCall != 0) { /* extend timeout time (for debugging purposes) */
|
|
a->start += now - lastCall - 0.5;
|
|
}
|
|
}
|
|
lastCall = now;
|
|
switch (a->state) {
|
|
case AsconConnectStart:
|
|
AsconConnect(a);
|
|
break;
|
|
case AsconConnecting:
|
|
ret = AsconConnectSuccess(a->fd);
|
|
if (ret == 0) {
|
|
/* in progress */
|
|
} else if (ret > 0) {
|
|
DynStringClear(a->errmsg);
|
|
a->state = AsconConnectDone; /* success */
|
|
} else if (ret < 0) {
|
|
AsconError(a, "ASC3", errno);
|
|
}
|
|
break;
|
|
case AsconWriteStart:
|
|
if (a->sendTerminator) {
|
|
DynStringConcat(a->wrBuffer, a->sendTerminator);
|
|
}
|
|
a->wrPos = 0;
|
|
a->state = AsconWriting;
|
|
break;
|
|
case AsconWriting:
|
|
AsconReadGarbage(a->fd);
|
|
l = GetDynStringLength(a->wrBuffer) - a->wrPos;
|
|
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
|
|
if (ret < 0) {
|
|
AsconError(a, "ASC4", errno); /* sets state to AsconFailed */
|
|
} else {
|
|
a->wrPos += ret;
|
|
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
|
|
a->state = AsconWriteDone;
|
|
}
|
|
}
|
|
break;
|
|
case AsconReadStart:
|
|
DynStringClear(a->rdBuffer);
|
|
a->start = DoubleTime();
|
|
a->state = AsconReading;
|
|
break;
|
|
case AsconReading:
|
|
ret = AsconReadChar(a->fd, &chr);
|
|
if (ret <= 0) {
|
|
if (ret < 0) {
|
|
AsconError(a, "ASC5", errno);
|
|
return 0;
|
|
}
|
|
if (a->timeout > 0) {
|
|
if (DoubleTime() - a->start > a->timeout) {
|
|
AsconError(a, "no response", 0);
|
|
a->state = AsconTimeout;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
a->lastChar = chr;
|
|
a->start = DoubleTime();
|
|
if (DynStringConcatChar(a->rdBuffer, chr) == 0) {
|
|
AsconError(a, "ASC6", errno);
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void AsconInsertProtocol(AsconProtocol *protocol) {
|
|
AsconProtocol *p;
|
|
|
|
for (p = protocols; p != NULL; p = p->next) {
|
|
if (p == protocol)
|
|
return;
|
|
}
|
|
protocol->next = protocols;
|
|
protocols = protocol;
|
|
}
|
|
|
|
AsconHandler AsconSetHandler(Ascon *a, SConnection *con,
|
|
int argc, char *argv[])
|
|
{
|
|
AsconProtocol *p;
|
|
|
|
if (argc < 1)
|
|
return NULL;
|
|
if (strcasecmp(argv[0], "std") == 0) {
|
|
if (argc < 2)
|
|
return NULL;
|
|
if (AsconStdInit(a, con, argc, argv)) {
|
|
return AsconStdHandler;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
for (p = protocols; p!= NULL; p=p->next) {
|
|
if (strcasecmp(p->name, argv[0]) == 0) {
|
|
if (p->init(a, con, argc, argv)) {
|
|
return p->handler;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* --- standard handler ---- */
|
|
|
|
int AsconStdHandler(Ascon * a)
|
|
{
|
|
int result;
|
|
char chr;
|
|
int ret, l;
|
|
char *cmd, *opt, *buf;
|
|
|
|
switch (a->state) {
|
|
case AsconWriteStart:
|
|
cmd = GetCharArray(a->wrBuffer);
|
|
a->lineCount = 1;
|
|
if (a->separator != NULL) { /* multiline mode enabled */
|
|
l = strlen(cmd);
|
|
if (l> 0 && cmd[l-1] == '}') {
|
|
opt = strrchr(cmd, '{');
|
|
if (opt != NULL) {
|
|
if (sscanf(opt, "{%d}", &a->lineCount) == 1) {
|
|
/* remove option */
|
|
for (l = strlen(opt); l > 0; l--) {
|
|
DynStringBackspace(a->wrBuffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (strstr(GetCharArray(a->wrBuffer), "@@NOSEND@@") != NULL) {
|
|
a->state = AsconWriteDone;
|
|
return 1;
|
|
}
|
|
if(a->lineCount == 0){
|
|
a->noResponse = 1;
|
|
} else {
|
|
a->noResponse = 0;
|
|
}
|
|
break; /* go to the base handler */
|
|
case AsconReading:
|
|
if (a->lineCount == 0) {
|
|
/* no response expected */
|
|
a->state = AsconReadDone;
|
|
return 1;
|
|
}
|
|
result = AsconBaseHandler(a);
|
|
if (result == 0)
|
|
return 0;
|
|
chr = a->lastChar;
|
|
if (chr == '\0') { /* skip NUL characters */
|
|
DynStringBackspace(a->rdBuffer); /* remove NUL */
|
|
return 1;
|
|
}
|
|
if (a->replyTerminator != NULL) {
|
|
if (strchr(a->replyTerminator, chr) != NULL) {
|
|
if (a->compositeTerminator) {
|
|
/* one character was o.k., but all have to match */
|
|
l = strlen(a->replyTerminator);
|
|
buf = GetCharArray(a->rdBuffer) + GetDynStringLength(a->rdBuffer) - l;
|
|
if (strncmp(buf, a->replyTerminator, l) == 0) {
|
|
a->state = AsconReadDone;
|
|
}
|
|
} else {
|
|
a->state = AsconReadDone;
|
|
if (chr == '\n' || chr == '\r') {
|
|
DynStringBackspace(a->rdBuffer); /* remove LF or CR */
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (chr == '\n') { /* LF */
|
|
DynStringBackspace(a->rdBuffer); /* remove LF */
|
|
if (a->readState) { /* last char was CR */
|
|
/* LF after CR is not a terminator */
|
|
a->readState = 0;
|
|
} else {
|
|
a->state = AsconReadDone;
|
|
}
|
|
} else if (chr == '\r') { /* CR */
|
|
DynStringBackspace(a->rdBuffer); /* remove CR */
|
|
a->readState = 1; /* set 'last char was CR' */
|
|
a->state = AsconReadDone;
|
|
}
|
|
}
|
|
if (a->state == AsconReadDone && a->lineCount > 1) {
|
|
if (a->separator != NULL) {
|
|
DynStringConcat(a->rdBuffer, a->separator);
|
|
}
|
|
a->lineCount--;
|
|
a->state = AsconReading;
|
|
}
|
|
return 1; /* base handler was already called */
|
|
default:
|
|
break;
|
|
}
|
|
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.
|
|
* M.Z. changed strstr to strncmp (more precise)
|
|
*
|
|
* M.Z. add new option (composite terminator):
|
|
* convert 'term' to term
|
|
*/
|
|
void AsconCheckTerminators(Ascon *a)
|
|
{
|
|
int i, l;
|
|
|
|
checkTerminator(a->sendTerminator);
|
|
checkTerminator(a->replyTerminator);
|
|
a->compositeTerminator = 0;
|
|
if (a->replyTerminator != NULL && a->replyTerminator[0] == '\'') {
|
|
l = strlen(a->replyTerminator);
|
|
if (l > 2 && a->replyTerminator[l-1] == '\'') {
|
|
for (i = 0; i < l - 2; i++) {
|
|
a->replyTerminator[i] = a->replyTerminator[i+1];
|
|
}
|
|
a->replyTerminator[l-2] = '\0';
|
|
a->compositeTerminator = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int AsconInterpreteArgs(int argc, char *argv[],
|
|
int parc, char *parn[], char *pars[])
|
|
{
|
|
/* interprete arguments in the form "name=value" or
|
|
with a fixed order. The result is 1 in case
|
|
of success or 0 in case of an error. */
|
|
|
|
int ia, ip, l;
|
|
|
|
for (ip = 0; ip < parc; ip++) {
|
|
pars[ip] = NULL;
|
|
}
|
|
if (argc == 0) return 1;
|
|
if (strchr(argv[0], '=') == NULL) {
|
|
for (ia = 0; ia < argc; ia++) {
|
|
if (ia >= ip) {
|
|
return 0;
|
|
}
|
|
if (strchr(argv[ia], '=') != NULL) {
|
|
return 0;
|
|
}
|
|
pars[ia] = argv[ia];
|
|
}
|
|
return 1;
|
|
}
|
|
for (ia = 0; ia < argc; ia++) {
|
|
for (ip = 0; ip < parc; ip++) {
|
|
l = strlen(parn[ip]);
|
|
if (strncasecmp(argv[ia], parn[ip], l) == 0 && argv[ia][l] == '=') {
|
|
pars[ip] = argv[ia] + l + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (ip >= parc) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[])
|
|
{
|
|
enum nPars {NA=4};
|
|
char *pars[NA];
|
|
static char *parn[NA]={
|
|
"sendterminator",
|
|
"timeout",
|
|
"replyterminator",
|
|
"lineseparator"
|
|
};
|
|
char *msg;
|
|
|
|
assert(argc>1);
|
|
a->hostport = strdup(argv[1]);
|
|
|
|
if (!AsconInterpreteArgs(argc-2, argv+2, NA, parn, pars)) {
|
|
return 0;
|
|
}
|
|
|
|
if (pars[0]) {
|
|
a->sendTerminator = strdup(pars[0]);
|
|
} else {
|
|
a->sendTerminator = strdup("\n");
|
|
}
|
|
if (pars[1] && pars[1][0] != '\0') {
|
|
a->timeout = atof(pars[1]);
|
|
} else {
|
|
a->timeout = 2.0; /* sec */
|
|
}
|
|
if (pars[2] && pars[2][0] != '\0') {
|
|
a->replyTerminator = strdup(pars[2]);
|
|
} else {
|
|
a->replyTerminator = NULL;
|
|
}
|
|
if (pars[3] && pars[3][0] != '\0') {
|
|
a->separator = strdup(pars[3]);
|
|
} else {
|
|
a->separator = NULL;
|
|
}
|
|
AsconCheckTerminators(a);
|
|
return 1;
|
|
}
|
|
|
|
/* --- implementation of higher level interface ---- */
|
|
|
|
char *ConcatArgs(int argc, char *argv[])
|
|
{
|
|
return Arg2Tcl(argc, argv, NULL, -1);
|
|
}
|
|
|
|
Ascon *AsconMake(SConnection * con, int argc, char *argv[])
|
|
{
|
|
Ascon *a;
|
|
char *args;
|
|
|
|
a = calloc(1, sizeof(*a));
|
|
if (a == NULL) {
|
|
SCWrite(con, "ERROR: no memory", eError);
|
|
return NULL;
|
|
}
|
|
a->fd = -1;
|
|
a->state = AsconConnectStart;
|
|
a->timeout = 2.0; /* 2 sec default */
|
|
a->reconnectInterval = 10; /* 10 sec default */
|
|
a->replyTerminator = NULL;
|
|
a->sendTerminator = NULL;
|
|
a->hostport = NULL;
|
|
a->responseValid = 0;
|
|
a->readState = 0;
|
|
a->lineCount = 1;
|
|
a->separator = NULL;
|
|
a->killPrivate = NULL;
|
|
a->private = NULL;
|
|
|
|
a->handler = AsconSetHandler(a, con, argc, argv);
|
|
if (a->handler == NULL) {
|
|
args = ConcatArgs(argc, argv);
|
|
if (!args)
|
|
return NULL;
|
|
SCPrintf(con, eError, "ERROR: illegal protocol: %s", args);
|
|
free(args);
|
|
return NULL;
|
|
}
|
|
a->rdBuffer = CreateDynString(60, 65536);
|
|
a->wrBuffer = CreateDynString(60, 63);
|
|
a->errmsg = CreateDynString(60, 63);
|
|
|
|
return a;
|
|
}
|
|
|
|
void AsconKill(Ascon * a)
|
|
{
|
|
if (a->fd > 0) {
|
|
close(a->fd);
|
|
lastClose = DoubleTime();
|
|
}
|
|
DeleteDynString(a->rdBuffer);
|
|
DeleteDynString(a->wrBuffer);
|
|
DeleteDynString(a->errmsg);
|
|
if (a->hostport) {
|
|
free(a->hostport);
|
|
}
|
|
if (a->sendTerminator) {
|
|
free(a->sendTerminator);
|
|
}
|
|
if (a->replyTerminator) {
|
|
free(a->replyTerminator);
|
|
}
|
|
if (a->separator) {
|
|
free(a->separator);
|
|
}
|
|
if (a->private != NULL && a->killPrivate != NULL) {
|
|
a->killPrivate(a->private);
|
|
}
|
|
free(a);
|
|
}
|
|
|
|
void AsconDisconnect(Ascon * a)
|
|
{
|
|
if (a->fd > 0) {
|
|
close(a->fd);
|
|
lastClose = DoubleTime();
|
|
}
|
|
a->fd = -1;
|
|
a->state = AsconNotConnected;
|
|
}
|
|
|
|
void AsconReconnect(Ascon * a, char * hostport)
|
|
{
|
|
if (a->fd > 0) {
|
|
close(a->fd);
|
|
lastClose = DoubleTime();
|
|
}
|
|
if (hostport != NULL && hostport[0] != '\0') {
|
|
if (a->hostport) {
|
|
free(a->hostport);
|
|
}
|
|
a->hostport = strdup(hostport);
|
|
}
|
|
a->fd = -1;
|
|
a->state = AsconConnectStart;
|
|
}
|
|
|
|
AsconStatus AsconTask(Ascon * a)
|
|
{
|
|
int result;
|
|
double now;
|
|
|
|
while (1) {
|
|
result = a->handler(a);
|
|
switch (a->state) {
|
|
case AsconNotConnected:
|
|
return AsconOffline;
|
|
case AsconConnecting:
|
|
return AsconConnectPending;
|
|
case AsconConnectDone:
|
|
a->state = AsconIdle;
|
|
DynStringClear(a->errmsg); /* connection o.k. */
|
|
return AsconReady;
|
|
case AsconWriteDone:
|
|
if (a->noResponse) {
|
|
a->state = AsconIdle;
|
|
return AsconReady;
|
|
}
|
|
a->state = AsconReadStart;
|
|
break;
|
|
case AsconReading:
|
|
if (result != 0) {
|
|
break; /* char received: try again, probably more characters pending */
|
|
}
|
|
return AsconPending;
|
|
case AsconReadDone:
|
|
a->state = AsconIdle;
|
|
a->responseValid = 1;
|
|
DynStringClear(a->errmsg);
|
|
return AsconReady;
|
|
case AsconReadDoneReconnect:
|
|
a->responseValid = 1;
|
|
DynStringClear(a->errmsg);
|
|
AsconReconnect(a,NULL);
|
|
return AsconReady;
|
|
case AsconIdle:
|
|
return AsconReady;
|
|
case AsconTimeout:
|
|
a->state = AsconIdle;
|
|
return AsconFailure;
|
|
case AsconFailed:
|
|
now = DoubleTime();
|
|
if (a->fd > 0) {
|
|
close(a->fd);
|
|
lastClose = now;
|
|
a->fd = -1;
|
|
}
|
|
if (a->reconnectInterval > 0 && now > a->lastReconnect + a->reconnectInterval) {
|
|
a->lastReconnect = now;
|
|
a->state = AsconConnectStart;
|
|
}
|
|
return AsconFailure;
|
|
default:
|
|
return AsconPending;
|
|
}
|
|
}
|
|
}
|
|
|
|
int AsconWrite(Ascon * a, char *command, int noResponse)
|
|
{
|
|
if (a->state != AsconIdle) {
|
|
/* this might happen if a script is sending after an error */
|
|
return 0;
|
|
}
|
|
|
|
DynStringCopy(a->wrBuffer, command);
|
|
a->noResponse = noResponse;
|
|
a->state = AsconWriteStart;
|
|
a->responseValid = 0;
|
|
return AsconTask(a);
|
|
}
|
|
|
|
char *AsconRead(Ascon * a)
|
|
{
|
|
if (a->noResponse) {
|
|
a->noResponse = 0;
|
|
return NULL;
|
|
}
|
|
if (a->state != AsconIdle) {
|
|
a->state = AsconIdle;
|
|
return "programming error in devser.c/ascon.c";
|
|
}
|
|
if (a->responseValid) {
|
|
a->responseValid = 0;
|
|
return GetCharArray(a->rdBuffer);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *AsconGetError(Ascon *a)
|
|
{
|
|
return GetCharArray(a->errmsg);
|
|
}
|
|
|
|
int AsconLastState(Ascon *a)
|
|
{
|
|
return (int)a->state;
|
|
}
|
|
|
|
char *AsconHostport(Ascon *a)
|
|
{
|
|
if (a==NULL) {
|
|
return NULL;
|
|
}
|
|
return a->hostport;
|
|
}
|
|
|
|
char *AsconIP(Ascon *a)
|
|
{
|
|
if (a==NULL) {
|
|
return NULL;
|
|
}
|
|
return a->ip;
|
|
}
|
|
|
|
double AsconGetSetTimeout(Ascon *a, double timeout, int setmode) {
|
|
if (setmode) {
|
|
a->timeout = timeout;
|
|
}
|
|
return a->timeout;
|
|
}
|
|
|
|
int AsconReconnectInterval(Ascon *a, int interval) {
|
|
if (interval >= 0) {
|
|
a->reconnectInterval = interval;
|
|
}
|
|
return a->reconnectInterval;
|
|
}
|