- 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
1044 lines
26 KiB
C
1044 lines
26 KiB
C
/*--------------------------------------------------------------------------
|
|
|
|
Some networking functions. This version for TCP/IP.
|
|
|
|
|
|
|
|
|
|
Mark Koennecke, October 1996
|
|
|
|
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 "network.h"
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include "sics.h"
|
|
#include "uselect.h"
|
|
#include "socketaddr.h"
|
|
|
|
#define PORT 1
|
|
#define SOCKET 2
|
|
#define UDP 3
|
|
|
|
/* wait time [ms] between a close and the next open */
|
|
#define WAIT_CLOSE_OPEN 500
|
|
|
|
struct timeval lastclose = { -1, 0 };
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Redefine this function if another means of error reporting is necessary.
|
|
*/
|
|
static void NetError(const char pText[])
|
|
{
|
|
unsigned int severity = DEBUG;
|
|
if(strstr(pText,"ERROR") != NULL){
|
|
severity = ERROR;
|
|
} else if(strstr(pText,"WARNING") != NULL){
|
|
severity = WARN;
|
|
}
|
|
|
|
Log(severity,"io","%s", pText);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
mkChannel *NETOpenPort(int iPort)
|
|
{
|
|
mkChannel *pRes = NULL;
|
|
int iRet, i;
|
|
struct linger lili;
|
|
|
|
pRes = (mkChannel *) malloc(sizeof(mkChannel));
|
|
if (!pRes)
|
|
return NULL;
|
|
|
|
/* open a socket */
|
|
pRes->sockid = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (pRes->sockid < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
/* REUSEADDR, for restarts */
|
|
i = 1;
|
|
setsockopt(pRes->sockid, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int));
|
|
|
|
/* bind */
|
|
memset(&(pRes->adresse), 0, sizeof(struct sockaddr_in));
|
|
pRes->adresse.sin_family = AF_INET;
|
|
pRes->adresse.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
pRes->adresse.sin_port = htons(iPort);
|
|
iRet = bind(pRes->sockid, (struct sockaddr *) &(pRes->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
if (iRet < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
/* listen */
|
|
iRet = listen(pRes->sockid, 8);
|
|
if (iRet < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
i = sizeof(struct linger);
|
|
lili.l_onoff = 1;
|
|
lili.l_linger = 1;
|
|
/*
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_LINGER,&lili,i);
|
|
*/
|
|
pRes->iType = PORT;
|
|
pRes->lMagic = NETMAGIC;
|
|
return pRes;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
mkChannel *NETAccept(mkChannel * self, long timeout)
|
|
{
|
|
mkChannel *pRes = NULL;
|
|
int iRet;
|
|
fd_set lMask;
|
|
struct timeval tmo = { 1, 0 };
|
|
int i;
|
|
socklen_t iLen;
|
|
struct linger lili;
|
|
|
|
assert(self != NULL);
|
|
|
|
if (timeout >= 0) {
|
|
/* select first */
|
|
tmo.tv_usec = (timeout % 1000) * 1000;
|
|
tmo.tv_sec = timeout / 1000;
|
|
FD_ZERO(&lMask);
|
|
FD_SET(self->sockid, &lMask);
|
|
if ((self->sockid >= FD_SETSIZE) || (self->sockid < 0)) { /* invalid descriptor */
|
|
return NULL; /* eof */
|
|
}
|
|
iRet =
|
|
uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
|
|
if (iRet <= 0) {
|
|
/* failure, or no request */
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* positive: accept */
|
|
iLen = sizeof(struct sockaddr);
|
|
pRes = (mkChannel *) malloc(sizeof(mkChannel));
|
|
if (!pRes)
|
|
return NULL;
|
|
|
|
pRes->sockid = accept(self->sockid,
|
|
(struct sockaddr *) &(pRes->adresse), &iLen);
|
|
if (pRes->sockid < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
/*
|
|
i = sizeof(struct linger);
|
|
lili.l_onoff = 1;
|
|
lili.l_linger = 1;
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_LINGER,&lili,i);
|
|
i = 1;
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
|
|
*/
|
|
pRes->iType = SOCKET;
|
|
pRes->lMagic = NETMAGIC;
|
|
return pRes;
|
|
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
mkChannel *NETConnectWithFlags(char *name, int port, int flags)
|
|
{
|
|
mkChannel *pRes = NULL;
|
|
int iRet, i, cnt;
|
|
char pBueffel[80];
|
|
struct hostent *pServer = NULL;
|
|
struct linger lili;
|
|
struct timeval now;
|
|
long dif;
|
|
int oldopts;
|
|
|
|
assert(port > 0);
|
|
|
|
/* default name to localhost */
|
|
if (name == NULL) {
|
|
strcpy(pBueffel, "localhost");
|
|
name = pBueffel;
|
|
}
|
|
|
|
/* new channel */
|
|
pRes = (mkChannel *) malloc(sizeof(mkChannel));
|
|
if (!pRes)
|
|
return NULL;
|
|
|
|
/* connect */
|
|
iRet = CreateSocketAdress(&(pRes->adresse), name, port);
|
|
if (!iRet) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
pRes->sockid = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (pRes->sockid < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
if (flags & 2) { /* wait if closed recently */
|
|
/* workaround for a bug in Lantronix terminal server:
|
|
if a channel is closed and reopened within short time
|
|
the connect will be succesful, but a message will be
|
|
sent on the channel! */
|
|
|
|
if (lastclose.tv_sec >= 0) {
|
|
gettimeofday(&now, NULL);
|
|
dif =
|
|
(now.tv_sec - lastclose.tv_sec) * 1000 + (now.tv_usec -
|
|
lastclose.tv_usec) /
|
|
1000;
|
|
if (dif < 0)
|
|
dif += 24 * 3600 * 1000;
|
|
} else {
|
|
dif = 0;
|
|
}
|
|
if (dif < WAIT_CLOSE_OPEN) {
|
|
usleep((WAIT_CLOSE_OPEN - dif) * 1000);
|
|
}
|
|
}
|
|
|
|
if (flags & 1) { /* open in non blocking mode */
|
|
oldopts = fcntl(pRes->sockid, F_GETFL, 0);
|
|
fcntl(pRes->sockid, F_SETFL, oldopts | O_NONBLOCK);
|
|
}
|
|
|
|
iRet = connect(pRes->sockid, (struct sockaddr *) &(pRes->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
if (iRet < 0 && errno ) {
|
|
if (errno != EINPROGRESS) {
|
|
close(pRes->sockid);
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
}
|
|
/*
|
|
i = sizeof(struct linger);
|
|
lili.l_onoff = 1;
|
|
lili.l_linger = 1;
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_LINGER,&lili,i);
|
|
i = 1;
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
|
|
*/
|
|
pRes->iType = SOCKET;
|
|
pRes->lMagic = NETMAGIC;
|
|
return pRes;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
mkChannel *NETConnect(char *name, int port)
|
|
{
|
|
return NETConnectWithFlags(name, port, 0);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int NETConnectFinished(mkChannel * self)
|
|
{
|
|
fd_set wmask, rmask;
|
|
struct timeval tmo = { 0, 0 };
|
|
int iret;
|
|
int oldopts;
|
|
int olderrno;
|
|
|
|
if (self->sockid == 0) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
oldopts = fcntl(self->sockid, F_GETFL, 0);
|
|
if (!(oldopts & O_NONBLOCK)) { /* DCL was | */
|
|
/* assume success when in blocking mode */
|
|
return 1;
|
|
}
|
|
FD_ZERO(&wmask);
|
|
FD_ZERO(&rmask);
|
|
FD_SET(self->sockid, &wmask);
|
|
FD_SET(self->sockid, &rmask);
|
|
iret = uselect(self->sockid + 1, &rmask, &wmask, NULL, &tmo);
|
|
if (iret == 0)
|
|
return 0; /* in progress */
|
|
if (iret > 0) {
|
|
/* the connection has either succeded or failed
|
|
- the write flag indicates success
|
|
- the read flag indicates, if there is already data pending
|
|
the read flag should not appear without the write flag
|
|
*/
|
|
if (FD_ISSET(self->sockid, &wmask)) { /* should always be true */
|
|
if (FD_ISSET(self->sockid, &rmask)) { /* there may already be data for read */
|
|
iret = recv(self->sockid, NULL, 0, 0); /* zero length, check only return value */
|
|
if (iret >= 0) {
|
|
iret = 1; /* success */
|
|
} /* else failure */
|
|
} else {
|
|
iret = send(self->sockid, NULL, 0, 0); /* zero length, check only return value */
|
|
if (iret >= 0) {
|
|
iret = 1; /* success */
|
|
} /* else failure */
|
|
}
|
|
}
|
|
}
|
|
/* reset to blocking mode */
|
|
olderrno = errno;
|
|
fcntl(self->sockid, F_SETFL, oldopts & ~O_NONBLOCK); /* DCL was | */
|
|
errno = olderrno;
|
|
return iret;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
int VerifyChannel(mkChannel * self)
|
|
{
|
|
if (!self) {
|
|
return 0;
|
|
}
|
|
if (self->lMagic != NETMAGIC) {
|
|
NetError("CORRUPTED MAGIC: network channel data corrupted");
|
|
return 0;
|
|
}
|
|
if ((self->sockid < 0) || (self->sockid > 65000)) {
|
|
NetError("MAGIC DEAD: Invalid socket number, data corrupted");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
int NETInfo(mkChannel * self, char *pCompost, int iBufLen)
|
|
{
|
|
|
|
int t;
|
|
socklen_t len;
|
|
struct sockaddr_in sin;
|
|
struct hostent *host;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
|
|
len = sizeof sin;
|
|
if (getpeername(self->sockid, (struct sockaddr *) &sin, &len) < 0) {
|
|
return 0;
|
|
} else {
|
|
if ((host = gethostbyaddr((char *) &sin.sin_addr,
|
|
sizeof sin.sin_addr, AF_INET)) == NULL) {
|
|
return 0;
|
|
} else {
|
|
strlcpy(pCompost, host->h_name, iBufLen);
|
|
pCompost[iBufLen - 1] = '\0';
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int NETWrite(mkChannel * self, char *buffer, long lLen)
|
|
{
|
|
long iRet;
|
|
fd_set lMask;
|
|
struct timeval tmo;
|
|
char buf[256];
|
|
time_t expire, delta;
|
|
char *pos;
|
|
long l;
|
|
int disconnected;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
if (!(self->iType == SOCKET)) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if the we can write to the socket first....
|
|
* Well, this how it should be. However, on linux I observe that
|
|
* there is a problem with Java clients not reliably receiving data when
|
|
* this is active.
|
|
*/
|
|
if ((self->sockid >= FD_SETSIZE) || (self->sockid < 0)) {
|
|
/* invalid descriptor */
|
|
return 0;
|
|
}
|
|
#ifdef DO_NOT_SELECT_BEFORE_SEND
|
|
iRet = send(self->sockid, buffer, lLen, 0);
|
|
if (iRet != lLen) {
|
|
self->iType = 0;
|
|
if (iRet < 0) {
|
|
if (errno == EPIPE) { /* do not write "broken pipe" error */
|
|
return 0;
|
|
}
|
|
snprintf(buf, sizeof buf,
|
|
"NETWrite: send system error: %s (socket %d)",
|
|
strerror(errno), self->sockid);
|
|
} else {
|
|
snprintf(buf, sizeof buf,
|
|
"NETWrite: only %ld of %ld bytes sent (socket %d)", iRet,
|
|
lLen, self->sockid);
|
|
}
|
|
NetError(buf);
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
#endif
|
|
pos = buffer;
|
|
l = lLen;
|
|
FD_ZERO(&lMask);
|
|
disconnected = 0;
|
|
|
|
#define TIMEOUT 10
|
|
expire = time(NULL) + TIMEOUT;
|
|
while (l > 0) {
|
|
delta = expire - time(NULL);
|
|
if (delta <= 0)
|
|
break;
|
|
FD_SET(self->sockid, &lMask);
|
|
tmo.tv_usec = 0;
|
|
tmo.tv_sec = delta;
|
|
iRet = uselect((self->sockid + 1), NULL, &lMask, NULL, &tmo);
|
|
if (iRet < 0) {
|
|
/* failure, or no data */
|
|
self->iType = 0;
|
|
snprintf(buf, sizeof buf,
|
|
"NETWrite: failure on select before send: %s (socket %d)",
|
|
strerror(errno), self->sockid);
|
|
NetError(buf);
|
|
return 0;
|
|
}
|
|
if (!FD_ISSET(self->sockid, &lMask))
|
|
break;
|
|
|
|
iRet = send(self->sockid, pos, l, 0);
|
|
disconnected = (iRet == 0);
|
|
if (iRet < 0) {
|
|
self->iType = 0;
|
|
if (errno == EPIPE || errno == ECONNRESET) { /* do not write these errors */
|
|
return 0;
|
|
}
|
|
snprintf(buf, sizeof buf,
|
|
"NETWrite: send system error: %s (socket %d)",
|
|
strerror(errno), self->sockid);
|
|
NetError(buf);
|
|
return 0;
|
|
}
|
|
l -= iRet;
|
|
pos += iRet;
|
|
}
|
|
if (l > 0) {
|
|
self->iType = 0;
|
|
if (!disconnected) { /* do not write an error message on disconnect */
|
|
snprintf(buf, sizeof buf,
|
|
"NETWrite: timeout, only %ld of %ld bytes sent (socket %d) %ld.%6.6ld",
|
|
lLen - l, lLen, self->sockid, (long) tmo.tv_sec,
|
|
(long) tmo.tv_usec);
|
|
NetError(buf);
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------------*/
|
|
long NETRead(mkChannel * self, char *buffer, long lLen, long timeout)
|
|
{
|
|
fd_set lMask;
|
|
struct timeval tmo = { 0, 1 };
|
|
long iRet;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
|
|
if (timeout >= 0) {
|
|
/* setup for select first */
|
|
tmo.tv_usec = (timeout % 1000) * 1000;
|
|
tmo.tv_sec = timeout / 1000;
|
|
FD_ZERO(&lMask);
|
|
FD_SET(self->sockid, &lMask);
|
|
if ((self->sockid >= FD_SETSIZE) || (self->sockid < 0)) { /* invalid descriptor */
|
|
return -1; /* eof */
|
|
}
|
|
iRet = uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
|
|
if (iRet <= 0) {
|
|
/* failure, or no data
|
|
printf(" %d %d\n", iRet, errno);
|
|
*/
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* data or block for read, read */
|
|
memset(buffer, 0, lLen);
|
|
iRet = recv(self->sockid, buffer, lLen, 0);
|
|
if (iRet == 0) { /* eof */
|
|
return -1;
|
|
}
|
|
if (iRet < 0) {
|
|
return 0;
|
|
} else {
|
|
return iRet;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
int NETAvailable(mkChannel * self, long timeout)
|
|
{
|
|
fd_set lMask;
|
|
struct timeval tmo = { 0, 1 };
|
|
int iRet;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
|
|
/* setup for select */
|
|
tmo.tv_usec = (timeout % 1000) * 1000;
|
|
tmo.tv_sec = timeout / 1000;
|
|
FD_ZERO(&lMask);
|
|
FD_SET(self->sockid, &lMask);
|
|
if ((self->sockid >= FD_SETSIZE) || (self->sockid < 0))
|
|
/* invalid descriptor */
|
|
{
|
|
return -1; /* eof */
|
|
}
|
|
iRet = uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
|
|
if (iRet < 0) {
|
|
return -1;
|
|
}
|
|
if (FD_ISSET(self->sockid, &lMask)) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int NETReadTillTerm(mkChannel * self, long timeout,
|
|
char *pTerm, char *pBuffer, int iBufLen)
|
|
{
|
|
struct timeval start, now;
|
|
int bufPtr = 0, status, i, length, matchIndex = 1;
|
|
char c;
|
|
long dif;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return -1;
|
|
}
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
if (pTerm == NULL)
|
|
pTerm = strdup("");
|
|
|
|
length = strlen(pTerm);
|
|
memset(pBuffer, 0, iBufLen);
|
|
|
|
status = NETAvailable(self, timeout); /* first time: full timeout */
|
|
if (status <= 0) { /* return on error or on timeout */
|
|
return status;
|
|
}
|
|
while (status > 0) {
|
|
status = recv(self->sockid, &c, 1, 0);
|
|
if (status <= 0) {
|
|
return status;
|
|
}
|
|
if ('&' != pTerm[0]) {
|
|
/* This accepts any terminator */
|
|
for (i = 0; i < length; i++) {
|
|
if (c == pTerm[i]) {
|
|
return bufPtr + 1;
|
|
}
|
|
}
|
|
} else {
|
|
/* this is for accepting a terminator consisting of multiple characters, pTerm[0] is &
|
|
* There is evidence that this code is broken. M.K.
|
|
*/
|
|
if (matchIndex == 1 && c == pTerm[1]) {
|
|
matchIndex++;
|
|
} else {
|
|
if (c == pTerm[matchIndex] && matchIndex < length - 1) {
|
|
matchIndex++;
|
|
} else {
|
|
if (c == pTerm[matchIndex] && matchIndex == length - 1) {
|
|
bufPtr = bufPtr - matchIndex + 1;
|
|
pBuffer[bufPtr] = '\0';
|
|
return bufPtr;
|
|
} else {
|
|
matchIndex = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (c == 0 && *pTerm != 0) {
|
|
/* a null character is an error, except when no terminator is defined (binary data) */
|
|
return -1;
|
|
}
|
|
if (bufPtr >= iBufLen - 1) {
|
|
if (*pTerm != 0) { /* terminator expected */
|
|
return -1; /* overflow */
|
|
}
|
|
pBuffer[bufPtr] = c;
|
|
return bufPtr + 1;
|
|
}
|
|
pBuffer[bufPtr] = c;
|
|
bufPtr++;
|
|
/*
|
|
wait for more data
|
|
*/
|
|
status = NETAvailable(self, 0); /* timeout 0 (just poll) */
|
|
if (status == 0 && timeout > 0) {
|
|
gettimeofday(&now, NULL);
|
|
dif =
|
|
(now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec -
|
|
start.tv_usec) / 1000;
|
|
if (dif < 0)
|
|
dif += 24 * 3600 * 1000; /* treat midnight correctly */
|
|
if (dif > timeout) {
|
|
return 0; /* timeout */
|
|
}
|
|
status = NETAvailable(self, timeout - dif);
|
|
};
|
|
};
|
|
return bufPtr;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int NETReadRemob(mkChannel * self, long timeout, long timeout2,
|
|
char term, char *pBuffer, int iBufLen)
|
|
{
|
|
struct timeval start, now;
|
|
int bufPtr = 0, status, i, length, matchIndex = 1;
|
|
char c;
|
|
long dif;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return -1;
|
|
}
|
|
|
|
memset(pBuffer, 0, iBufLen);
|
|
|
|
status = NETAvailable(self, timeout);
|
|
while (status > 0) {
|
|
status = recv(self->sockid, &c, 1, 0);
|
|
if (status <= 0) {
|
|
return status;
|
|
}
|
|
if (c == term) {
|
|
return bufPtr + 1;
|
|
}
|
|
if (bufPtr >= iBufLen - 1) {
|
|
return -1; /* overflow */
|
|
}
|
|
pBuffer[bufPtr] = c;
|
|
bufPtr++;
|
|
/*
|
|
wait for more data
|
|
*/
|
|
status = NETAvailable(self, timeout2);
|
|
};
|
|
return status;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int NETClosePort(mkChannel * self)
|
|
{
|
|
int iRet;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
|
|
iRet = close(self->sockid);
|
|
gettimeofday(&lastclose, NULL);
|
|
self->iType = 0;
|
|
self->sockid = 0;
|
|
if (iRet < 0)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int NETReconnectWithFlags(mkChannel * self, int flags)
|
|
{
|
|
int iRet = 0;
|
|
int sock;
|
|
int oldopts;
|
|
|
|
/*
|
|
* Get the flags and close the old socket
|
|
*/
|
|
oldopts = fcntl(self->sockid, F_GETFL, 0);
|
|
if (self->sockid != 0) {
|
|
close(self->sockid);
|
|
}
|
|
/* Reopen and try to get it on the old fd */
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
/* If this isn't the same fd, try to move it over */
|
|
if (self->sockid != sock) {
|
|
/* Duplicate the new socket with the old fd if we can */
|
|
iRet = fcntl(sock, F_DUPFD, self->sockid);
|
|
if (iRet != self->sockid) {
|
|
/* If we didn't get the one we want, use original and close new */
|
|
self->sockid = sock;
|
|
close(iRet);
|
|
} else {
|
|
/* If we did get the one we want, close original and use old */
|
|
close(sock);
|
|
}
|
|
sock = self->sockid;
|
|
}
|
|
/* restore the old flags */
|
|
fcntl(self->sockid, F_SETFL, oldopts);
|
|
/* set socket non-blocking */
|
|
oldopts = fcntl(self->sockid, F_GETFL, 0);
|
|
if ( /*(flags & 1) && */ !(oldopts & O_NONBLOCK))
|
|
fcntl(self->sockid, F_SETFL, oldopts | O_NONBLOCK);
|
|
/* try to reconnect */
|
|
iRet = connect(self->sockid,
|
|
(struct sockaddr *) &(self->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
if (iRet < 0) {
|
|
if (errno == EINPROGRESS) {
|
|
if ((flags & 1)) {
|
|
iRet = 0; /* in progress */
|
|
} else {
|
|
fd_set rmask;
|
|
fd_set wmask;
|
|
struct timeval tmo = { 1, 0 };
|
|
|
|
FD_ZERO(&rmask);
|
|
FD_ZERO(&wmask);
|
|
FD_SET(self->sockid, &rmask);
|
|
FD_SET(self->sockid, &wmask);
|
|
iRet = uselect(self->sockid + 1, &rmask, &wmask, NULL, &tmo);
|
|
if (iRet < 0) /* error */
|
|
iRet = -1;
|
|
else if (iRet == 0) /* timeout */
|
|
iRet = 0; /* in progress */
|
|
else {
|
|
char reply[1];
|
|
if (FD_ISSET(self->sockid, &rmask)) {
|
|
iRet = recv(self->sockid, reply, 1, MSG_PEEK);
|
|
if (iRet <= 0)
|
|
iRet = -1; /* failure */
|
|
}
|
|
else if (FD_ISSET(self->sockid, &wmask)) {
|
|
iRet = send(self->sockid, NULL, 0, 0);
|
|
if (iRet < 0)
|
|
iRet = -1; /* failure */
|
|
else
|
|
iRet = 1; /* success */
|
|
}
|
|
}
|
|
}
|
|
} else /* other error */
|
|
iRet = -1; /* error */
|
|
} else
|
|
iRet = 1; /* success */
|
|
|
|
if (iRet != 0 && !(oldopts & O_NONBLOCK))
|
|
fcntl(self->sockid, F_SETFL, oldopts);
|
|
return iRet;
|
|
}
|
|
|
|
int NETReconnect(mkChannel * self)
|
|
{
|
|
return NETReconnectWithFlags(self, 0);
|
|
}
|
|
|
|
/* ################### UDP -functions ######################################*/
|
|
mkChannel *UDPOpen(int iPort)
|
|
{
|
|
mkChannel *pRes = NULL;
|
|
int iRet, i;
|
|
|
|
pRes = (mkChannel *) malloc(sizeof(mkChannel));
|
|
if (!pRes)
|
|
return NULL;
|
|
|
|
/* open a socket */
|
|
pRes->sockid = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (pRes->sockid < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
/* REUSEADDR for restarting ability */
|
|
i = 1;
|
|
setsockopt(pRes->sockid, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int));
|
|
|
|
/*
|
|
assert(pRes->sockid < (sizeof(long) * 8));
|
|
if this fails the masks for select will be to
|
|
short.
|
|
*/
|
|
|
|
|
|
/* bind */
|
|
memset(&(pRes->adresse), 0, sizeof(struct sockaddr_in));
|
|
pRes->adresse.sin_family = AF_INET;
|
|
pRes->adresse.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
pRes->adresse.sin_port = htons(iPort);
|
|
iRet = bind(pRes->sockid, (struct sockaddr *) &(pRes->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
if (iRet < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
pRes->iType = UDP;
|
|
pRes->lMagic = NETMAGIC;
|
|
return pRes;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
mkChannel *UDPConnect(char *name, int port)
|
|
{
|
|
mkChannel *pRes = NULL;
|
|
int iRet, i;
|
|
char pBueffel[80];
|
|
struct hostent *pServer = NULL;
|
|
|
|
assert(port > 0);
|
|
|
|
/* default name to localhost */
|
|
if (name == NULL) {
|
|
strcpy(pBueffel, "localhost");
|
|
name = pBueffel;
|
|
}
|
|
|
|
/* new channel */
|
|
pRes = (mkChannel *) malloc(sizeof(mkChannel));
|
|
if (!pRes)
|
|
return NULL;
|
|
|
|
/* connect */
|
|
iRet = CreateSocketAdress(&(pRes->adresse), name, port);
|
|
if (!iRet) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
pRes->sockid = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (pRes->sockid < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
/*
|
|
iRet = connect(pRes->sockid,(struct sockaddr *)&(pRes->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
*/
|
|
iRet = 1;
|
|
if (iRet < 0) {
|
|
free(pRes);
|
|
return NULL;
|
|
}
|
|
|
|
/* i = 1;
|
|
setsockopt(pRes->sockid,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
|
|
*/
|
|
pRes->iType = UDP;
|
|
pRes->lMagic = NETMAGIC;
|
|
return pRes;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
long UDPRead(mkChannel * self, char *buffer, long lLen, int timeout)
|
|
{
|
|
fd_set lMask;
|
|
struct timeval tmo = { 0, 1 };
|
|
long iRet;
|
|
socklen_t iLang;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
assert(self->iType == UDP);
|
|
|
|
if (timeout >= 0) {
|
|
/* setup for select first */
|
|
tmo.tv_usec = (timeout % 1000) * 1000;
|
|
tmo.tv_sec = timeout / 1000;
|
|
FD_ZERO(&lMask);
|
|
FD_SET(self->sockid, &lMask);
|
|
iRet =
|
|
uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
|
|
if (iRet <= 0) {
|
|
/* failure, or no data */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* data, read */
|
|
buffer[0] = '\0';
|
|
iLang = sizeof(struct sockaddr_in);
|
|
iRet = recvfrom(self->sockid, buffer, lLen, 0,
|
|
(struct sockaddr *) &(self->adresse), &iLang);
|
|
if (iRet == 0) { /* eof */
|
|
return -1;
|
|
}
|
|
if (iRet < 0) {
|
|
return 0;
|
|
} else {
|
|
buffer[iRet] = '\0';
|
|
return iRet;
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int UDPWrite(mkChannel * self, char *buffer, long lLen)
|
|
{
|
|
int iRet;
|
|
|
|
if (!VerifyChannel(self)) {
|
|
return 0;
|
|
}
|
|
assert(self->iType == UDP);
|
|
|
|
iRet = sendto(self->sockid, buffer, lLen, 0,
|
|
(struct sockaddr *) &(self->adresse),
|
|
sizeof(struct sockaddr_in));
|
|
fsync(self->sockid);
|
|
if (iRet < 0) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* ==========================================================================
|
|
|
|
TestCode. Compile with DBCLIENT defined to get a test client,
|
|
with DBSERVER defined to get a test server.
|
|
*/
|
|
|
|
#ifdef DBCLIENT
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char pBueffel[80];
|
|
mkChannel *pChan = NULL;
|
|
int iRet;
|
|
|
|
pChan = NETConnect("localhost", 4711);
|
|
if (!pChan) {
|
|
puts("No connection to server");
|
|
exit(2);
|
|
}
|
|
|
|
for (;;) {
|
|
printf("Client> ");
|
|
gets(pBueffel);
|
|
if (strcmp(pBueffel, "exit") == 0)
|
|
break;
|
|
iRet = NETWrite(pChan, pBueffel, strlen(pBueffel));
|
|
if (!iRet)
|
|
puts("Write error");
|
|
|
|
iRet = NETRead(pChan, pBueffel, 79, 2000);
|
|
if (iRet < -1) {
|
|
puts("Read error");
|
|
} else if (iRet > 0) {
|
|
pBueffel[iRet] = '\0';
|
|
puts(pBueffel);
|
|
}
|
|
}
|
|
NETClosePort(pChan);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DBSERVER
|
|
int main(int argc, char *argv[])
|
|
{
|
|
mkChannel *pPort = NULL;
|
|
mkChannel *pCon[20];
|
|
int i, iRet, iPtr = 0;
|
|
char pBueffel[132];
|
|
|
|
pPort = NETOpenPort(4711);
|
|
if (!pPort) {
|
|
puts("Cannot start server");
|
|
exit(1);
|
|
}
|
|
|
|
while (1) {
|
|
/* accept new connections */
|
|
pCon[iPtr] = NETAccept(pPort, 200);
|
|
if (pCon[iPtr]) {
|
|
printf("Connection accepted on socket %d\n", pCon[iPtr]->sockid);
|
|
iPtr++;
|
|
}
|
|
|
|
/* look for messages */
|
|
for (i = 0; i < iPtr; i++) {
|
|
if (pCon[i]) {
|
|
iRet = NETRead(pCon[i], pBueffel, 131, 200);
|
|
if (iRet < 0) {
|
|
printf("closing socket %d\n", pCon[i]->sockid);
|
|
NETClosePort(pCon[i]);
|
|
free(pCon[i]);
|
|
pCon[i] = NULL;
|
|
} else if (iRet > 0) {
|
|
pBueffel[iRet] = '\0';
|
|
printf("Received - %s - from %d\n", pBueffel, pCon[i]->sockid);
|
|
NETWrite(pCon[i], "Acknowledge", strlen("Acknowledge"));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
#endif
|