diff --git a/dump1090.h b/dump1090.h index fc81eb0..f23527d 100644 --- a/dump1090.h +++ b/dump1090.h @@ -82,7 +82,8 @@ #include "winstubs.h" //Put everything Windows specific in here #endif -#include +// Avoid a dependency on rtl-sdr except where it's really needed. +typedef struct rtlsdr_dev rtlsdr_dev_t; // ============================= #defines =============================== @@ -212,6 +213,7 @@ #include "util.h" #include "anet.h" +#include "net_io.h" #include "crc.h" #include "demod_2000.h" #include "demod_2400.h" @@ -222,24 +224,6 @@ //======================== structure declarations ========================= -// Structure used to describe a networking client -struct client { - struct client* next; // Pointer to next client - int fd; // File descriptor - int service; // TCP port the client is connected to - int buflen; // Amount of data on buffer - char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer -}; - -// Common writer state for all output sockets of one type -struct net_writer { - int socket; // listening socket FD, used to identify the owning service - int connections; // number of active clients - void *data; // shared write buffer, sized MODES_OUT_BUF_SIZE - int dataUsed; // number of bytes of write buffer currently used - uint64_t lastWrite; // time of last write to clients -}; - // Structure representing one magnitude buffer struct mag_buf { uint16_t *data; // Magnitude data. Starts with Modes.trailing_samples worth of overlap from the previous block @@ -287,10 +271,8 @@ struct { // Internal state // Networking char aneterr[ANET_ERR_LEN]; + struct net_service *services; // Active services struct client *clients; // Our clients - int ris; // Raw input listening socket - int bis; // Beast input listening socket - int https; // HTTP listening socket struct net_writer raw_out; // Raw output struct net_writer beast_out; // Beast-format output @@ -448,21 +430,6 @@ void useModesMessage (struct modesMessage *mm); // void interactiveShowData(void); -// -// Functions exported from net_io.c -// -void modesInitNet (void); -void modesQueueOutput (struct modesMessage *mm); -void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); -void modesNetPeriodicWork (void); -int decodeBinMessage (struct client *c, char *p); - -void writeJsonToFile(const char *file, char * (*generator) (const char*,int*)); -char *generateAircraftJson(const char *url_path, int *len); -char *generateReceiverJson(const char *url_path, int *len); -char *generateStatsJson(const char *url_path, int *len); -char *generateHistoryJson(const char *url_path, int *len); - #ifdef __cplusplus } #endif diff --git a/net_io.c b/net_io.c index 236f439..899c312 100644 --- a/net_io.c +++ b/net_io.c @@ -66,80 +66,160 @@ // function gets called and we accept new connections. All the rest is // handled via non-blocking I/O and manually polling clients to see if // they have something new to share with us when reading is needed. + +static int decodeBinMessage(struct client *c, char *p); +static int decodeHexMessage(struct client *c, char *hex); +static int handleHTTPRequest(struct client *c, char *p); + // //========================================================================= // // Networking "stack" initialization // -struct service { - char *descr; - int *socket; - struct net_writer *writer; - int port; - int enabled; -}; -struct service services[MODES_NET_SERVICES_NUM]; +// Init a service with the given read/write characteristics, return the new service. +// Doesn't arrange for the service to listen or connect +struct net_service *serviceInit(const char *descr, struct net_writer *writer, const char *sep, read_handler handler) +{ + struct net_service *service; -void modesInitNet(void) { - int j; - - struct service svc[MODES_NET_SERVICES_NUM] = { - {"Raw TCP output", &Modes.raw_out.socket, &Modes.raw_out, Modes.net_output_raw_port, 1}, - {"Raw TCP input", &Modes.ris, NULL, Modes.net_input_raw_port, 1}, - {"Beast TCP output", &Modes.beast_out.socket, &Modes.beast_out, Modes.net_output_beast_port, 1}, - {"Beast TCP input", &Modes.bis, NULL, Modes.net_input_beast_port, 1}, - {"HTTP server", &Modes.https, NULL, Modes.net_http_port, 1}, - {"Basestation TCP output", &Modes.sbs_out.socket, &Modes.sbs_out, Modes.net_output_sbs_port, 1}, - {"FlightAware TSV output", &Modes.fatsv_out.socket, &Modes.fatsv_out, Modes.net_fatsv_port, 1} - }; - - memcpy(&services, &svc, sizeof(svc));//services = svc; - - Modes.clients = NULL; - -#ifdef _WIN32 - if ( (!Modes.wsaData.wVersion) - && (!Modes.wsaData.wHighVersion) ) { - // Try to start the windows socket support - if (WSAStartup(MAKEWORD(2,1),&Modes.wsaData) != 0) - { - fprintf(stderr, "WSAStartup returned Error\n"); - } - } -#endif - - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - services[j].enabled = (services[j].port != 0); - if (services[j].enabled) { - int s = anetTcpServer(Modes.aneterr, services[j].port, Modes.net_bind_address); - if (s == -1) { - fprintf(stderr, "Error opening the listening port %d (%s): %s\n", - services[j].port, services[j].descr, Modes.aneterr); - exit(1); - } - anetNonBlock(Modes.aneterr, s); - *services[j].socket = s; - - if (services[j].writer) { - if (! (services[j].writer->data = malloc(MODES_OUT_BUF_SIZE)) ) { - fprintf(stderr, "Out of memory allocating output buffer for service %s\n", services[j].descr); - exit(1); - } - - services[j].writer->socket = s; - services[j].writer->connections = 0; - services[j].writer->dataUsed = 0; - services[j].writer->lastWrite = mstime(); - } - } else { - if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); - } + if (!(service = calloc(sizeof(*service), 1))) { + fprintf(stderr, "Out of memory allocating service %s\n", descr); + exit(1); } -#ifndef _WIN32 + service->next = Modes.services; + Modes.services = service; + + service->descr = descr; + service->listen_fd = -1; + service->connections = 0; + service->writer = writer; + service->read_sep = sep; + service->read_handler = handler; + + if (service->writer) { + if (! (service->writer->data = malloc(MODES_OUT_BUF_SIZE)) ) { + fprintf(stderr, "Out of memory allocating output buffer for service %s\n", descr); + exit(1); + } + + service->writer->service = service; + service->writer->dataUsed = 0; + service->writer->lastWrite = mstime(); + } + + return service; +} + +// Create a client attached to the given service using the provided socket FD +static struct client *createClient(struct net_service *service, int fd) +{ + struct client *c; + + anetNonBlock(Modes.aneterr, fd); + + if (!(c = (struct client *) malloc(sizeof(*c)))) { + fprintf(stderr, "Out of memory allocating a new %s network client\n", service->descr); + exit(1); + } + + c->service = service; + c->next = Modes.clients; + c->fd = fd; + c->buflen = 0; + Modes.clients = c; + anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size)); + + ++service->connections; + if (service->writer && service->connections == 1) { + service->writer->lastWrite = mstime(); // suppress heartbeat initially + } + + return c; +} + +// Initiate an outgoing connection which will use the given service. +// Return the new client or NULL if the connection failed +struct client *serviceConnect(struct net_service *service, char *addr, int port) +{ + int s = anetTcpConnect(Modes.aneterr, addr, port); + if (s == ANET_ERR) + return NULL; + + return createClient(service, s); +} + +// Set up the given service to listen on an address/port. +// _exits_ on failure! +void serviceListen(struct net_service *service, char *bind_addr, int bind_port) +{ + int s; + + if (service->listen_fd >= 0) { + fprintf(stderr, "Tried to set up the service %s twice!\n", service->descr); + exit(1); + } + + s = anetTcpServer(Modes.aneterr, bind_port, bind_addr); + if (s == ANET_ERR) { + fprintf(stderr, "Error opening the listening port %d (%s): %s\n", + bind_port, service->descr, Modes.aneterr); + exit(1); + } + + anetNonBlock(Modes.aneterr, s); + service->listen_fd = s; +} + +struct net_service *makeBeastInputService(void) +{ + return serviceInit("Beast TCP input", NULL, NULL, decodeBinMessage); +} + +void modesInitNet(void) { + struct net_service *s; + signal(SIGPIPE, SIG_IGN); -#endif + Modes.clients = NULL; + Modes.services = NULL; + + // set up listeners + + if (Modes.net_output_raw_port) { + s = serviceInit("Raw TCP output", &Modes.raw_out, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_raw_port); + } + + if (Modes.net_output_beast_port) { + s = serviceInit("Beast TCP output", &Modes.beast_out, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_beast_port); + } + + if (Modes.net_output_sbs_port) { + s = serviceInit("Basestation TCP output", &Modes.sbs_out, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_output_sbs_port); + } + + if (Modes.net_fatsv_port) { + s = serviceInit("FATSV TCP output", &Modes.fatsv_out, NULL, NULL); + serviceListen(s, Modes.net_bind_address, Modes.net_fatsv_port); + } + + if (Modes.net_input_raw_port) { + s = serviceInit("Raw TCP input", NULL, "\n", decodeHexMessage); + serviceListen(s, Modes.net_bind_address, Modes.net_input_raw_port); + } + + if (Modes.net_input_beast_port) { + s = makeBeastInputService(); + serviceListen(s, Modes.net_bind_address, Modes.net_input_beast_port); + } + + if (Modes.net_http_port) { + s = serviceInit("HTTP server", NULL, "\r\n\r\n", handleHTTPRequest); + serviceListen(s, Modes.net_bind_address, Modes.net_http_port); + } } // //========================================================================= @@ -147,37 +227,18 @@ void modesInitNet(void) { // This function gets called from time to time when the decoding thread is // awakened by new data arriving. This usually happens a few times every second // -struct client * modesAcceptClients(void) { +static struct client * modesAcceptClients(void) { int fd, port; - unsigned int j; - struct client *c; + struct net_service *s; - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (services[j].enabled) { - fd = anetTcpAccept(Modes.aneterr, *services[j].socket, NULL, &port); - if (fd == -1) continue; - - anetNonBlock(Modes.aneterr, fd); - c = (struct client *) malloc(sizeof(*c)); - c->service = *services[j].socket; - c->next = Modes.clients; - c->fd = fd; - c->buflen = 0; - Modes.clients = c; - anetSetSendBuffer(Modes.aneterr,fd, (MODES_NET_SNDBUF_SIZE << Modes.net_sndbuf_size)); - - if (services[j].writer) { - if (++ services[j].writer->connections == 1) { - services[j].writer->lastWrite = mstime(); // suppress heartbeat initially - } - } - - j--; // Try again with the same listening port - - if (Modes.debug & MODES_DEBUG_NET) - printf("Created new client %d\n", fd); - } + for (s = Modes.services; s; s = s->next) { + if (s->listen_fd >= 0) { + while ((fd = anetTcpAccept(Modes.aneterr, s->listen_fd, NULL, &port)) >= 0) { + createClient(s, fd); + } + } } + return Modes.clients; } // @@ -185,8 +246,11 @@ struct client * modesAcceptClients(void) { // // On error free the client, collect the structure, adjust maxfd if needed. // -void modesCloseClient(struct client *c) { - int j; +static void modesCloseClient(struct client *c) { + if (!c->service) { + fprintf(stderr, "warning: double close of net client\n"); + return; + } // Clean up, but defer removing from the list until modesNetCleanup(). // This is because there may be stackframes still pointing at this @@ -194,21 +258,11 @@ void modesCloseClient(struct client *c) { // be freed) close(c->fd); - - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (c->service == *services[j].socket) { - if (services[j].writer) - services[j].writer->connections--; - break; - } - } - - if (Modes.debug & MODES_DEBUG_NET) - printf("Closing client %d\n", c->fd); + c->service->connections--; // mark it as inactive and ready to be freed c->fd = -1; - c->service = -1; + c->service = NULL; } // //========================================================================= @@ -219,7 +273,7 @@ static void flushWrites(struct net_writer *writer) { struct client *c; for (c = Modes.clients; c; c = c->next) { - if (c->service == writer->socket) { + if (c->service == writer->service) { #ifndef _WIN32 int nwritten = write(c->fd, writer->data, writer->dataUsed); #else @@ -238,31 +292,32 @@ static void flushWrites(struct net_writer *writer) { // Prepare to write up to 'len' bytes to the given net_writer. // Returns a pointer to write to, or NULL to skip this write. static void *prepareWrite(struct net_writer *writer, int len) { - if (!writer || - !writer->connections || - !writer->data) - return NULL; + if (!writer || + !writer->service || + !writer->service->connections || + !writer->data) + return NULL; - if (len > MODES_OUT_BUF_SIZE) - return NULL; + if (len > MODES_OUT_BUF_SIZE) + return NULL; - if (writer->dataUsed + len >= MODES_OUT_BUF_SIZE) { - // Flush now to free some space - flushWrites(writer); - } + if (writer->dataUsed + len >= MODES_OUT_BUF_SIZE) { + // Flush now to free some space + flushWrites(writer); + } - return writer->data + writer->dataUsed; + return writer->data + writer->dataUsed; } // Complete a write previously begun by prepareWrite. // endptr should point one byte past the last byte written // to the buffer returned from prepareWrite. static void completeWrite(struct net_writer *writer, void *endptr) { - writer->dataUsed = endptr - writer->data; + writer->dataUsed = endptr - writer->data; - if (writer->dataUsed >= Modes.net_output_flush_size) { - flushWrites(writer); - } + if (writer->dataUsed >= Modes.net_output_flush_size) { + flushWrites(writer); + } } // @@ -270,7 +325,7 @@ static void completeWrite(struct net_writer *writer, void *endptr) { // // Write raw output in Beast Binary format with Timestamp to TCP clients // -void modesSendBeastOutput(struct modesMessage *mm) { +static void modesSendBeastOutput(struct modesMessage *mm) { int msgLen = mm->msgbits / 8; char *p = prepareWrite(&Modes.beast_out, 2 + 2 * (7 + msgLen)); char ch; @@ -320,7 +375,7 @@ void modesSendBeastOutput(struct modesMessage *mm) { // // Write raw output to TCP clients // -void modesSendRawOutput(struct modesMessage *mm) { +static void modesSendRawOutput(struct modesMessage *mm) { int msgLen = mm->msgbits / 8; char *p = prepareWrite(&Modes.raw_out, msgLen*2 + 15); int j; @@ -353,7 +408,7 @@ void modesSendRawOutput(struct modesMessage *mm) { // Write SBS output to TCP clients // The message structure mm->bFlags tells us what has been updated by this message // -void modesSendSBSOutput(struct modesMessage *mm) { +static void modesSendSBSOutput(struct modesMessage *mm) { char *p; struct timespec now; struct tm stTime_receive, stTime_now; @@ -536,7 +591,7 @@ void modesQueueOutput(struct modesMessage *mm) { // The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // -int decodeBinMessage(struct client *c, char *p) { +static int decodeBinMessage(struct client *c, char *p) { int msgLen = 0; int j; char ch; @@ -611,7 +666,7 @@ int decodeBinMessage(struct client *c, char *p) { // Turn an hex digit into its 4 bit decimal value. // Returns -1 if the digit is not in the 0-F range. // -int hexDigitVal(int c) { +static int hexDigitVal(int c) { c = tolower(c); if (c >= '0' && c <= '9') return c-'0'; else if (c >= 'a' && c <= 'f') return c-'a'+10; @@ -631,7 +686,7 @@ int hexDigitVal(int c) { // The function always returns 0 (success) to the caller as there is no // case where we want broken messages here to close the client connection. // -int decodeHexMessage(struct client *c, char *hex) { +static int decodeHexMessage(struct client *c, char *hex) { int l = strlen(hex), j; unsigned char msg[MODES_LONG_MSG_BYTES]; struct modesMessage mm; @@ -1102,7 +1157,7 @@ static struct { // Returns 1 on error to signal the caller the client connection should // be closed. // -int handleHTTPRequest(struct client *c, char *p) { +static int handleHTTPRequest(struct client *c, char *p) { char hdr[512]; int clen, hdrlen; int httpver, keepalive; @@ -1277,8 +1332,7 @@ int handleHTTPRequest(struct client *c, char *p) { // The handler returns 0 on success, or 1 to signal this function we should // close the connection with the client in case of non-recoverable errors. // -void modesReadFromClient(struct client *c, char *sep, - int(*handler)(struct client *, char *)) { +static void modesReadFromClient(struct client *c) { int left; int nread; int fullmsg; @@ -1332,7 +1386,7 @@ void modesReadFromClient(struct client *c, char *sep, e = s = c->buf; // Start with the start of buffer, first message - if (c->service == Modes.bis) { + if (c->service->read_sep == NULL) { // This is the Beast Binary scanning case. // If there is a complete message still in the buffer, there must be the separator 'sep' // in the buffer, note that we full-scan the buffer at every read for simplicity. @@ -1366,7 +1420,7 @@ void modesReadFromClient(struct client *c, char *sep, break; } // Have a 0x1a followed by 1, 2 or 3 - pass message less 0x1a to handler. - if (handler(c, s)) { + if (c->service->read_handler(c, s)) { modesCloseClient(c); return; } @@ -1380,13 +1434,13 @@ void modesReadFromClient(struct client *c, char *sep, // If there is a complete message still in the buffer, there must be the separator 'sep' // in the buffer, note that we full-scan the buffer at every read for simplicity. // - while ((e = strstr(s, sep)) != NULL) { // end of first message if found + while ((e = strstr(s, c->service->read_sep)) != NULL) { // end of first message if found *e = '\0'; // The handler expects null terminated strings - if (handler(c, s)) { // Pass message to handler. + if (c->service->read_handler(c, s)) { // Pass message to handler. modesCloseClient(c); // Handler returns 1 on error to signal we . return; // should close the client connection } - s = e + strlen(sep); // Move to start of next message + s = e + strlen(c->service->read_sep); // Move to start of next message fullmsg = 1; } } @@ -1402,13 +1456,14 @@ void modesReadFromClient(struct client *c, char *sep, #define TSV_MAX_PACKET_SIZE 160 -static void writeFATSV() { +static void writeFATSV() +{ struct aircraft *a; uint64_t now; static uint64_t next_update; - if (!Modes.fatsv_out.connections) { - return; // no active connections + if (!Modes.fatsv_out.service || !Modes.fatsv_out.service->connections) { + return; // not enabled or no active connections } now = mstime(); @@ -1595,82 +1650,77 @@ static void writeFATSV() { // Perform periodic network work // void modesNetPeriodicWork(void) { - struct client *c, **prev; - uint64_t now = mstime(); - int j; - int need_heartbeat = 0, need_flush = 0; + struct client *c, **prev; + struct net_service *s; + uint64_t now = mstime(); + int need_heartbeat = 0, need_flush = 0; - // Accept new connetions - modesAcceptClients(); + // Accept new connetions + modesAcceptClients(); - // Read from clients - for (c = Modes.clients; c; c = c->next) { - if (c->service == Modes.ris) { - modesReadFromClient(c,"\n",decodeHexMessage); - } else if (c->service == Modes.bis) { - modesReadFromClient(c,"",decodeBinMessage); - } else if (c->service == Modes.https) { - modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest); - } - } + // Read from clients + for (c = Modes.clients; c; c = c->next) { + if (c->service->read_handler) + modesReadFromClient(c); + } - // Generate FATSV output - writeFATSV(); + // Generate FATSV output + writeFATSV(); - // If we have generated no messages for a while, generate - // a dummy heartbeat message. - if (Modes.net_heartbeat_interval) { - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (services[j].writer && - services[j].writer->connections && - (services[j].writer->lastWrite + Modes.net_heartbeat_interval) <= now) { - need_flush = 1; - if (services[j].writer->dataUsed == 0) { - need_heartbeat = 1; - break; - } - } - } + // If we have generated no messages for a while, generate + // a dummy heartbeat message. + if (Modes.net_heartbeat_interval) { + for (s = Modes.services; s; s = s->next) { + if (s->writer && + s->connections && + (s->writer->lastWrite + Modes.net_heartbeat_interval) <= now) { + need_flush = 1; + if (s->writer->dataUsed == 0) { + need_heartbeat = 1; + break; + } + } } + } - if (need_heartbeat) { - // - // We haven't sent any traffic for some time. To try and keep any TCP - // links alive, send a null frame. This will help stop any routers discarding our TCP - // link which will cause an un-recoverable link error if/when a real frame arrives. - // - // Fudge up a null message - struct modesMessage mm; + if (need_heartbeat) { + // + // We haven't sent any traffic for some time. To try and keep any TCP + // links alive, send a null frame. This will help stop any routers discarding our TCP + // link which will cause an un-recoverable link error if/when a real frame arrives. + // + // Fudge up a null message + struct modesMessage mm; - memset(&mm, 0, sizeof(mm)); - mm.msgbits = MODES_SHORT_MSG_BITS; - mm.timestampMsg = 0; - mm.msgtype = -1; + memset(&mm, 0, sizeof(mm)); + mm.msgbits = MODES_SHORT_MSG_BITS; + mm.timestampMsg = 0; + mm.msgtype = -1; - // Feed output clients - modesQueueOutput(&mm); + // Feed output clients + modesQueueOutput(&mm); + } + + // If we have data that has been waiting to be written for a while, + // write it now. + for (s = Modes.services; s; s = s->next) { + if (s->writer && + s->writer->dataUsed && + (need_flush || (s->writer->lastWrite + Modes.net_output_flush_interval) <= now)) { + flushWrites(s->writer); } + } - // If we have data that has been waiting to be written for a while, - // write it now. - for (j = 0; j < MODES_NET_SERVICES_NUM; j++) { - if (services[j].writer && - services[j].writer->dataUsed && - (need_flush || (services[j].writer->lastWrite + Modes.net_output_flush_interval) <= now)) { - flushWrites(services[j].writer); - } - } - - // Unlink and free closed clients - for (prev = &Modes.clients, c = *prev; c; c = *prev) { - if (c->fd == -1) { - // Recently closed, prune from list - *prev = c->next; - free(c); - } else { - prev = &c->next; - } - } + // Unlink and free closed clients + for (prev = &Modes.clients, c = *prev; c; c = *prev) { + if (c->fd == -1) { + // Recently closed, prune from list + *prev = c->next; + free(c); + } else { + prev = &c->next; + } + } } // diff --git a/net_io.h b/net_io.h new file mode 100644 index 0000000..b184f3a --- /dev/null +++ b/net_io.h @@ -0,0 +1,78 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// net_io.h: network handling. +// +// Copyright (c) 2014,2015 Oliver Jowett +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// This file is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef DUMP1090_NETIO_H +#define DUMP1090_NETIO_H + +// Describes a networking service (group of connections) + +struct modesMessage; +struct client; +typedef int (*read_handler)(struct client *, char *); + +// Describes one network service (a group of clients with common behaviour) +struct net_service { + struct net_service* next; + const char *descr; + int listen_fd; + + int connections; // number of active clients + + struct net_writer *writer; // shared writer state + + const char *read_sep; // hander details for input data + read_handler read_handler; +}; + +// Structure used to describe a networking client +struct client { + struct client* next; // Pointer to next client + int fd; // File descriptor + struct net_service *service; // Service this client is part of + int buflen; // Amount of data on buffer + char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer +}; + +// Common writer state for all output sockets of one type +struct net_writer { + struct net_service *service; // owning service + void *data; // shared write buffer, sized MODES_OUT_BUF_SIZE + int dataUsed; // number of bytes of write buffer currently used + uint64_t lastWrite; // time of last write to clients +}; + +struct net_service *serviceInit(const char *descr, struct net_writer *writer, const char *sep, read_handler handler); +struct client *serviceConnect(struct net_service *service, char *addr, int port); +void serviceListen(struct net_service *service, char *bind_addr, int bind_port); + +// view1090 / faup1090 want to create this themselves: +struct net_service *makeBeastInputService(void); + +void modesInitNet(void); +void modesQueueOutput(struct modesMessage *mm); +void modesNetPeriodicWork(void); + +// TODO: move these somewhere else +char *generateAircraftJson(const char *url_path, int *len); +char *generateStatsJson(const char *url_path, int *len); +char *generateReceiverJson(const char *url_path, int *len); +char *generateHistoryJson(const char *url_path, int *len); +void writeJsonToFile(const char *file, char * (*generator) (const char *,int*)); + +#endif diff --git a/view1090.c b/view1090.c index 119ad49..573f751 100644 --- a/view1090.c +++ b/view1090.c @@ -27,12 +27,12 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#include "view1090.h" +#include "dump1090.h" // // ============================= Utility functions ========================== // void sigintHandler(int dummy) { - NOTUSED(dummy); + MODES_NOTUSED(dummy); signal(SIGINT, SIG_DFL); // reset signal handler - bit extra safety Modes.exit = 1; // Signal to threads that we are done } @@ -63,15 +63,11 @@ int getTermRows() { return MODES_INTERACTIVE_ROWS;} void view1090InitConfig(void) { // Default everything to zero/NULL memset(&Modes, 0, sizeof(Modes)); - memset(&View1090, 0, sizeof(View1090)); // Now initialise things that should not be 0/NULL to their defaults Modes.check_crc = 1; - strcpy(View1090.net_input_beast_ipaddr,VIEW1090_NET_OUTPUT_IP_ADDRESS); - Modes.net_input_beast_port = MODES_NET_OUTPUT_BEAST_PORT; Modes.interactive_rows = getTermRows(); Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; - Modes.interactive = 1; } // @@ -117,33 +113,6 @@ void view1090Init(void) { icaoFilterInit(); } -// Set up data connection -int setupConnection(struct client *c) { - int fd; - - // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. - if ((fd = anetTcpConnect(Modes.aneterr, View1090.net_input_beast_ipaddr, Modes.net_input_beast_port)) != ANET_ERR) { - anetNonBlock(Modes.aneterr, fd); - // - // Setup a service callback client structure for a beast binary input (from dump1090) - // This is a bit dodgy under Windows. The fd parameter is a handle to the internet - // socket on which we are receiving data. Under Linux, these seem to start at 0 and - // count upwards. However, Windows uses "HANDLES" and these don't nececeriy start at 0. - // dump1090 limits fd to values less than 1024, and then uses the fd parameter to - // index into an array of clients. This is ok-ish if handles are allocated up from 0. - // However, there is no gaurantee that Windows will behave like this, and if Windows - // allocates a handle greater than 1024, then dump1090 won't like it. On my test machine, - // the first Windows handle is usually in the 0x54 (84 decimal) region. - - c->next = NULL; - c->buflen = 0; - c->fd = - c->service = - Modes.bis = fd; - Modes.clients = c; - } - return fd; -} // // ================================ Main ==================================== // @@ -175,9 +144,11 @@ void showHelp(void) { //========================================================================= // int main(int argc, char **argv) { - int j, fd; + int j; struct client *c; - char pk_buf[8]; + struct net_service *s; + char *bo_connect_ipaddr = "127.0.0.1"; + int bo_connect_port = MODES_NET_OUTPUT_BEAST_PORT; // Set sane defaults @@ -189,9 +160,9 @@ int main(int argc, char **argv) { int more = ((j + 1) < argc); // There are more arguments if (!strcmp(argv[j],"--net-bo-port") && more) { - Modes.net_input_beast_port = atoi(argv[++j]); + bo_connect_port = atoi(argv[++j]); } else if (!strcmp(argv[j],"--net-bo-ipaddr") && more) { - strcpy(View1090.net_input_beast_ipaddr, argv[++j]); + bo_connect_ipaddr = argv[++j]; } else if (!strcmp(argv[j],"--modeac")) { Modes.mode_ac = 1; } else if (!strcmp(argv[j],"--interactive-rows") && more) { @@ -240,11 +211,13 @@ int main(int argc, char **argv) { // Initialization view1090Init(); + modesInitNet(); // Try to connect to the selected ip address and port. We only support *ONE* input connection which we initiate.here. - c = (struct client *) malloc(sizeof(*c)); - if ((fd = setupConnection(c)) == ANET_ERR) { - fprintf(stderr, "Failed to connect to %s:%d\n", View1090.net_input_beast_ipaddr, Modes.net_input_beast_port); + s = makeBeastInputService(); + c = serviceConnect(s, bo_connect_ipaddr, bo_connect_port); + if (!c) { + fprintf(stderr, "Failed to connect to %s:%d: %s\n", bo_connect_ipaddr, bo_connect_port, Modes.aneterr); exit(1); } @@ -252,21 +225,18 @@ int main(int argc, char **argv) { while (!Modes.exit) { icaoFilterExpire(); trackPeriodicUpdate(); + modesNetPeriodicWork(); interactiveShowData(); - if ((fd == ANET_ERR) || (recv(c->fd, pk_buf, sizeof(pk_buf), MSG_PEEK | MSG_DONTWAIT) == 0)) { - free(c); - usleep(1000000); - c = (struct client *) malloc(sizeof(*c)); - fd = setupConnection(c); - continue; - } - modesReadFromClient(c,"",decodeBinMessage); - usleep(100000); - } - // The user has stopped us, so close any socket we opened - if (fd != ANET_ERR) - {close(fd);} + if (s->connections == 0) { + // lost input connection, try to reconnect + usleep(1000000); + c = serviceConnect(s, bo_connect_ipaddr, bo_connect_port); + continue; + } + + usleep(100000); + } return (0); } diff --git a/view1090.h b/view1090.h deleted file mode 100644 index 03ad96d..0000000 --- a/view1090.h +++ /dev/null @@ -1,84 +0,0 @@ -// view1090, a Mode S messages viewer for dump1090 devices. -// -// Copyright (C) 2013 by Malcolm Robb -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -#ifndef __VIEW1090_H -#define __VIEW1090_H - -// ============================= Include files ========================== - -#include "dump1090.h" - -#ifndef _WIN32 - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include "rtl-sdr.h" - #include "anet.h" -#else - #include "winstubs.h" //Put everything Windows specific in here -#endif - -// ============================= #defines =============================== - -#define VIEW1090_NET_OUTPUT_IP_ADDRESS "127.0.0.1" - -#define NOTUSED(V) ((void) V) - -// ======================== structure declarations ======================== - -// Program global state -struct { // Internal state - // Networking - char net_input_beast_ipaddr[32]; // IPv4 address or network name of server/RPi -} View1090; - -// ======================== function declarations ========================= - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif // __VIEW1090_H