- allow scriptcontext objects to be dynamic

- enhancements in scriptcontext (error messages stored as properties)
This commit is contained in:
zolliker
2009-02-19 13:30:32 +00:00
parent 981534624f
commit 35f2b6b810
33 changed files with 753 additions and 310 deletions

172
ascon.c
View File

@ -14,6 +14,8 @@
#include "ascon.i"
#include "uselect.h"
static double lastClose = 0; /* time of last close operation */
/*
CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout
*/
@ -60,13 +62,13 @@ double DoubleTime(void)
return now.tv_sec + now.tv_usec / 1e6;
}
void AsconError(Ascon * a, char *msg, int errorno)
void AsconError(Ascon *a, char *msg, int errorno)
{
static char *stateText[] = {
"state 0", "kill", "state 2", "notConnected",
"connect", "start connect", "connect finished", "connect failed",
"write", "start write", "write finished", "write failed",
"read", "start read", "read finished", "read failed",
static char *stateText[]={
"state 0", "state 1", "state 2", "notConnected",
"connect", "start connect", "connect finished", "connect failed",
"write", "start write", "write finished", "write failed",
"read", "start read", "read finished", "read failed",
"state 16", "state 17", "state 18", "idle"
};
char *state;
@ -76,13 +78,19 @@ void AsconError(Ascon * a, char *msg, int errorno)
} else {
state = stateText[a->state];
}
if (errorno != 0) {
a->errList =
ErrPutMsg(a->errList, "ASCERR: %s %s (during %s)", msg,
strerror(errorno), state);
DynStringCopy(a->errmsg, "ASCERR: ");
if (errorno == 0) {
DynStringConcat(a->errmsg, msg);
DynStringConcat(a->errmsg, " (");
DynStringConcat(a->errmsg, state);
DynStringConcat(a->errmsg, " state)");
} else {
a->errList =
ErrPutMsg(a->errList, "ASCERR: %s (during %s)", msg, state);
DynStringConcat(a->errmsg, strerror(errorno));
DynStringConcat(a->errmsg, " (");
DynStringConcat(a->errmsg, state);
DynStringConcat(a->errmsg, " state, ");
DynStringConcat(a->errmsg, msg);
DynStringConcat(a->errmsg, ")");
}
a->state |= AsconFailed;
}
@ -96,11 +104,23 @@ static void AsconConnect(Ascon * a)
char *colon;
int port;
int oldopts;
/* 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, "socket failed:", errno);
AsconError(a, "ASC1", errno);
return;
}
}
@ -119,20 +139,19 @@ static void AsconConnect(Ascon * a)
AsconError(a, "bad host specification", 0);
return;
}
/* should we insert the workaround for lantronix server ? see network.c */
oldopts = fcntl(a->fd, F_GETFL, 0);
fcntl(a->fd, F_SETFL, oldopts | O_NONBLOCK);
ret =
connect(a->fd, (struct sockaddr *) &adr, sizeof(struct sockaddr_in));
if (ret < 0) {
switch (errno) {
switch(errno) {
case EINPROGRESS:
case EALREADY:
case EISCONN:
a->state = AsconConnecting;
break;
default:
AsconError(a, "connect failed:", errno);
AsconError(a, "ASC2", errno);
return;
}
}
@ -140,11 +159,10 @@ static void AsconConnect(Ascon * a)
return;
}
int AsconStdInit(Ascon * a, SConnection * con, int argc, char *argv[])
int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[])
{
a->fd = -1;
a->state = AsconConnectStart;
a->reconnectInterval = 10;
a->hostport = strdup(argv[1]);
if (argc > 2) {
a->sendTerminator = strdup(argv[2]);
@ -154,7 +172,11 @@ int AsconStdInit(Ascon * a, SConnection * con, int argc, char *argv[])
if (argc > 3) {
a->timeout = atof(argv[3]);
} else {
a->timeout = 2.0; /* sec */
a->timeout = 2.0; /* sec */
}
a->replyTerminator == NULL;
if (argc > 4 && argv[4][0] != '\0') {
a->replyTerminator = strdup(argv[4]);
}
return 1;
}
@ -172,9 +194,11 @@ int AsconReadGarbage(int fd)
FD_SET(fd, &rmask);
ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo);
if (ret > 0) {
l = recv(fd, garbage, sizeof garbage, 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;
@ -307,7 +331,7 @@ int AsconStdHandler(Ascon * a)
} else if (ret > 0) {
a->state = AsconConnectDone; /* success */
} else if (ret < 0) {
AsconError(a, "AsconConnectSuccess failed:", errno);
AsconError(a, "ASC3", errno);
}
break;
case AsconWriteStart:
@ -320,13 +344,22 @@ int AsconStdHandler(Ascon * a)
}
break;
case AsconWriting:
if (a->readState) { /* last char was CR */
ret = AsconReadChar(a->fd, &chr);
if (ret > 0) {
if (chr == '\n') {
/* swallow LF after CR */
a->readState = 0;
} else {
/* garbage character found */
}
}
}
AsconReadGarbage(a->fd);
l = GetDynStringLength(a->wrBuffer) - a->wrPos;
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
if (ret < 0) {
if (errno != EINTR && errno != EAGAIN) {
AsconError(a, "send failed:", errno);
}
AsconError(a, "ASC4", errno);
/*
* Ooops: which state shall we go to after a write fail?
* This seems to retry.
@ -347,23 +380,33 @@ int AsconStdHandler(Ascon * a)
ret = AsconReadChar(a->fd, &chr);
while (ret > 0) {
a->start = DoubleTime();
if (chr == '\n') {
if (a->readState) {
/* swallow LF after CR */
DynStringClear(a->rdBuffer);
a->readState = 0;
} else {
if (a->replyTerminator != NULL) {
if (strchr(a->replyTerminator, chr) != NULL) {
DynStringConcatChar(a->rdBuffer, chr);
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone;
break;
}
} else if (chr == '\r') {
a->readState = 1;
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone;
break;
} else {
if (chr == '\n') {
if (a->readState) { /* last char was CR */
/* swallow LF after CR */
a->readState = 0;
chr = 0;
} else {
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone;
break;
}
} else if (chr == '\r') {
a->readState = 1; /* set 'last char was CR' */
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone;
break;
}
}
if (chr != 0) {
if (DynStringConcatChar(a->rdBuffer, chr) == 0) {
AsconError(a, "DynStringConcatChar failed:", ENOMEM);
break;
@ -375,7 +418,7 @@ int AsconStdHandler(Ascon * a)
if (ret < 0) {
/* EINTR means we shall retry */
if (errno != EINTR && errno != EAGAIN) {
AsconError(a, "AsconReadChar failed:", errno);
AsconError(a, "ASC5", errno);
}
return 1;
}
@ -384,30 +427,26 @@ int AsconStdHandler(Ascon * a)
} else {
if (a->timeout > 0) {
if (DoubleTime() - a->start > a->timeout) {
AsconError(a, "read timeout", 0);
AsconError(a, "no response", 0);
a->state = AsconTimeout;
}
}
}
break;
default:
return 1;
}
break;
}
return 1;
}
/* define type AsconProtocolList and functions AsconProtocolAdd etc. */
#define MC_NAME(T) AsconProtocol##T
#include "mclist.c"
static AsconProtocol *protocols = NULL;
static AsconProtocolList protocols = { 0 };
void AsconInsertProtocol(AsconProtocol * protocol)
{
AsconProtocolAdd(&protocols, protocol);
void AsconInsertProtocol(AsconProtocol *protocol) {
protocol->next = protocols;
protocols = protocol;
}
AsconHandler AsconSetHandler(Ascon * a, SConnection * con,
AsconHandler AsconSetHandler(Ascon *a, SConnection *con,
int argc, char *argv[])
{
AsconProtocol *p;
@ -420,7 +459,7 @@ AsconHandler AsconSetHandler(Ascon * a, SConnection * con,
AsconStdInit(a, con, argc, argv);
return AsconStdHandler;
}
for (p = protocols.head; p != NULL; p = p->next) {
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;
@ -460,10 +499,8 @@ Ascon *AsconMake(SConnection * con, int argc, char *argv[])
}
a->rdBuffer = CreateDynString(60, 63);
a->wrBuffer = CreateDynString(60, 63);
a->errList = NULL;
a->errmsg = CreateDynString(60, 63);
a->responseValid = 0;
a->reconnectInterval = 10;
a->lastReconnect = 0;
return a;
}
@ -471,15 +508,20 @@ 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) {
if(a->sendTerminator){
free(a->sendTerminator);
}
if(a->replyTerminator){
free(a->replyTerminator);
}
if (a->private != NULL && a->killPrivate != NULL) {
a->killPrivate(a->private);
}
@ -528,17 +570,15 @@ AsconStatus AsconTask(Ascon * a)
case AsconStart:
return AsconPending;
case AsconFailed:
if (a->state != AsconTimeout) {
now = DoubleTime();
if (now > a->lastReconnect + a->reconnectInterval) {
a->lastReconnect = now;
close(a->fd);
/* allow the system to cleanup the socket, otherwise a reconnect will fail */
sleep(1);
a->fd = -1;
a->state = AsconConnectStart;
}
if (a->state == AsconTimeout) {
a->state = AsconIdle;
} else {
close(a->fd);
lastClose = DoubleTime();
a->fd = -1;
a->state = AsconConnectStart;
}
return AsconFailure;
case AsconFinished:
if (a->state < AsconConnectFailed) {
@ -580,7 +620,7 @@ char *AsconRead(Ascon * a)
return NULL;
}
ErrMsg *AsconGetErrList(Ascon * a)
char *AsconGetError(Ascon *a)
{
return a->errList;
return GetCharArray(a->errmsg);
}