From 86ac8bdd8fa70044756fad9846a14ccd2ea571d2 Mon Sep 17 00:00:00 2001 From: SnakePin <18491360+SnakePin@users.noreply.github.com> Date: Wed, 4 Aug 2021 15:00:41 +0300 Subject: [PATCH] Added Windows support --- Makefile | 35 +++++++++----- README.md | 9 ++++ anet.c | 46 +++++++++++++------ compat/compat.h | 29 +++--------- compat/endian.h | 115 ++++++++++++++++++++++++++++++++++++++++++++++ dump1090.c | 1 + dump1090.h | 9 +++- faup1090.c | 2 + interactive.c | 6 ++- net_io.c | 118 ++++++++++++++++++++++++++++++++++-------------- net_io.h | 1 + view1090.c | 20 +------- 12 files changed, 290 insertions(+), 101 deletions(-) create mode 100644 compat/endian.h diff --git a/Makefile b/Makefile index a85d154c5..efbb63abf 100644 --- a/Makefile +++ b/Makefile @@ -39,17 +39,20 @@ else LIMESDR ?= no endif -UNAME := $(shell uname) +ifeq ($(OS),Windows_NT) + DETECTED_OS := Windows +else + DETECTED_OS := $(shell sh -c 'uname 2>/dev/null || echo Unknown') +endif -ifeq ($(UNAME), Linux) - CPPFLAGS += -D_DEFAULT_SOURCE +ifeq ($(DETECTED_OS), Linux) LIBS += -lrt LIBS_USB += -lusb-1.0 LIBS_CURSES := -lncurses CPUFEATURES ?= yes endif -ifeq ($(UNAME), Darwin) +ifeq ($(DETECTED_OS), Darwin) ifneq ($(shell sw_vers -productVersion | egrep '^10\.([0-9]|1[01])\.'),) # Mac OS X ver <= 10.11 CPPFLAGS += -DMISSING_GETTIME COMPAT += compat/clock_gettime/clock_gettime.o @@ -61,29 +64,37 @@ ifeq ($(UNAME), Darwin) CPUFEATURES ?= yes endif -ifeq ($(UNAME), OpenBSD) +ifeq ($(DETECTED_OS), OpenBSD) CPPFLAGS += -DMISSING_NANOSLEEP COMPAT += compat/clock_nanosleep/clock_nanosleep.o LIBS_USB += -lusb-1.0 LIBS_CURSES := -lncurses + CPUFEATURES ?= yes endif -ifeq ($(UNAME), FreeBSD) - CPPFLAGS += -D_DEFAULT_SOURCE +ifeq ($(DETECTED_OS), FreeBSD) LIBS += -lrt LIBS_USB += -lusb LIBS_CURSES := -lncurses endif -ifeq ($(UNAME), NetBSD) - CFLAGS += -D_DEFAULT_SOURCE +ifeq ($(DETECTED_OS), NetBSD) LIBS += -lrt LIBS_USB += -lusb-1.0 LIBS_CURSES := -lcurses endif -CPUFEATURES ?= no +ifeq ($(DETECTED_OS), Windows) + # TODO: Perhaps copy the DLL files to the output folder if the OS is Windows? + CPPFLAGS += -DMISSING_TIME_R_FUNCS -DMISSING_CURSES_H_NCURSES -D_USE_MATH_DEFINES + LIBS += -lws2_32 -lsystre + LIBS_USB += -lusb-1.0 + LIBS_CURSES := -lncurses + CPUFEATURES ?= yes +endif + +CPUFEATURES ?= no ifeq ($(CPUFEATURES),yes) include Makefile.cpufeatures CPPFLAGS += -DENABLE_CPUFEATURES -Icpu_features/include @@ -202,7 +213,9 @@ starch-benchmark: cpu.o dsp/helpers/tables.o $(CPUFEATURES_OBJS) $(STARCH_OBJS) $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) clean: - rm -f *.o oneoff/*.o compat/clock_gettime/*.o compat/clock_nanosleep/*.o cpu_features/src/*.o dsp/generated/*.o dsp/helpers/*.o $(CPUFEATURES_OBJS) dump1090 view1090 faup1090 cprtests crctests oneoff/convert_benchmark oneoff/decode_comm_b oneoff/dsp_error_measurement oneoff/uc8_capture_stats starch-benchmark + rm -f dump1090 view1090 faup1090 cprtests crctests oneoff/convert_benchmark oneoff/decode_comm_b oneoff/dsp_error_measurement oneoff/uc8_capture_stats starch-benchmark + find . -type f -name '*.o' -exec rm {} + + find . -type f -name '*.exe' -exec rm {} + test: cprtests ./cprtests diff --git a/README.md b/README.md index 96369edfb..7e8845ec2 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,15 @@ libhackrf. ``make LIMESDR=no`` will disable LimeSDR support and remove the dependency on libLimeSuite. +## Building on MSYS2 & MinGW-w64 + +Install PothosSDR on the default location and install MSYS2 with MinGW-w64. + +``` +$ pacman -S mingw-w64-x86_64-ncurses mingw-w64-x86_64-libgnurx +$ PKG_CONFIG_PATH="/c/PothosSDR/lib/pkgconfig:$PKG_CONFIG_PATH" make -j$(nproc) +``` + ## Building on OSX Minimal testing on Mojave 10.14.6, YMMV. diff --git a/anet.c b/anet.c index 9d89badd2..7e24dc737 100644 --- a/anet.c +++ b/anet.c @@ -51,17 +51,29 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef _WIN32 +# include +# include +# include +# include +# define socklen_t int +# define sockaddr_storage sockaddr +# define p_setsockopt_optval_t const char* +#else +# include +# include +# include +# include +# include +# include +# include +# define p_setsockopt_optval_t void* +#endif #include -#include #include -#include -#include -#include -#include -#include #include #include -#include + #include #include #include @@ -80,6 +92,7 @@ static void anetSetError(char *err, const char *fmt, ...) int anetNonBlock(char *err, int fd) { +#if !defined(_WIN32) int flags; /* Set the socket nonblocking. @@ -93,14 +106,21 @@ int anetNonBlock(char *err, int fd) anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)); return ANET_ERR; } - +#else + u_long mode = 1; // 1 to enable non-blocking socket + int errorCode = ioctlsocket(fd, FIONBIO, &mode); + if (errorCode != 0) { + anetSetError(err, "ioctlsocket(FIONBIO): %d", errorCode); + return ANET_ERR; + } +#endif return ANET_OK; } int anetTcpNoDelay(char *err, int fd) { int yes = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(yes)) == -1) + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (p_setsockopt_optval_t)&yes, sizeof(yes)) == -1) { anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); return ANET_ERR; @@ -110,7 +130,7 @@ int anetTcpNoDelay(char *err, int fd) int anetSetSendBuffer(char *err, int fd, int buffsize) { - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&buffsize, sizeof(buffsize)) == -1) + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (p_setsockopt_optval_t)&buffsize, sizeof(buffsize)) == -1) { anetSetError(err, "setsockopt SO_SNDBUF: %s", strerror(errno)); return ANET_ERR; @@ -121,7 +141,7 @@ int anetSetSendBuffer(char *err, int fd, int buffsize) int anetTcpKeepAlive(char *err, int fd) { int yes = 1; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&yes, sizeof(yes)) == -1) { + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (p_setsockopt_optval_t)&yes, sizeof(yes)) == -1) { anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); return ANET_ERR; } @@ -138,7 +158,7 @@ static int anetCreateSocket(char *err, int domain) /* Make sure connection-intensive things like the redis benckmark * will be able to close/open sockets a zillion of times */ - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) == -1) { + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (p_setsockopt_optval_t)&on, sizeof(on)) == -1) { anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno)); return ANET_ERR; } @@ -239,7 +259,7 @@ int anetWrite(int fd, char *buf, int count) static int anetListen(char *err, int s, struct sockaddr *sa, socklen_t len) { if (sa->sa_family == AF_INET6) { int on = 1; - setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (p_setsockopt_optval_t)&on, sizeof(on)); } if (bind(s,sa,len) == -1) { diff --git a/compat/compat.h b/compat/compat.h index f94015fc3..51cf2d3c9 100644 --- a/compat/compat.h +++ b/compat/compat.h @@ -5,33 +5,18 @@ * Platform-specific bits */ -#if defined(__APPLE__) - -/* - * Mach endian conversion - */ -# include -# define bswap_16 OSSwapInt16 -# define bswap_32 OSSwapInt32 -# define bswap_64 OSSwapInt64 -# include -# define le16toh(x) OSSwapLittleToHostInt16(x) -# define le32toh(x) OSSwapLittleToHostInt32(x) -# define le64toh(x) OSSwapLittleToHostInt64(x) - -#elif defined(__FreeBSD__) -#include - -#else // other platforms - -# include - -#endif +#include "endian.h" /* clock_* and time-related types */ #include +//MSVC/MinGW has no _r time functions +#ifdef MISSING_TIME_R_FUNCS +# define localtime_r(T,Tm) (localtime_s(Tm,T) ? NULL : Tm) +# define gmtime_r(T,Tm) (gmtime_s(Tm,T) ? NULL : Tm) +#endif + #if defined(CLOCK_REALTIME) # define HAVE_CLOCKID_T #endif diff --git a/compat/endian.h b/compat/endian.h new file mode 100644 index 000000000..d41838e5f --- /dev/null +++ b/compat/endian.h @@ -0,0 +1,115 @@ +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. +// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to +// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it +// an example on how to get the endian conversion functions on different platforms. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) __builtin_bswap16(x) +# define htole16(x) (x) +# define be16toh(x) __builtin_bswap16(x) +# define le16toh(x) (x) + +# define htobe32(x) __builtin_bswap32(x) +# define htole32(x) (x) +# define be32toh(x) __builtin_bswap32(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif \ No newline at end of file diff --git a/dump1090.c b/dump1090.c index b7a6ce9fb..553a6d475 100644 --- a/dump1090.c +++ b/dump1090.c @@ -920,6 +920,7 @@ int main(int argc, char **argv) { } } + modesDeInitNet(); interactiveCleanup(); // Write final stats diff --git a/dump1090.h b/dump1090.h index 0ec6def60..167921058 100644 --- a/dump1090.h +++ b/dump1090.h @@ -61,6 +61,13 @@ // ============================= Include files ========================== +#ifdef _WIN32 +//WinSocks2 must be included before Windows.h +# include +#else +# include +#endif + #include #include #include @@ -76,7 +83,7 @@ #include #include #include -#include +#include #include #include diff --git a/faup1090.c b/faup1090.c index 033c7ec00..5b8489b78 100644 --- a/faup1090.c +++ b/faup1090.c @@ -219,6 +219,8 @@ int main(int argc, char **argv) { nanosleep(&r, NULL); } + modesDeInitNet(); + return 0; } // diff --git a/interactive.c b/interactive.c index d1682d5fc..d68698d7c 100644 --- a/interactive.c +++ b/interactive.c @@ -49,7 +49,11 @@ #include "dump1090.h" -#include +#ifdef MISSING_CURSES_H_NCURSES +# include +#else +# include +#endif #include #include diff --git a/net_io.c b/net_io.c index 00f3f3010..4a82b92a8 100644 --- a/net_io.c +++ b/net_io.c @@ -256,10 +256,36 @@ struct net_service *makeFaCmdInputService(void) return serviceInit("faup Command input", NULL, NULL, READ_MODE_ASCII, "\n", handleFaupCommand); } -void modesInitNet(void) { +void modesDeInitNet(void) +{ +#if defined(_WIN32) + if ((Modes.wsaData.wVersion) && (Modes.wsaData.wHighVersion)) + { + if (WSACleanup() != 0) + { + fprintf(stderr, "WSACleanup returned Error\n"); + } + } +#endif +} + +void modesInitNet(void) +{ struct net_service *s; +#if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); +#else + 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 + Modes.clients = NULL; Modes.services = NULL; @@ -351,7 +377,7 @@ static void flushWrites(struct net_writer *writer) { #ifndef _WIN32 int nwritten = write(c->fd, writer->data, writer->dataUsed); #else - int nwritten = send(c->fd, writer->data, writer->dataUsed, 0 ); + int nwritten = send(c->fd, writer->data, writer->dataUsed, 0); #endif if (nwritten != writer->dataUsed) { modesCloseClient(c); @@ -2061,63 +2087,85 @@ static void ratelimitWriteError(const char *format, ...) va_end(ap); } +static bool UtilDeleteFile(const char *path) +{ +#ifdef _WIN32 + return DeleteFileA(path); +#else + return unlink(path) == 0; +#endif +} + +static bool UtilMoveFile(const char *fromPath, const char *toPath) +{ +#ifdef _WIN32 + return MoveFileA(fromPath, toPath); +#else + return rename(fromPath, toPath) == 0; +#endif +} + +static bool UtilCreateWritableTempFile(char* outPathBuffer, const char* prefix, const char* path) +{ +#ifdef _WIN32 + if (GetTempFileNameA(path, prefix, 0, outPathBuffer) == 0) { + return false; + } +#else + int unix_fd = 0; + mode_t mask = 0; + + snprintf(outPathBuffer, PATH_MAX, "%s/%sXXXXXX", path, prefix); + unix_fd = mkstemp(outPathBuffer); + if (unix_fd < 0) { + return NULL; + } + + mask = umask(0); + umask(mask); + fchmod(unix_fd, 0644 & ~mask); +#endif + + return true; +} + // Write JSON to file void writeJsonToFile(const char *file, char * (*generator) (const char *,int*)) { -#ifndef _WIN32 char pathbuf[PATH_MAX]; char tmppath[PATH_MAX]; - int fd; - int len = 0; - mode_t mask; + FILE *fd; + size_t len = 0; char *content; if (!Modes.json_dir) return; - snprintf(tmppath, PATH_MAX, "%s/%s.XXXXXX", Modes.json_dir, file); - tmppath[PATH_MAX-1] = 0; - fd = mkstemp(tmppath); - if (fd < 0) { + if(!UtilCreateWritableTempFile(tmppath, file, Modes.json_dir) || + (fd = fopen(tmppath, "wb")) == NULL) { + //TODO: implement a function to get the last error on windows and linux as a string ratelimitWriteError("failed to create %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno)); - return; } - mask = umask(0); - umask(mask); - fchmod(fd, 0644 & ~mask); - snprintf(pathbuf, PATH_MAX, "/data/%s", file); - pathbuf[PATH_MAX-1] = 0; - content = generator(pathbuf, &len); - - if (write(fd, content, len) != len) { - ratelimitWriteError("failed to write to %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno)); - goto error_1; - } + content = generator(pathbuf, (int*)&len); - if (close(fd) < 0) { + if (fwrite(content, 1, len, fd) != len || fclose(fd) != 0) + { ratelimitWriteError("failed to write to %s (while updating %s/%s): %s", tmppath, Modes.json_dir, file, strerror(errno)); - goto error_2; + fclose(fd); + UtilDeleteFile(tmppath); + free(content); + return; } snprintf(pathbuf, PATH_MAX, "%s/%s", Modes.json_dir, file); - pathbuf[PATH_MAX-1] = 0; - if (rename(tmppath, pathbuf) < 0) { + if(!UtilMoveFile(tmppath, pathbuf)){ ratelimitWriteError("failed to rename %s to %s: %s", tmppath, pathbuf, strerror(errno)); - goto error_2; } free(content); return; - - error_1: - close(fd); - error_2: - unlink(tmppath); - free(content); - return; -#endif } diff --git a/net_io.h b/net_io.h index 661cdb4e6..ca8a863bb 100644 --- a/net_io.h +++ b/net_io.h @@ -85,6 +85,7 @@ struct net_service *makeFaCmdInputService(void); void sendBeastSettings(struct client *c, const char *settings); void modesInitNet(void); +void modesDeInitNet(void); void modesQueueOutput(struct modesMessage *mm, struct aircraft *a); void modesNetPeriodicWork(void); diff --git a/view1090.c b/view1090.c index 3f9c5b118..afff3d547 100644 --- a/view1090.c +++ b/view1090.c @@ -66,18 +66,6 @@ static void view1090InitConfig(void) { //========================================================================= // static void view1090Init(void) { - -#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 - // Validate the users Lat/Lon home location inputs if ( (Modes.fUserLat > 90.0) // Latitude must be -90 to +90 || (Modes.fUserLat < -90.0) // and @@ -211,12 +199,6 @@ int main(int argc, char **argv) { } } -#ifdef _WIN32 - // Try to comply with the Copyright license conditions for binary distribution - if (!Modes.quiet) {showCopyright();} -#define MSG_DONTWAIT 0 -#endif - if (Modes.nfix_crc > MODES_MAX_BITERRORS) Modes.nfix_crc = MODES_MAX_BITERRORS; @@ -228,6 +210,7 @@ int main(int argc, char **argv) { s = makeBeastInputService(); c = serviceConnect(s, bo_connect_ipaddr, bo_connect_port); if (!c) { + modesDeInitNet(); interactiveCleanup(); fprintf(stderr, "Failed to connect to %s:%d: %s\n", bo_connect_ipaddr, bo_connect_port, Modes.aneterr); exit(1); @@ -261,6 +244,7 @@ int main(int argc, char **argv) { nanosleep(&r, NULL); } + modesDeInitNet(); interactiveCleanup(); return (0); }