- allow scriptcontext objects to be dynamic
- enhancements in scriptcontext (error messages stored as properties)
This commit is contained in:
172
ascon.c
172
ascon.c
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user