From 453d4be4d0e85f429d85f237f12b903b7bd2d400 Mon Sep 17 00:00:00 2001
From: Alex Alashkin <alex.gremlin@outlook.com>
Date: Wed, 11 Aug 2021 09:56:46 +0300
Subject: [PATCH] Update FREERTOS + LWIP support

---
 mongoose.c               | 26 +++++++++++++++++---------
 mongoose.h               | 13 ++++++++++++-
 src/arch_freertos_lwip.h | 13 ++++++++++++-
 src/fs_posix.c           |  2 +-
 src/log.c                |  1 +
 src/sock.c               | 23 +++++++++++++++--------
 6 files changed, 58 insertions(+), 20 deletions(-)

diff --git a/mongoose.c b/mongoose.c
index 485d2a255..8b60b340a 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -529,7 +529,7 @@ static char *posix_realpath(const char *path, char *resolved_path) {
 #ifdef _WIN32
   return _fullpath(resolved_path, path, _MAX_PATH);
 #elif MG_ARCH == MG_ARCH_ESP32 || MG_ARCH == MG_ARCH_ESP8266 || \
-    MG_ARCH == MG_ARCH_FREERTOS_TCP
+    MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP
   if (resolved_path == NULL) resolved_path = malloc(strlen(path) + 1);
   strcpy(resolved_path, path);
   return resolved_path;
@@ -1830,6 +1830,7 @@ bool mg_log_prefix(int level, const char *file, int line, const char *fname) {
     time_t t = time(NULL);
     struct tm tmp, *tm = gmtime_r(&t, &tmp);
     int n, tag;
+    (void)tmp;
     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
     tag = level == LL_ERROR ? 'E' : level == LL_INFO ? 'I' : ' ';
     n = snprintf(buf, sizeof(buf), "%s  %c %s:%d:%s", timebuf, tag, p, line,
@@ -2944,6 +2945,8 @@ static void mg_set_non_blocking_mode(SOCKET fd) {
   const BaseType_t off = 0;
   setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off));
   setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off));
+#elif MG_ARCH == MG_ARCH_FREERTOS_LWIP
+  lwip_fcntl(fd, F_SETFL, O_NONBLOCK);  
 #else
   fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK, FD_CLOEXEC);
 #endif
@@ -2961,16 +2964,21 @@ SOCKET mg_open_listener(const char *url, struct mg_addr *addr) {
     int on = 1, af = addr->is_ip6 ? AF_INET6 : AF_INET;
     int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
     int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+    (void)on;
 
     if ((fd = socket(af, type, proto)) != INVALID_SOCKET &&
-#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE)
-        // SO_RESUSEADDR is not enabled on Windows because the semantics of
-        // SO_REUSEADDR on UNIX and Windows is different. On Windows,
-        // SO_REUSEADDR allows to bind a socket to a port without error even if
-        // the port is already open by another program. This is not the behavior
-        // SO_REUSEADDR was designed for, and leads to hard-to-track failure
-        // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
-        // SO_EXCLUSIVEADDRUSE is supported and set on a socket.
+#if (!defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE)) \
+    && (!defined(LWIP_SOCKET) || (defined(LWIP_SOCKET) && SO_REUSE == 1))
+        // 1. SO_RESUSEADDR is not enabled on Windows because the semantics of
+        //    SO_REUSEADDR on UNIX and Windows is different. On Windows,
+        //    SO_REUSEADDR allows to bind a socket to a port without error even if
+        //    the port is already open by another program. This is not the behavior
+        //    SO_REUSEADDR was designed for, and leads to hard-to-track failure
+        //    scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
+        //    SO_EXCLUSIVEADDRUSE is supported and set on a socket.
+        // 2. In case of LWIP, SO_REUSEADDR should be explicitly enabled, by defining   
+        //    SO_REUSE (in lwipopts.h), otherwise the code below will compile
+        //    but won't work! (setsockopt will return EINVAL)  
         !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) &&
 #endif
 #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE)
diff --git a/mongoose.h b/mongoose.h
index ba48ced28..69883995b 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -192,10 +192,10 @@ extern "C" {
 
 #if MG_ARCH == MG_ARCH_FREERTOS_LWIP
 
-#include <errno.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
+#include <string.h>
 
 #if defined(__GNUC__)
 #include <sys/stat.h>
@@ -213,6 +213,17 @@ struct timeval {
 
 #include <lwip/sockets.h>
 
+#if LWIP_SOCKET != 1
+// Sockets support disabled in LWIP by default 
+#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
+#endif
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES != 0
+// LWIP_POSIX_SOCKETS_IO_NAMES must be disabled in posix-compatible OS enviroment
+// (freertos mimics to one) otherwise names like `read` and `write` conflict
+#error LWIP_POSIX_SOCKETS_IO_NAMES must be set to 0 (in lwipopts.h) for FreeRTOS
+#endif
+
 #define MG_INT64_FMT "%lld"
 #define MG_DIRSEP '/'
 
diff --git a/src/arch_freertos_lwip.h b/src/arch_freertos_lwip.h
index 4e5ebc384..56213b972 100644
--- a/src/arch_freertos_lwip.h
+++ b/src/arch_freertos_lwip.h
@@ -2,10 +2,10 @@
 
 #if MG_ARCH == MG_ARCH_FREERTOS_LWIP
 
-#include <errno.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
+#include <string.h>
 
 #if defined(__GNUC__)
 #include <sys/stat.h>
@@ -23,6 +23,17 @@ struct timeval {
 
 #include <lwip/sockets.h>
 
+#if LWIP_SOCKET != 1
+// Sockets support disabled in LWIP by default 
+#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
+#endif
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES != 0
+// LWIP_POSIX_SOCKETS_IO_NAMES must be disabled in posix-compatible OS enviroment
+// (freertos mimics to one) otherwise names like `read` and `write` conflict
+#error LWIP_POSIX_SOCKETS_IO_NAMES must be set to 0 (in lwipopts.h) for FreeRTOS
+#endif
+
 #define MG_INT64_FMT "%lld"
 #define MG_DIRSEP '/'
 
diff --git a/src/fs_posix.c b/src/fs_posix.c
index 15e4d2dd6..e5c55f09c 100644
--- a/src/fs_posix.c
+++ b/src/fs_posix.c
@@ -5,7 +5,7 @@ static char *posix_realpath(const char *path, char *resolved_path) {
 #ifdef _WIN32
   return _fullpath(resolved_path, path, _MAX_PATH);
 #elif MG_ARCH == MG_ARCH_ESP32 || MG_ARCH == MG_ARCH_ESP8266 || \
-    MG_ARCH == MG_ARCH_FREERTOS_TCP
+    MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP
   if (resolved_path == NULL) resolved_path = malloc(strlen(path) + 1);
   strcpy(resolved_path, path);
   return resolved_path;
diff --git a/src/log.c b/src/log.c
index a4c0d721c..b2e9e9843 100644
--- a/src/log.c
+++ b/src/log.c
@@ -37,6 +37,7 @@ bool mg_log_prefix(int level, const char *file, int line, const char *fname) {
     time_t t = time(NULL);
     struct tm tmp, *tm = gmtime_r(&t, &tmp);
     int n, tag;
+    (void)tmp;
     strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
     tag = level == LL_ERROR ? 'E' : level == LL_INFO ? 'I' : ' ';
     n = snprintf(buf, sizeof(buf), "%s  %c %s:%d:%s", timebuf, tag, p, line,
diff --git a/src/sock.c b/src/sock.c
index 05d87594e..2b8772f60 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -118,6 +118,8 @@ static void mg_set_non_blocking_mode(SOCKET fd) {
   const BaseType_t off = 0;
   setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off));
   setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off));
+#elif MG_ARCH == MG_ARCH_FREERTOS_LWIP
+  lwip_fcntl(fd, F_SETFL, O_NONBLOCK);  
 #else
   fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK, FD_CLOEXEC);
 #endif
@@ -135,16 +137,21 @@ SOCKET mg_open_listener(const char *url, struct mg_addr *addr) {
     int on = 1, af = addr->is_ip6 ? AF_INET6 : AF_INET;
     int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM;
     int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+    (void)on;
 
     if ((fd = socket(af, type, proto)) != INVALID_SOCKET &&
-#if !defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE)
-        // SO_RESUSEADDR is not enabled on Windows because the semantics of
-        // SO_REUSEADDR on UNIX and Windows is different. On Windows,
-        // SO_REUSEADDR allows to bind a socket to a port without error even if
-        // the port is already open by another program. This is not the behavior
-        // SO_REUSEADDR was designed for, and leads to hard-to-track failure
-        // scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
-        // SO_EXCLUSIVEADDRUSE is supported and set on a socket.
+#if (!defined(_WIN32) || !defined(SO_EXCLUSIVEADDRUSE)) \
+    && (!defined(LWIP_SOCKET) || (defined(LWIP_SOCKET) && SO_REUSE == 1))
+        // 1. SO_RESUSEADDR is not enabled on Windows because the semantics of
+        //    SO_REUSEADDR on UNIX and Windows is different. On Windows,
+        //    SO_REUSEADDR allows to bind a socket to a port without error even if
+        //    the port is already open by another program. This is not the behavior
+        //    SO_REUSEADDR was designed for, and leads to hard-to-track failure
+        //    scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
+        //    SO_EXCLUSIVEADDRUSE is supported and set on a socket.
+        // 2. In case of LWIP, SO_REUSEADDR should be explicitly enabled, by defining   
+        //    SO_REUSE (in lwipopts.h), otherwise the code below will compile
+        //    but won't work! (setsockopt will return EINVAL)  
         !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) &&
 #endif
 #if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) && !defined(WINCE)
-- 
GitLab