diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6dde896c..c620c513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,3 +57,20 @@ jobs: - name: Build the project shell: cmd run: make -j CC=gcc + + test-mingw-linux: + name: Build and test with Mingw on Linux + Wine + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@master + - name: Setup Mingw and wine + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install libstdc++6:i386 libgcc-s1:i386 + sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64 + - name: Compile the project + run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine + - name: Test the project + run: make test UNAME=MINGW RUN=wine diff --git a/Makefile b/Makefile index f2432311..f5595310 100644 --- a/Makefile +++ b/Makefile @@ -46,12 +46,12 @@ SONAME_SETTER=-Wl,-soname, # For cross compilation HOSTCC?=$(CC) HOSTAR?=$(AR) -CFLAGS?=-O2 +CFLAGS?=-O2 -g LDFLAGS?=-rdynamic RUN:=$(RUN) COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC -BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS) +BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS) # For installation @@ -223,7 +223,7 @@ repl: $(JANET_TARGET) debug: $(JANET_TARGET) $(DEBUGGER) ./$(JANET_TARGET) -VALGRIND_COMMAND=valgrind --leak-check=full +VALGRIND_COMMAND=valgrind --leak-check=full --quiet valgrind: $(JANET_TARGET) $(VALGRIND_COMMAND) ./$(JANET_TARGET) @@ -293,6 +293,7 @@ build/janet.pc: $(JANET_TARGET) install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h mkdir -p '$(DESTDIR)$(BINDIR)' cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet' + strip '$(DESTDIR)$(BINDIR)/janet' mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet' cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet' ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd diff --git a/src/conf/janetconf.h b/src/conf/janetconf.h index 13d7f4ff..73e39d55 100644 --- a/src/conf/janetconf.h +++ b/src/conf/janetconf.h @@ -6,8 +6,8 @@ #define JANET_VERSION_MAJOR 1 #define JANET_VERSION_MINOR 28 #define JANET_VERSION_PATCH 0 -#define JANET_VERSION_EXTRA "" -#define JANET_VERSION "1.28.0" +#define JANET_VERSION_EXTRA "-dev" +#define JANET_VERSION "1.28.0-dev" /* #define JANET_BUILD "local" */ diff --git a/src/core/features.h b/src/core/features.h index 0adb136a..b3e1f752 100644 --- a/src/core/features.h +++ b/src/core/features.h @@ -26,9 +26,10 @@ #define JANET_FEATURES_H_defined #if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \ - || defined(__bsdi__) || defined(__DragonFly__) + || defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__) /* Use BSD source on any BSD systems, include OSX */ # define _BSD_SOURCE +# define _POSIX_C_SOURCE 200809L #else /* Use POSIX feature flags */ # ifndef _POSIX_C_SOURCE @@ -36,6 +37,10 @@ # endif #endif +#if defined(__APPLE__) +#define _DARWIN_C_SOURCE +#endif + /* Needed for sched.h for cpu count */ #ifdef __linux__ #define _GNU_SOURCE @@ -45,6 +50,11 @@ #define WIN32_LEAN_AND_MEAN #endif +/* needed for inet_pton and InitializeSRWLock */ +#ifdef __MINGW32__ +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + /* Needed for realpath on linux, as well as pthread rwlocks. */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 @@ -62,7 +72,7 @@ #endif /* Needed for several things when building with -std=c99. */ -#if !__BSD_VISIBLE && defined(__DragonFly__) +#if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__)) #define __BSD_VISIBLE 1 #endif diff --git a/src/core/net.c b/src/core/net.c index 8ebbdb53..843f36f8 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -872,6 +872,98 @@ JANET_CORE_FN(cfun_stream_flush, return argv[0]; } +struct sockopt_type { + const char *name; + int level; + int optname; + enum JanetType type; +}; + +/* List of supported socket options; The type JANET_POINTER is used + * for options that require special handling depending on the type. */ +static const struct sockopt_type sockopt_type_list[] = { + { "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN }, + { "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN }, + { "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN }, + { "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER }, + { "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER }, + { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER }, + { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER }, + { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER }, + { NULL, 0, 0, JANET_POINTER } +}; + +JANET_CORE_FN(cfun_net_setsockopt, + "(net/setsockopt stream option value)", + "set socket options.\n" + "\n" + "supported options and associated value types:\n" + "- :so-broadcast boolean\n" + "- :so-reuseaddr boolean\n" + "- :so-keepalive boolean\n" + "- :ip-multicast-ttl number\n" + "- :ip-add-membership string\n" + "- :ip-drop-membership string\n" + "- :ipv6-join-group string\n" + "- :ipv6-leave-group string\n") { + janet_arity(argc, 3, 3); + JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type); + janet_stream_flags(stream, JANET_STREAM_SOCKET); + JanetKeyword optstr = janet_getkeyword(argv, 1); + + const struct sockopt_type *st = sockopt_type_list; + while (st->name) { + if (janet_cstrcmp(optstr, st->name) == 0) { + break; + } + st++; + } + + if (st->name == NULL) { + janet_panicf("unknown socket option %q", argv[1]); + } + + union { + int v_int; + struct ip_mreq v_mreq; + struct ipv6_mreq v_mreq6; + } val; + + void *optval = (void *)&val; + socklen_t optlen = 0; + + if (st->type == JANET_BOOLEAN) { + val.v_int = janet_getboolean(argv, 2); + optlen = sizeof(val.v_int); + } else if (st->type == JANET_NUMBER) { + val.v_int = janet_getinteger(argv, 2); + optlen = sizeof(val.v_int); + } else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) { + const char *addr = janet_getcstring(argv, 2); + memset(&val.v_mreq, 0, sizeof val.v_mreq); + val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY); + val.v_mreq.imr_multiaddr.s_addr = inet_addr(addr); + optlen = sizeof(val.v_mreq); + } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) { + const char *addr = janet_getcstring(argv, 2); + memset(&val.v_mreq6, 0, sizeof val.v_mreq6); + val.v_mreq6.ipv6mr_interface = 0; + inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr); + optlen = sizeof(val.v_mreq6); + } else { + janet_panicf("invalid socket option type"); + } + + janet_assert(optlen != 0, "invalid socket option value"); + + int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen); + if (r == -1) { + janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno)); + } + + return janet_wrap_nil(); +} + static const JanetMethod net_stream_methods[] = { {"chunk", cfun_stream_chunk}, {"close", janet_cfun_stream_close}, @@ -886,6 +978,7 @@ static const JanetMethod net_stream_methods[] = { {"evchunk", janet_cfun_stream_chunk}, {"evwrite", janet_cfun_stream_write}, {"shutdown", cfun_net_shutdown}, + {"setsockopt", cfun_net_setsockopt}, {NULL, NULL} }; @@ -910,6 +1003,7 @@ void janet_lib_net(JanetTable *env) { JANET_CORE_REG("net/peername", cfun_net_getpeername), JANET_CORE_REG("net/localname", cfun_net_getsockname), JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack), + JANET_CORE_REG("net/setsockopt", cfun_net_setsockopt), JANET_REG_END }; janet_core_cfuns_ext(env, NULL, net_cfuns); diff --git a/src/include/janet.h b/src/include/janet.h index c88cd35c..941a6c35 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -182,7 +182,7 @@ extern "C" { /* Enable or disable the FFI library. Currently, FFI only enabled on * x86-64 operating systems. */ #ifndef JANET_NO_FFI -#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64)) +#if !defined(__EMSCRIPTEN__) #define JANET_FFI #endif #endif