# HG changeset patch # User Medo # Date 1338483280 -7200 # Node ID c42949cfdd929595b8fd2d24a011f471ebf8dfdc # Parent a0573014ff4fe4ee85a663b57eaff15b8080ad3c Moved frontlib into project_files diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/frontlib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/frontlib.c Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,65 @@ +#include "frontlib.h" +#include "logging.h" +#include "nonblocksockets.h" +#include "ipcconn.h" + +#include +#include +#include +#include +#include + +static int flib_initflags; + +int flib_init(int flags) { + flib_initflags = flags; + + if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { + if(SDL_Init(0)==-1) { + flib_log_e("Error in SDL_Init: %s", SDL_GetError()); + return -1; + } + } + + if(SDLNet_Init()==-1) { + flib_log_e("Error in SDLNet_Init: %s", SDLNet_GetError()); + if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { + SDL_Quit(); + } + return -1; + } + + flib_ipcconn_init(); + return 0; +} + +void flib_quit() { + flib_ipcconn_quit(); + + SDLNet_Quit(); + if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { + SDL_Quit(); + } +} + +int main(int argc, char *argv[]) { + flib_init(0); + int port = flib_ipcconn_listen(); + printf("%i\n", port); + fflush(stdout); + char data[256]; + while(flib_ipcconn_state() != IPC_NOT_CONNECTED) { + flib_ipcconn_tick(); + int size = flib_ipcconn_recv_message(data); + if(size>0) { + data[size]=0; + flib_log_i("IPC IN: %s", data); + if(data[0]=='?') { + flib_log_i("IPC OUT: !"); + flib_ipcconn_send_message("!", 1); + } + } + } + flib_log_i("IPC connection lost."); + return 0; +} diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/frontlib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/frontlib.h Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,31 @@ +/* + * Public header file for the hedgewars frontent networking library. + * + * This is the only header you should need to include from frontend code. + */ + +#ifndef FRONTLIB_H_ +#define FRONTLIB_H_ + +#define FRONTLIB_SDL_ALREADY_INITIALIZED 1 + +/** + * Call this function before anything else in this library. + * + * If the calling program uses SDL, it needs to call SDL_Init before initializing + * this library and then pass FRONTLIB_SDL_ALREADY_INITIALIZED as flag to this function. + * + * Otherwise, pass 0 to let this library handle SDL_Init an SDL_Quit itself. + * + * Returns 0 on success, -1 on error. + */ +int flib_init(int flags); + +/** + * Free resources associated with the library. Call this function once + * the library is no longer needed. You can re-initialize the library by calling + * flib_init again. + */ +void flib_quit(); + +#endif /* FRONTLIB_H_ */ diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/ipcconn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipcconn.c Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,134 @@ +#include "ipcconn.h" +#include "logging.h" +#include "nonblocksockets.h" + +#include +#include +static TCPsocket ipcListenSocket; +static NonBlockSocket ipcConnSocket; + +static uint8_t ipcReadBuffer[256]; +static int ipcReadBufferSize; + +void flib_ipcconn_init() { + ipcListenSocket = NULL; + ipcConnSocket = NULL; + ipcReadBufferSize = 0; +} + +void flib_ipcconn_quit() { + flib_ipcconn_close(); +} + +int flib_ipcconn_listen() { + if(ipcListenSocket || ipcConnSocket) { + flib_log_e("flib_ipcconn_listen: Already listening or connected."); + return -1; + } + IPaddress addr; + addr.host = INADDR_ANY; + + /* SDL_net does not seem to have a way to listen on a random unused port + and find out which port that is, so let's try to find one ourselves. */ + // TODO: Is socket binding fail-fast on all platforms? + srand(time(NULL)); + rand(); + for(int i=0; i<1000; i++) { + // IANA suggests using ports in the range 49152-65535 for things like this + int ipcPort = 49152+(rand()%(65535-49152)); + SDLNet_Write16(ipcPort, &addr.port); + ipcListenSocket = SDLNet_TCP_Open(&addr); + if(!ipcListenSocket) { + flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError()); + } else { + flib_log_i("Listening for IPC connections on port %i.", ipcPort); + return ipcPort; + } + } + flib_log_e("Unable to find a free port for IPC."); + return -1; +} + +void flib_ipcconn_close() { + if(ipcListenSocket) { + SDLNet_TCP_Close(ipcListenSocket); + ipcListenSocket = NULL; + } + flib_nbsocket_close(&ipcConnSocket); + ipcReadBufferSize = 0; +} + +IpcConnState flib_ipcconn_state() { + if(ipcConnSocket) { + return IPC_CONNECTED; + } else if(ipcListenSocket) { + return IPC_LISTENING; + } else { + return IPC_NOT_CONNECTED; + } +} + +/** + * Receive a single message and copy it into the data buffer. + * Returns the length of the received message, -1 when nothing is received. + */ +int flib_ipcconn_recv_message(void *data) { + flib_ipcconn_tick(); + + if(ipcConnSocket) { + int size = flib_nbsocket_recv(ipcConnSocket, ipcReadBuffer+ipcReadBufferSize, sizeof(ipcReadBuffer)-ipcReadBufferSize); + if(size>=0) { + ipcReadBufferSize += size; + } else { + flib_nbsocket_close(&ipcConnSocket); + } + } + + int msgsize = ipcReadBuffer[0]; + if(ipcReadBufferSize > msgsize) { + memcpy(data, ipcReadBuffer+1, msgsize); + memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1)); + ipcReadBufferSize -= (msgsize+1); + return msgsize; + } else if(!ipcConnSocket && ipcReadBufferSize>0) { + flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipcReadBufferSize-1, msgsize); + ipcReadBufferSize = 0; + return -1; + } else { + return -1; + } +} + +int flib_ipcconn_send_message(void *data, size_t len) { + flib_ipcconn_tick(); + + if(!ipcConnSocket) { + flib_log_w("flib_ipcconn_send_message: Not connected."); + return -1; + } + if(len>255) { + flib_log_e("Attempt to send too much data to the engine in a single message."); + return -1; + } + + uint8_t sendbuf[256]; + sendbuf[0] = len; + memcpy(sendbuf+1, data, len); + if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) < len+1) { + flib_log_w("Failed or incomplete ICP write: engine connection lost."); + flib_nbsocket_close(&ipcConnSocket); + return -1; + } else { + return 0; + } +} + +void flib_ipcconn_tick() { + if(!ipcConnSocket && ipcListenSocket) { + ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true); + if(ipcConnSocket) { + SDLNet_TCP_Close(ipcListenSocket); + ipcListenSocket = NULL; + } + } +} diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/ipcconn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipcconn.h Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,67 @@ +/* + * Low-level protocol support for the IPC connection to the engine. + */ + +#ifndef IPCCONN_H_ +#define IPCCONN_H_ + +#include + +typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState; + +/** + * Called by flib_init(). Initialize everything related to ipc. + */ +void flib_ipcconn_init(); + +/** + * Called by flib_quit(). Free resources and shut down. + */ +void flib_ipcconn_quit(); + +/** + * Start listening for a connection from the engine. The system has to be in state + * IPC_NOT_CONNECTED when calling this function. + * + * Returns the port we started listening on, or a negative value if there is an error. + * + * We stop listening once a connection has been established, so if you want to start + * the engine again and talk to it you need to call this function again after the old + * connection is closed. + */ +int flib_ipcconn_listen(); + +/** + * Close the current IPC connection and/or stop listening for an incoming one. + * This also discards all unread messages. + */ +void flib_ipcconn_close(); + +/** + * Determine the current connection state + */ +IpcConnState flib_ipcconn_state(); + +/** + * Receive a single message (up to 255 bytes) and copy it into the data buffer. + * Returns the length of the received message, a negative value if no message could + * be read. + */ +int flib_ipcconn_recv_message(void *data); + +/** + * Write a single message (up to 255 bytes) to the engine. This call blocks until the + * message is completely written or the connection is closed or an error occurs. + * + * Calling this function in a state other than IPC_CONNECTED will fail immediately. + * Returns a negative value on failure. + */ +int flib_ipcconn_send_message(void *data, size_t len); + +/** + * Call regularly to allow background work to proceed + */ +void flib_ipcconn_tick(); + +#endif /* IPCCONN_H_ */ + diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/logging.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/logging.c Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,51 @@ +#include "logging.h" + +#include +#include +#include + +char* flib_format_ip(uint32_t numip) { + static char ip[16]; + snprintf(ip, 16, "%u.%u.%u.%u", numip>>24, (numip>>16)&0xff, (numip>>8)&0xff, numip&0xff); + return ip; +} + +static void log_time(FILE *file) { + time_t timer; + char buffer[25]; + struct tm* tm_info; + + time(&timer); + tm_info = localtime(&timer); + + strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info); + fprintf(file, "%s", buffer); +} + +static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) { + log_time(file); + fprintf(file, " [%s]", prefix); + vfprintf(file, fmt, args); + fprintf(file, "\n"); +} + +void flib_log_e(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + flib_vflog(stderr, "E", fmt, argp); + va_end(argp); +} + +void flib_log_w(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + flib_vflog(stdout, "W", fmt, argp); + va_end(argp); +} + +void flib_log_i(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + flib_vflog(stdout, "I", fmt, argp); + va_end(argp); +} diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/logging.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/logging.h Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,16 @@ +/* + * + */ + +#ifndef LOGGING_H_ +#define LOGGING_H_ + +#include + +char* flib_format_ip(uint32_t numip); + +void flib_log_e(const char *fmt, ...); +void flib_log_w(const char *fmt, ...); +void flib_log_i(const char *fmt, ...); + +#endif /* LOGGING_H_ */ diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/nonblocksockets.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/nonblocksockets.c Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,76 @@ +#include "nonblocksockets.h" +#include "logging.h" +#include + +static uint32_t get_peer_ip(TCPsocket sock) { + IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); + return SDLNet_Read32(&addr->host); +} + +static bool connection_is_local(TCPsocket sock) { + return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 +} + +void flib_nbsocket_close(NonBlockSocket *nbsockptr) { + NonBlockSocket nbsock = *nbsockptr; + if(nbsock!=NULL) { + SDLNet_DelSocket(nbsock->sockset, (SDLNet_GenericSocket)nbsock->sock); + SDLNet_TCP_Close(nbsock->sock); + SDLNet_FreeSocketSet(nbsock->sockset); + } + free(nbsock); + *nbsockptr = NULL; +} + +NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly) { + NonBlockSocket result = NULL; + if(!listensocket) { + flib_log_e("Attempt to accept a connection on a NULL socket."); + return NULL; + } + while(result==NULL) { + TCPsocket sock = SDLNet_TCP_Accept(listensocket); + if(!sock) { + // No incoming connections + return NULL; + } + if(localOnly && !connection_is_local(sock)) { + flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock))); + SDLNet_TCP_Close(sock); + } else { + result = malloc(sizeof(_NonBlockSocket)); + if(result==NULL) { + flib_log_e("Out of memory!"); + SDLNet_TCP_Close(sock); + return NULL; + } + result->sock = sock; + result->sockset = SDLNet_AllocSocketSet(1); + if(result->sockset==NULL) { + flib_log_e("Out of memory!"); + SDLNet_TCP_Close(sock); + free(result); + return NULL; + } + SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); + } + } + return result; +} + +int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen) { + if(!sock) { + flib_log_e("Attempt to receive on a NULL socket."); + return -1; + } + int readySockets = SDLNet_CheckSockets(sock->sockset, 0); + if(readySockets>0) { + int size = SDLNet_TCP_Recv(sock->sock, data, maxlen); + return size>0 ? size : -1; + } else if(readySockets==0) { + return 0; + } else { + flib_log_e("Error in select system call: %s", SDLNet_GetError()); + return -1; + } +} diff -r a0573014ff4f -r c42949cfdd92 project_files/frontlib/nonblocksockets.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/nonblocksockets.h Thu May 31 18:54:40 2012 +0200 @@ -0,0 +1,51 @@ +/* + * nonblocksockets.h + * + * Created on: 31.05.2012 + * Author: simmax + */ + +#ifndef NONBLOCKSOCKETS_H_ +#define NONBLOCKSOCKETS_H_ + +#include +#include + +typedef struct { + TCPsocket sock; + SDLNet_SocketSet sockset; +} _NonBlockSocket; + +typedef _NonBlockSocket *NonBlockSocket; + +/** + * Close the indicated socket, free its memory and set it to NULL. + * If the socket is already NULL, nothing happens. + */ +void flib_nbsocket_close(NonBlockSocket *socket); + +/** + * Try to accept a connection from a listening socket. + * if localOnly is true, this will only accept connections which came from 127.0.0.1 + * Returns NULL if nothing can be accepted. + */ +NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly); + +/** + * Attempt to receive up to maxlen bytes from the socket, but does not + * block if nothing is available. + * Returns the ammount of data received, 0 if there was nothing to receive, + * or a negative number if the connection was closed or an error occurred. + */ +int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen); + +/** + * We can't do a nonblocking send over SDL_net, so this function just forwards + * to SDLNet_TCP_Send for convenience, which blocks until all data is sent or an + * error occurs. The ammount of data actually sent is returned, negative value on error. + */ +static inline int flib_nbsocket_blocksend(NonBlockSocket sock, void *data, int len) { + return SDLNet_TCP_Send(sock->sock, data, len); +} + +#endif /* NONBLOCKSOCKETS_H_ */