diff --git a/utils/Make.pmacterm b/utils/Make.pmacterm index 3044706..31f30a1 100644 --- a/utils/Make.pmacterm +++ b/utils/Make.pmacterm @@ -1,10 +1,10 @@ .SUFFIXES: .SUFFIXES: .c .o -OBJ=asynnet.o rwpuffer.o pmacterm.o +OBJ=asynnet.o rwpuffer.o pmacterm.o strlutil.o .c.o: - $(CC) -c -g $*.c + $(CC) -I. -c -g $*.c all:pmacterm diff --git a/utils/Make.sicslog b/utils/Make.sicslog index 07299c4..e24fbc1 100644 --- a/utils/Make.sicslog +++ b/utils/Make.sicslog @@ -1,11 +1,11 @@ .SUFFIXES: .SUFFIXES: .c .o -LIBROOT=/afs/psi.ch/project/sinq/sl6 +LIBROOT=/usr OBJ=../sicslogquery.o ../approxidate.o sicslogmain.o CFLAGS=-I$(LIBROOT)/include/libmongoc-1.0 -I../ -I../../sics -I$(LIBROOT)/include/libbson-1.0 -I. -LIBFLAGS=-L$(LIBROOT)/lib -lmongoc-1.0 -lbson-1.0 +LIBFLAGS=-L$(LIBROOT)/lib64 -lmongoc-1.0 -lbson-1.0 -lm .c.o: $(CC) $(CFLAGS) -c -g $*.c diff --git a/utils/asynnet.c b/utils/asynnet.c new file mode 100644 index 0000000..beac28c --- /dev/null +++ b/utils/asynnet.c @@ -0,0 +1,655 @@ +/** + * Asynchronous networking for SICS and other programs. This module centrally manages + * a number of network connections for a client program. It is a layer between the + * program and the network which manages non blocking network I/O. To this purpose, the + * client program has to call ANETprocess at convenient intervalls. This module + * has a couple of features: + * - Connections are abstracted to handles which are guranteed to be unique + * rather then socket numbers. Socket numbers may be reused by the OS. + * - This module allows upper level code to figure out if a connection is still + * connected or not. + * - This module introduces a buffer layer between the socket and the application. + * Thus the upper layer does not have to worry much about I/O blocking. This + * is taken care of by this module both for reading and writing. + * - All I/O is non blocking. + * - This module can detect if a client is hanging and close the connection then. + * Hanging is detected by not being able to write to the client for some period + * of time. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asynnet.h" +#include "rwpuffer.h" + +/*--------------------------------------------------------------------------*/ +#define SERVERSOCKET 0 +#define DATASOCKET 1 +#define MAXCONNECTIONS 1024 +#define RBUFFERSIZE 262144 /* 256kb */ +#define WBUFFERSIZE 20*262144 +/* #define WBUFFERSIZE 100*262144 */ +#define MAXWBUFFERSIZE 128*1000*1024 +/*--------------------------------------------------------------------------*/ +typedef struct { + int socket; + int handle; + int type; + prwBuffer readBuffer; + prwBuffer writeBuffer; + time_t lastOpenForWrite; + ANETcallback readCallback; + void *userData; + ANETkill killUser; + char host[132]; +} SocketDescriptor, *pSocketDescriptor; + +static SocketDescriptor connections[MAXCONNECTIONS]; +static int noConnections = 0; +static unsigned int handleID = 0; +/*------------------------------------------------------------------------*/ +static int SocketCompare(const void *s1, const void *s2) +{ + pSocketDescriptor socke1, socke2; + + socke1 = (pSocketDescriptor) s1; + socke2 = (pSocketDescriptor) s2; + return socke1->handle - socke2->handle; +} + +/*------------------------------------------------------------------------*/ +static void sortConnections() +{ + qsort(connections, noConnections, + sizeof(SocketDescriptor), SocketCompare); +} + +/*------------------------------------------------------------------------*/ +static pSocketDescriptor findSocketDescriptor(int handle) +{ + SocketDescriptor key; + pSocketDescriptor result; + + key.handle = handle; + result = bsearch(&key, connections, noConnections, + sizeof(SocketDescriptor), SocketCompare); + return result; +} + +/*------------------------------------------------------------------------*/ +static ANETlog logOutput = NULL; +static void *logUserData = NULL; +/*------------------------------------------------------------------------*/ +static void anetLog(int level, char *fmt, ...) +{ + va_list ap; + char buf[256]; + char *text = NULL; + int l; + + if (logOutput == NULL) { + return; + } + + va_start(ap, fmt); + l = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + if (l < sizeof buf) { + text = buf; + logOutput(level, text, logUserData); + } else { + /* assuming we have a C99 conforming snprintf and need a larger buffer */ + text = calloc(l, 1); + va_start(ap, fmt); + vsnprintf(text, l, fmt, ap); + va_end(ap); + logOutput(level, text, logUserData); + free(text); + } +} + +/*============= public interface =========================================*/ +void ANETsetLog(ANETlog lcb, void *userData) +{ + logOutput = lcb; + logUserData = userData; +} + +/*------------------------------------------------------------------------*/ +int ANETopenServerPort(int iPort, ANETcallback cb, void *userData) +{ + SocketDescriptor socke; + int i = 1, status; + struct sockaddr_in addresse; + + assert(iPort > 0); + assert(cb != NULL); + + memset(&socke, 0, sizeof(SocketDescriptor)); + socke.socket = socket(AF_INET, SOCK_STREAM, 0); + if (socke.socket < 0) { + anetLog(ANETERROR, "Failed to open server port: socket: %d", iPort); + return ANETOPENFAIL; + } + status = + setsockopt(socke.socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int)); + if (status < 0) { + anetLog(ANETERROR, + "Failed to open server port: setsockopt: %d, errno = %d", + iPort, errno); + return ANETOPENFAIL; + } + memset(&addresse, 0, sizeof(struct sockaddr_in)); + addresse.sin_family = AF_INET; + addresse.sin_addr.s_addr = htonl(INADDR_ANY); + addresse.sin_port = htons(iPort); + status = bind(socke.socket, (struct sockaddr *) &addresse, + sizeof(struct sockaddr_in)); + if (status < 0) { + anetLog(ANETERROR, "Failed to open server port: bind: %d, errno = %d", + iPort, errno); + return ANETOPENFAIL; + } + status = listen(socke.socket, 8); + if (status < 0) { + anetLog(ANETERROR, "Failed to open server port: listen: %d", iPort); + return ANETOPENFAIL; + } + socke.type = SERVERSOCKET; + socke.handle = handleID; + handleID++; + socke.readCallback = cb; + socke.userData = userData; + socke.lastOpenForWrite = time(NULL); + connections[noConnections] = socke; + noConnections++; + sortConnections(); + anetLog(ANETCON, "Opened server port %d", iPort); + return socke.handle; +} + +/*-----------------------------------------------------------------------*/ +int ANETregisterSocket(int socket) +{ + SocketDescriptor socke; + int flags, status; + + if (noConnections >= MAXCONNECTIONS) { + anetLog(ANETERROR, "Maximum number of connections exceeded"); + return ANETOUTOFSOCKETS; + } + + memset(&socke, 0, sizeof(SocketDescriptor)); + flags = fcntl(socket, F_GETFL, 0); + status = fcntl(socket, F_SETFL, flags | O_NONBLOCK); + if (status < 0) { + return ANETSOCKERROR; + } + flags =1; + setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char *) &flags, sizeof(int)); + socke.readBuffer = MakeRWPuffer(RBUFFERSIZE); + socke.writeBuffer = MakeBigRWPuffer(WBUFFERSIZE, MAXWBUFFERSIZE); + if (socke.readBuffer == NULL || socke.writeBuffer == NULL) { + return ANETMEM; + } + socke.socket = socket; + socke.handle = handleID; + handleID++; + socke.type = DATASOCKET; + socke.lastOpenForWrite = time(NULL); + connections[noConnections] = socke; + noConnections++; + sortConnections(); + return socke.handle; +} + +/*-------------------------------------------------------------------------*/ +int ANETconnect(char *name, int iPort) +{ + struct in_addr addr; + struct sockaddr_in addresse; + struct hostent *host; + int socke, status; + + memset(&addresse, 0, sizeof(struct sockaddr_in)); + addresse.sin_family = AF_INET; + addresse.sin_port = htons((unsigned short)(iPort &0xFFFF)); + host = gethostbyname(name); + if (host != NULL) { + memcpy((char *) &addr, + (char *)host->h_addr_list[0], (size_t)host->h_length); + } else { + /* check for aaa.bbbb.ccc.dddd */ + addr.s_addr = inet_addr(name); + if(addr.s_addr == (unsigned int) -1) { + anetLog(ANETERROR, "Failed to locate host: %s", name); + return ANETOPENFAIL; + } + } + addresse.sin_addr.s_addr = addr.s_addr; + socke = socket(AF_INET, SOCK_STREAM, 0); + status = connect(socke, (struct sockaddr *) &addresse, + sizeof(struct sockaddr_in)); + if (status < 0) { + close(socke); + anetLog(ANETERROR, "Failed to open socket to %s:%d", name, iPort); + return ANETOPENFAIL; + } + anetLog(ANETCON, "Opened socket %d to %s:%d", socke, name, iPort); + return ANETregisterSocket(socke); +} + +/*--------------------------------------------------------------------------*/ +void ANETclose(int handle) +{ + pSocketDescriptor socke = NULL; + + socke = findSocketDescriptor(handle); + if (socke == NULL) { + return; + } + close(socke->socket); + anetLog(ANETCON, "Closed socket %d", socke->socket); + if (socke->readBuffer != NULL) { + KillRWBuffer(socke->readBuffer); + } + if (socke->writeBuffer != NULL) { + KillRWBuffer(socke->writeBuffer); + } + if (socke->userData && socke->killUser) { + socke->killUser(socke->userData); + } + if (noConnections > 1) { + *socke = connections[noConnections - 1]; + noConnections--; + sortConnections(); + } else { + noConnections = 0; + } +} + +/*--------------------------------------------------------------------------*/ +static int anetWrite(SocketDescriptor con) +{ + int status, length; + void *pPtr; + + pPtr = GetRWBufferData(con.writeBuffer, &length); + if (length > 0) { + status = send(con.socket, pPtr, length, 0); + if (status < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + return 1; + } + ANETclose(con.handle); + return 0; + } + RemoveRWBufferData(con.writeBuffer, status); + } + return 1; +} +/*-------------------------------------------------------------------------- + * I have seen that select did not report the write possibility set, + * though the send buffer was empty. Thus I try to write if data is there + * and leave the lastOpenForWrite flag only when the socket would block + * on write. + *--------------------------------------------------------------------------*/ +static int anetTestWrite(SocketDescriptor con) +{ + int status, length; + void *pPtr; + time_t lastTime; + + if(con.type != DATASOCKET){ + return 1; + } + + lastTime = con.lastOpenForWrite; + con.lastOpenForWrite = time(NULL); + pPtr = GetRWBufferData(con.writeBuffer, &length); + if (length > 0) { + status = send(con.socket, pPtr, length, 0); + if (status < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + con.lastOpenForWrite = lastTime; + return 1; + } + ANETclose(con.handle); + return 0; + } + RemoveRWBufferData(con.writeBuffer, status); + } + return 1; +} +/*-------------------------------------------------------------------------- + There is something implicit in the return here: anetRead must return 0 + when the list of sockets changes. Otherwise ANETprocess cannot detect that it has + an invalid socket list and jump out. In all other cases anetRead must return 1 +-------------------------------------------------------------------------------*/ + +static int anetRead(SocketDescriptor con) +{ + int socke, handle, status; + unsigned int len; + struct sockaddr addresse; + char buffer[8192]; + + switch (con.type) { + case SERVERSOCKET: + len = sizeof(struct sockaddr); + socke = accept(con.socket, &addresse, &len); + if (socke < 0) { + return 1; + } + handle = ANETregisterSocket(socke); + if (handle > 0) { + status = con.readCallback(handle, con.userData); + if (status != 1) { + ANETclose(handle); + return 0; + } + } + anetLog(ANETCON, "Accepted socket %d on port %d, handle %d", + socke, con.socket, handle); + break; + case DATASOCKET: + memset(buffer, 0, 8192); + status = recv(con.socket, buffer, 8192, 0); + if (status < 0) { + if (errno == EAGAIN) { + return 1; + } + ANETclose(con.handle); + return 0; + } else if (status == 0) { + /* this means EOF */ + ANETclose(con.handle); + return 0; + } else { + status = StoreRWBuffer(con.readBuffer, buffer, status); + if (status != 1) { + anetLog(ANETERROR, "Read buffer overrun at handle %d, socket %d", + con.handle, con.socket); + } + if (con.readCallback != NULL) { + con.readCallback(con.handle, con.userData); + } + } + break; + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +void ANETprocess(void) +{ + int i, status, count = 0, socke = 0, length; + fd_set readMask, writeMask; + struct timeval tmo = { 0, 10000 }; + + FD_ZERO(&readMask); + FD_ZERO(&writeMask); + for (i = 0; i < noConnections; i++) { + socke = connections[i].socket; + FD_SET(socke, &readMask); + if (connections[i].writeBuffer != NULL) { + GetRWBufferData(connections[i].writeBuffer, &length); + if (length > 0) + FD_SET(socke, &writeMask); + else + connections[i].lastOpenForWrite = time(NULL); + } + if (socke > count) { + count = socke; + } + } + + count++; + status = select(count, &readMask, &writeMask, NULL, &tmo); + if (status < 0) { + if (errno == EINTR) { + return; + } + return; + } + + /** + * I always jump out of this loop when a socket is created or closed + * because then the order in the connections array is no longer valid. + * Try again the next time round. + */ + for (i = 0; i < noConnections; i++) { + socke = connections[i].socket; + if (FD_ISSET(socke, &readMask)) { + if (anetRead(connections[i]) == 0) { + return; + } + } + if (FD_ISSET(socke, &writeMask)) { + /* + * This has to be here, as I found out tracing a subtle bug: + * If the time is set in writeANET the modification will not + * propagate into the array as C copies the structure for the function call. + */ + connections[i].lastOpenForWrite = time(NULL); + if (anetWrite(connections[i]) == 0) { + return; + } + } else { + /* + * If I could not write to the socket for 5 minutes, + * the socket is considered broken and is closed. + */ + if (time(NULL) > connections[i].lastOpenForWrite + 300 && + connections[i].type == DATASOCKET) { + GetRWBufferData(connections[i].writeBuffer, &length); + anetLog(ANETCON, "Closing socket because of time overrun: %d, delay = %d, bytes to be written = %d, write bit = %d\n", connections[i].socket, + (int)(time(NULL) - connections[i].lastOpenForWrite), length, FD_ISSET(connections[i].socket, &writeMask) ); + ANETclose(connections[i].handle); + return; + } + } + } +} + +/*--------------------------------------------------------------------------*/ +int ANETvalidHandle(int handle) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con != NULL) { + return ANETOK; + } else { + return 0; + } +} + +/*---------------------------------------------------------------------------*/ +int ANETinfo(int handle, char *hostname, int hostnameLen) +{ + pSocketDescriptor con = NULL; + struct sockaddr_in sin; + struct hostent *host; + socklen_t len; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + if(strlen(con->host) < 3){ + len = sizeof sin; + if (getpeername(con->socket, (struct sockaddr *) &sin, &len) < 0) { + return ANETSOCKERROR; + } + if ((host = gethostbyaddr((char *) &sin.sin_addr, + sizeof(sin.sin_addr), AF_INET)) == NULL) { + return ANETSOCKERROR; + } + strlcpy(con->host,host->h_name, 132); + } + memset(hostname, 0, hostnameLen); + strlcpy(hostname, con->host, hostnameLen); + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int ANETcanWrite(int handle, void *buffer, int count) +{ + pSocketDescriptor con = NULL; + int status; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + ANETprocess(); + return CanStoreRWBuffer(con->writeBuffer, buffer, count); + } +} +/*---------------------------------------------------------------------------*/ +int ANETwrite(int handle, void *buffer, int count) +{ + pSocketDescriptor con = NULL; + int status; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + status = StoreRWBuffer(con->writeBuffer, buffer, count); + /* + first try if ANETprocess can write some and free the buffer + before giving up + */ + if (status != 1) { + ANETprocess(); + status = StoreRWBuffer(con->writeBuffer, buffer, count); + } + if (status != 1) { + anetLog(ANETERROR, "Write buffer overrun on handle %d, socket %d, trying to write %d bytes", + con->handle, con->socket, count); + return ANETWRITEBUFFERFULL; + } + } + return ANETOK; +} + +/*---------------------------------------------------------------------------*/ +int ANETread(int handle, void *buffer, int bufferLength) +{ + pSocketDescriptor con = NULL; + int status, length, len; + void *data = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } else { + data = GetRWBufferData(con->readBuffer, &length); + if (length == 0) { + len = 0; + } else if (length >= bufferLength) { + len = bufferLength; + } else { + len = length; + } + if (len > 0) { + memcpy(buffer, data, len); + } + } + return len; +} + +/*---------------------------------------------------------------------------*/ +void *ANETreadPtr(int handle, int *length) +{ + pSocketDescriptor con = NULL; + void *data = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + *length = 0; + return NULL; + } else { + data = GetRWBufferData(con->readBuffer, length); + return data; + } +} + +/*---------------------------------------------------------------------------*/ +void ANETreadConsume(int handle, int count) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return; + } else { + RemoveRWBufferData(con->readBuffer, count); + } +} + +/*---------------------------------------------------------------------------*/ +void ANETsetReadCallback(int handle, ANETcallback cb, + void *userData, ANETkill killUser) +{ + pSocketDescriptor con = NULL; + + con = findSocketDescriptor(handle); + if (con == NULL) { + return; + } else { + con->readCallback = cb; + con->userData = userData; + con->killUser = killUser; + } +} + +/*----------------------------------------------------------------------------*/ +int ANETreadTillTerm(int handle, + ANETtermCallback tcb, void *termData, + ANETwait wcb, void *waitData, char **buffer) +{ + pSocketDescriptor con = NULL; + char *data; + int length, status; + + while (wcb(waitData) > 0) { + ANETprocess(); + con = findSocketDescriptor(handle); + if (con == NULL) { + return ANETDISCONNECTED; + } + data = GetRWBufferData(con->readBuffer, &length); + if (length > 0) { + status = tcb(data, length, termData); + if (status > 0) { + *buffer = malloc(status * sizeof(char)); + if (*buffer != NULL) { + memcpy(*buffer, data, status); + } + RemoveRWBufferData(con->readBuffer, status); + return ANETOK; + } + } + } + return ANETTIMEOUT; +} diff --git a/utils/asynnet.h b/utils/asynnet.h new file mode 100644 index 0000000..ae2f114 --- /dev/null +++ b/utils/asynnet.h @@ -0,0 +1,218 @@ +/** + * Asynchronous networking for SICS and other programs. This module centrally manages + * a number of network connections for a client program. It is a layer between the + * program and the network which manages non blocking network I/O. To this purpose, the + * client program has to call ANETprocess at convenient intervals. This module + * has a couple of features: + * - Connections are abstracted to handles which are guaranteed to be unique + * rather then socket numbers. Socket numbers may be reused by the OS. + * - This module allows upper level code to figure out if a connection is still + * connected or not. + * - This module introduces a buffer layer between the socket and the application. + * Thus the upper layer does not have to worry much about I/O blocking. This + * is taken care of by this module both for reading and writing. + * - All I/O is non blocking. + * - This module can detect if a client is hanging and close the connection then. + * Hanging is detected by not being able to write to the client for some period + * of time. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + */ +#ifndef ASYNNET_H_ +#define ASYNNET_H_ +/*=================== error codes ========================================*/ +#define ANETOK 1 +#define ANETDISCONNECTED -10000 +#define ANETWRITEBUFFERFULL -10001 +#define ANETTIMEOUT -10002 +#define ANETOPENFAIL -10003 +#define ANETSOCKERROR -10004 +#define ANETMEM -10005 +#define ANETOUTOFSOCKETS -10006 +/*================== log levels ==========================================*/ +#define ANETNONE 0 +#define ANETERROR 1 +#define ANETIO 2 +#define ANETCON 3 +/*================== callback functions ==================================*/ +/** + * \brief Callback called when a connection has been accepted on a + * port or data is ready. + * \param handle The handle of the new network connection + * \return 1 if the new connection can be accepted or 0 if not. + * Same for data. + */ +typedef int (*ANETcallback) (int handle, void *userData); +/** + * \brief a callback which is called in order to determine if a + * a terminator is present in the data. + * \param data The data to inspect + * \param length The length of the data to inspect + * \param userData An opaque pointer passed through to this + * function + * \return -1 when no terminator is in the data, an integer pointing + * to after the terminator in data (in bytes). + */ +typedef int (*ANETtermCallback) (void *data, int length, void *userData); +/** + * \brief a callback function for waiting on some event. + * This is typically called to do something else while waiting + * for data to arrive. It can also return a negative return value, + * which will effectively implement a timeout. + * \param userData An opaque pointer passed through to this callback + * \return 1 to continue waiting, -1 for a timeout. + */ +typedef int (*ANETwait) (void *userData); +/** + * \brief callback to log events in ANET + * \param level The level of the logging message + * \param txt The logging data + * \param userData An opaque pointer passed through to ANETlog + */ +typedef void (*ANETlog) (int level, char *txt, void *userData); +/** + * \brief a callback for killing userdata associated with a read callback. + * This is called in ANETclose, if defined. + * \param userData The user data to kill. + */ +typedef void (*ANETkill) (void *userData); +/*===================== open/close functions =============================*/ +/** + * * \brief open a server port + * \param iPort The port number at which to listen for + * connections. + * \param cb A callback which will be called whenever a new connection + * has been accepted on this port. + * \prama userData An opaque pointer to be passed as an argument to the + * callback function. + * \return A handle for the server port or a negative error code. + */ +int ANETopenServerPort(int iPort, ANETcallback cb, void *userData); +/** + * \brief open a client connection to a server. + * \param name the computer name of the server + * \param iPort The port number at which the server is listening + * \return A handle to the open port or a negative error code. + */ +int ANETconnect(char *name, int iPort); +/** + * \brief register a socket to be managed by this module. The socket + * may have been obtained by any means. + * \param socket The file descriptor of the socket + * \return A handle to use for this socket later on. + */ +int ANETregisterSocket(int socket); +/** + * \brief close a connection + * \param handle The handle of the connection + */ +void ANETclose(int handle); +/** + * \brief This function drives I/O processing, i.e. reading and writing. + * This has to be called by the client of this module at regular intervalls. + */ +void ANETprocess(void); +/** + * \brief tests if a handle is still a valid connection + * \param handle The handle to test. + * \return 1 if this is still a connected socket, 0 else. + */ +int ANETvalidHandle(int handle); +/** + * \brief figure out to which host we are connected. + * \param handle The connection handle + * \param hostname a buffer to copy the hostname into + * \param hostNameLen the length of the hostname buffer + * \return 1 on success, a negative error code else. + */ +int ANETinfo(int handle, char *hostname, int hostNameLen); +/*=================== I/O functions =========================================== + * For reading there are possibilities: + * - Raw reading happens through the combination of + * ANETread and ANETreadConsume. + * - Another way for raw reading is to register a read callback which is + * called anytime new data arrives. + * - The usual case is to wait for a line of terminated command input. This is + * done through ANETreadTillterm. + * ==========================================================================*/ +/** + * \brief write to the network + * \param handle The handle for the connection + * \param buffer A pointer to the data to write + * \param count The number of bytes to write. + * \return 1 on success, 0 on failure + */ +int ANETwrite(int handle, void *buffer, int count); +/** + * \brief Test if the buffer can be written to the network + * \param handle The handle for the connection + * \param buffer A pointer to the data to write + * \param count The number of bytes to write. + * \return 1 when possible, 0 when buffer overrun + */ +int ANETcanWrite(int handle, void *buffer, int count); +/** + * \brief copy at max bufferLength bytes into buffer. The data is not deleted from + * the read buffer yet. + * \param handle The handle of the connection to read from + * \param buffer a pointer to an area for holding the data + * \param bufferLength The maximum number of bytes which can be copied into + * the buffer. + * \return The number of bytes copied. Can be 0 if no data is available. On + * errors a negative error code is returned. + */ +int ANETread(int handle, void *buffer, int bufferLength); +/** + * \brief Get a pointer to the data which has been read up to now. + * Do not mess with the data!! Else the result may be badly defined! + * \param handle The handle for the connection + * \param length will be set to the length of the data read so far. + * \return NULL when the socket is disconnected, a pointer else. + */ +void *ANETreadPtr(int handle, int *length); +/** + * \brief remove count bytes from the read buffer. + * \param handle The handle for the connection. + * \param count The number of bytes which can be removed from the + * read buffer. + */ +void ANETreadConsume(int handle, int count); +/** + * \brief set a callback to be called when data is available at the port. + * \param handle The handle of the connection + * \param cb The callback function to call + * \param userData An opaque pointer passed on as a parameter to the + * callback function. + */ +void ANETsetReadCallback(int handle, ANETcallback cb, void *userData, + ANETkill killUser); +/** + * \brief wait for terminated data to arrive. + * \param handle The connection handle to read from + * \param tcb a callback function which determines if a terminator is in the + * data. + * \param termData An opaque data pointer passed on to tcb + * \param wcb A callback function called while waiting for data + * to arrive. + * \param waitData An opaque pointer passed on to wcb + * \param buffer A newly allocated buffer holding the data as read + * including the terminator. + * \return 1 on success, a negative error code else. + */ +int ANETreadTillTerm(int handle, + ANETtermCallback tcb, void *termData, + ANETwait wcb, void *waitData, char **buffer); +/** + * Note to Markus: suitable callbacks for the standard case: waiting for \n and + * TaskYield for waiting to a timeout will become part of nread.h, .c. + */ +/*========================== system ====================================*/ +/** + * \brief install a logging function + * \param lcb The logging function to install + * \param userData An opaque pointer with data for lcb + */ +void ANETsetLog(ANETlog lcb, void *userData); +#endif /*ASYNNET_H_ */ diff --git a/utils/makefile_linux b/utils/makefile_linux index 986b07e..9ab6e6e 100644 --- a/utils/makefile_linux +++ b/utils/makefile_linux @@ -7,7 +7,7 @@ # Mark Koennecke, December 2009 #-------------------------------------------------------------------------- -include ../../linux_def +include ../../sics/linux_def CC = gcc CFLAGS = -g -DLINUX $(DFORTIFY) -I../hardsup -I. diff --git a/utils/rwpuffer.c b/utils/rwpuffer.c new file mode 100644 index 0000000..68acf5e --- /dev/null +++ b/utils/rwpuffer.c @@ -0,0 +1,136 @@ +/** + * This is a buffer to store bytes for reading and writing. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + * + * added resizing option and MakeBigRWPuffer in order to support transfer + * of large amounts of image data on few connections + * + * Mark Koennecke, August 2014 + */ +#include +#include +#include +#include "rwpuffer.h" +/*----------------------------------------------------------------------*/ +typedef struct __RWBuffer { + char *data; + int length; + int startPtr; + int endPtr; + int maxSize; +} RWBuffer; +/*----------------------------------------------------------------------*/ +prwBuffer MakeRWPuffer(int size) +{ + prwBuffer self = NULL; + + self = malloc(sizeof(RWBuffer)); + if (self == NULL) { + return NULL; + } + self->data = calloc(size, sizeof(char)); + if (self->data == NULL) { + return NULL; + } + self->length = size; + self->startPtr = 0; + self->endPtr = 0; + self->maxSize = size; + return self; +} +/*------------------------------------------------------------------------*/ +prwBuffer MakeBigRWPuffer(int size, int maxSize) +{ + prwBuffer result = MakeRWPuffer(size); + if(result != NULL){ + result->maxSize = maxSize; + } + return result; +} +/*------------------------------------------------------------------------*/ +void KillRWBuffer(prwBuffer self) +{ + if (self == NULL) { + return; + } + if (self->data != NULL) { + free(self->data); + } + free(self); +} +/*------------------------------------------------------------------------*/ +int CanStoreRWBuffer(prwBuffer self, void *data, int count) +{ + int length; + char *ptr; + + length = self->endPtr - self->startPtr; + if (count + length >= self->length ) { + if(self->length < self->maxSize){ + ptr = calloc(self->maxSize,sizeof(char)); + if(ptr == NULL) { + return 0; + } + memcpy(ptr,self->data, length*sizeof(char)); + free(self->data); + self->data = ptr; + self->length = self->maxSize; + } else { + return 0; + } + } + return 1; +} +/*------------------------------------------------------------------------*/ +int StoreRWBuffer(prwBuffer self, void *data, int count) +{ + int length; + char *ptr; + + length = self->endPtr - self->startPtr; + if (count + length >= self->length ) { + if(self->length < self->maxSize){ + ptr = calloc(self->maxSize,sizeof(char)); + if(ptr == NULL) { + printf("HELP: RWBuffer overrun!!!!\n"); + return 0; + } + memcpy(ptr,self->data, length*sizeof(char)); + free(self->data); + self->data = ptr; + self->length = self->maxSize; + } else { + printf("HELP: RWBuffer overrun!!!!\n"); + return 0; + } + } + if (count + self->endPtr > self->length) { + memmove(self->data, self->data + self->startPtr, length); + self->startPtr = 0; + self->endPtr = length; + } + memcpy(self->data + self->endPtr, data, count); + self->endPtr += count; + return 1; +} + +/*------------------------------------------------------------------------*/ +void *GetRWBufferData(prwBuffer self, int *length) +{ + *length = self->endPtr - self->startPtr; + return (void *) self->data + self->startPtr; +} + +/*-------------------------------------------------------------------------*/ +void RemoveRWBufferData(prwBuffer self, int count) +{ + self->startPtr += count; + if (self->startPtr >= self->endPtr) { + self->startPtr = 0; + self->endPtr = 0; + memset(self->data,0,self->length*sizeof(char)); + } +} diff --git a/utils/rwpuffer.h b/utils/rwpuffer.h new file mode 100644 index 0000000..59ec439 --- /dev/null +++ b/utils/rwpuffer.h @@ -0,0 +1,61 @@ +/** + * This is a buffer to store bytes for reading and writing. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2009 + */ +#ifndef RWPUFFER_H_ +#define RWPUFFER_H_ + +typedef struct __RWBuffer *prwBuffer; + +/** + * \brief create a RW buffer. + * \param size The size of the buffer. + * \return NULL on success, else a pointer to t a new rwPuffer + */ +prwBuffer MakeRWPuffer(int size); +/** + * \brief create a RW buffer which can grow. + * \param size The size of the buffer. + * \param maxSize The maximum size of the buffer. + * \return NULL on success, else a pointer to t a new rwPuffer + */ +prwBuffer MakeBigRWPuffer(int size, int maxSize); +/** + * \brief delete a rw buffer. + * \param self The rwPuffer to delete. + */ +void KillRWBuffer(prwBuffer self); +/** + * \brief store some data in the RW buffer + * \param self The rw buffer to store the data in + * \param data pointer to the data to store + * \param count The number of bytes to store + * \return 1 on success, 0 on failure + */ +int StoreRWBuffer(prwBuffer self, void *data, int count); +/** + * \brief Test if the data can be stored in the rwBuffer + * \param self The rw buffer to store the data in + * \param data pointer to the data to store + * \param count The number of bytes to store + * \return 1 when OK, 0 when buffer full + */ +int CanStoreRWBuffer(prwBuffer self, void *data, int count); +/** + * \brief Get a pointer to the current buffer data + * \param self the buffer to get the data from + * \param length Will be set to the number of available bytes. + * \return A pointer to the data + */ +void *GetRWBufferData(prwBuffer self, int *length); +/** + * \brief remove data from the buffer + * \param self the buffer to remove data from + * \param count The number of bytes to remove + */ +void RemoveRWBufferData(prwBuffer self, int count); + +#endif /*RWPUFFER_H_ */ diff --git a/utils/strlutil.c b/utils/strlutil.c new file mode 100644 index 0000000..53d3bed --- /dev/null +++ b/utils/strlutil.c @@ -0,0 +1,112 @@ +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* only compile if no system supplied version */ + +#ifndef strlcpy + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +#endif diff --git a/utils/strlutil.h b/utils/strlutil.h new file mode 100644 index 0000000..7a60393 --- /dev/null +++ b/utils/strlutil.h @@ -0,0 +1,18 @@ +/* + * strlutil.h + * Provide strcpy, strlcat when not provided by ssystem + * + * Created on: Mar 25, 2010 + * Author: koennecke + */ + +#ifndef STRLUTIL_H_ +#define STRLUTIL_H_ + +#ifndef strlcpy +size_t strlcat(char *dst, const char *src, size_t siz); +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + + +#endif /* STRLUTIL_H_ */