Timur Davydov has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmocore/+/41813?usp=email )


Change subject: Add Emscripten build support and JS callback logging backend
......................................................................

Add Emscripten build support and JS callback logging backend

Change-Id: Ia8d5f4bb6570b5e055826f3a051e5e5896866e31
---
M configure.ac
M include/osmocom/core/logging.h
M src/core/Makefile.am
A src/core/logging_web.c
M src/core/netdev.c
M src/core/osmo_io_internal.h
M src/core/serial.c
M src/core/socket.c
M src/core/stats_tcp.c
M src/core/tun.c
M src/vty/Makefile.am
A src/vty/telnet_interface_dummy.c
12 files changed, 230 insertions(+), 11 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/13/41813/1

diff --git a/configure.ac b/configure.ac
index 6a5f0c0..b678237 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,18 @@
 AC_PROG_INSTALL
 LT_INIT([pic-only disable-static])

+dnl Detect emscripten compiler
+case "$CC" in
+*emcc*)
+       emscripten=yes
+       ;;
+*)
+       emscripten=no
+       ;;
+esac
+AM_CONDITIONAL(HAVE_EMSCRIPTEN, test "x$emscripten" = "xyes")
+AC_SUBST([HAVE_EMSCRIPTEN], [$emscripten])
+
 AC_CONFIG_MACRO_DIR([m4])
 
 dnl patching ${archive_cmds} to affect generation of file "libtool" to fix 
linking with clang
diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h
index 44263fb..5aa7f9b 100644
--- a/include/osmocom/core/logging.h
+++ b/include/osmocom/core/logging.h
@@ -282,6 +282,7 @@
        LOG_TGT_TYPE_STRRB,     /*!< osmo_strrb-backed logging */
        LOG_TGT_TYPE_GSMTAP,    /*!< GSMTAP network logging */
        LOG_TGT_TYPE_SYSTEMD,   /*!< systemd journal logging */
+       LOG_TGT_TYPE_WEB,       /*!< Web logging */
 };

 /*! Whether/how to log the source filename (and line number). */
@@ -443,6 +444,7 @@
                                            bool ofd_wq_mode,
                                            bool add_sink);
 struct log_target *log_target_create_systemd(bool raw);
+struct log_target *log_target_create_web();
 void log_target_systemd_set_raw(struct log_target *target, bool raw);
 int log_target_file_reopen(struct log_target *tgt);
 int log_target_file_switch_to_stream(struct log_target *tgt);
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 0b756da..338e6fa 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -46,6 +46,7 @@
        logging.c \
        logging_syslog.c \
        logging_gsmtap.c \
+       logging_web.c \
        loggingrb.c \
        macaddr.c \
        msgb.c \
diff --git a/src/core/logging_web.c b/src/core/logging_web.c
new file mode 100644
index 0000000..859cbe8
--- /dev/null
+++ b/src/core/logging_web.c
@@ -0,0 +1,98 @@
+/*! \file logging_web.c
+ *  Logging support code using a JS callback. This module sends log
+ *  messages to a JavaScript callback named `on_log`.
+ *  */
+/*
+ * (C) 2026 by Timur Davydov <[email protected]>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it 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 program 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.
+ *
+ */
+
+/*! \addtogroup logging
+ *  @{
+ * \file logging_web.c */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/byteswap.h>
+
+#if defined(__EMSCRIPTEN__)
+
+#include <emscripten.h>
+
+EM_JS(void, on_log_wrapper, (const char *subsys, int level, const char *msg), {
+       return on_log(subsys, level, msg);
+});
+#else
+void on_log_wrapper(const char *subsys, int level, const char *msg)
+{
+}
+#endif
+
+static void _web_raw_output(struct log_target *target, int subsys,
+                              unsigned int level, const char *file,
+                              int line, int cont, const char *format,
+                              va_list ap)
+{
+       const int msgLen = 4096;
+       char msg[msgLen + 1];
+       const char *subsys_name = log_category_name(subsys);
+       char subsys_buf[16];
+       int rc;
+
+       if (subsys_name)
+               OSMO_STRLCPY_ARRAY(subsys_buf, subsys_name + 1);
+       else
+               subsys_buf[0] = '\0';
+
+       rc = vsnprintf(msg, msgLen, format, ap);
+       if (rc > 0) {
+               if (msg[rc - 1] == '\n') msg[rc - 1] = '\0';
+               on_log_wrapper(subsys_buf, level, msg);
+       }
+}
+
+/*! Create a new logging target for JS callback logging (uses `on_log`)
+ *  \returns Log target in case of success, NULL in case of error
+ */
+struct log_target *log_target_create_web()
+{
+       struct log_target *target;
+
+       target = log_target_create();
+       if (!target)
+               return NULL;
+
+       target->type = LOG_TGT_TYPE_WEB;
+       target->raw_output = _web_raw_output;
+
+       return target;
+}
+
+/* @} */
diff --git a/src/core/netdev.c b/src/core/netdev.c
index 1e886f9..4abc30a 100644
--- a/src/core/netdev.c
+++ b/src/core/netdev.c
@@ -64,7 +64,7 @@
  *     osmo_netdev_free(netdev);
  */

-#if (!EMBEDDED)
+#if (!EMBEDDED) && !defined(__EMSCRIPTEN__)

 #include <stdio.h>
 #include <stdlib.h>
@@ -1018,6 +1018,6 @@
        return rc;
 }

-#endif /* (!EMBEDDED) */
+#endif /* (!EMBEDDED) && !defined(__EMSCRIPTEN__) */

 /*! @} */
diff --git a/src/core/osmo_io_internal.h b/src/core/osmo_io_internal.h
index f425da2..03ec70a 100644
--- a/src/core/osmo_io_internal.h
+++ b/src/core/osmo_io_internal.h
@@ -4,7 +4,9 @@

 #include <unistd.h>
 #include <stdbool.h>
+#ifdef HAVE_LIBSCTP
 #include <netinet/sctp.h>
+#endif

 #include <osmocom/core/osmo_io.h>
 #include <osmocom/core/linuxlist.h>
diff --git a/src/core/serial.c b/src/core/serial.c
index 117c049..5ed1b34 100644
--- a/src/core/serial.c
+++ b/src/core/serial.c
@@ -24,6 +24,8 @@
  *
  * \file serial.c */

+#if !defined(__EMSCRIPTEN__)
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -277,3 +279,5 @@
 }

 /*! @} */
+
+#endif /* if !defined(__EMSCRIPTEN__) */
diff --git a/src/core/socket.c b/src/core/socket.c
index bb32185..1379bb6 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1695,7 +1695,7 @@

 static unsigned int in6_addr_netmask_to_prefixlen(const struct in6_addr 
*netmask)
 {
-       #if defined(__linux__)
+       #if defined(__linux__) || defined(__EMSCRIPTEN__)
                #define ADDRFIELD(i) s6_addr32[i]
        #else
                #define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
@@ -1895,7 +1895,6 @@
        return 0;
 }

-#ifdef HAVE_LIBSCTP
 /*! Get multiple IP addresses and/or port number on socket in separate string 
buffers
  *  \param[in] fd file descriptor of socket.
  *  \param[out] ip_proto IPPROTO of the socket, eg: IPPROTO_SCTP.
@@ -1943,6 +1942,7 @@
                return osmo_sock_get_ip_and_port(fd, ip, ip_len, port, 
port_len, local);
        }

+#ifdef HAVE_LIBSCTP
        rc = local ? sctp_getladdrs(fd, 0, &addrs) : sctp_getpaddrs(fd, 0, 
&addrs);
        if (rc < 0)
                return rc;
@@ -1983,8 +1983,10 @@
 free_addrs_ret:
        local ? sctp_freeladdrs(addrs) : sctp_freepaddrs(addrs);
        return rc;
-}
+#else
+       return -ENOTSUP;
 #endif
+}

 /*! Get local IP address on socket
  *  \param[in] fd file descriptor of socket
@@ -2047,7 +2049,6 @@
        return talloc_asprintf(ctx, "(%s)", str);
 }

-#ifdef HAVE_LIBSCTP
 /*! Format multiple IP addresses and/or port number into a combined string 
buffer
  *  \param[out] str  Destination string buffer.
  *  \param[in] str_len  sizeof(str), usually 
OSMO_SOCK_MULTIADDR_PEER_STR_MAXLEN.
@@ -2153,7 +2154,6 @@

        return sb.chars_needed;
 }
-#endif

 /*! Get address/port information on socket in provided string buffer, like 
"r=1.2.3.4:5<->l=6.7.8.9:10".
  * This does not include braces like osmo_sock_get_name().
diff --git a/src/core/stats_tcp.c b/src/core/stats_tcp.c
index c6459fe..8341979 100644
--- a/src/core/stats_tcp.c
+++ b/src/core/stats_tcp.c
@@ -24,6 +24,7 @@
 #include "config.h"
 #if !defined(EMBEDDED)

+#if !defined(__EMSCRIPTEN__)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
@@ -186,6 +187,7 @@

        return false;
 }
+#endif /* !defined(__EMSCRIPTEN__) */

 /*! Register an osmo_fd for TCP stats monitoring.
  *  \param[in] fd osmocom file descriptor to be registered.
@@ -193,6 +195,7 @@
  *  \returns 0 on success; negative in case of error. */
 int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name)
 {
+#if !defined(__EMSCRIPTEN__)
        struct stats_tcp_entry *stats_tcp_entry;

        /* Only TCP sockets can be registered for monitoring, anything else 
will fall through. */
@@ -212,10 +215,12 @@
        pthread_mutex_lock(&stats_tcp_lock);
        llist_add_tail(&stats_tcp_entry->entry, &stats_tcp);
        pthread_mutex_unlock(&stats_tcp_lock);
+#endif /* !defined(__EMSCRIPTEN__) */

        return 0;
 }

+#if !defined(__EMSCRIPTEN__)
 static void next_stats_tcp_entry(void)
 {
        struct stats_tcp_entry *last;
@@ -235,12 +240,14 @@
                    (struct stats_tcp_entry 
*)llist_entry(stats_tcp_entry_cur->entry.next, struct stats_tcp_entry,
                                                          entry);
 }
+#endif /* !defined(__EMSCRIPTEN__) */
 
 /*! Register an osmo_fd for TCP stats monitoring.
  *  \param[in] fd osmocom file descriptor to be unregistered.
  *  \returns 0 on success; negative in case of error. */
 int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd)
 {
+#if !defined(__EMSCRIPTEN__)
        struct stats_tcp_entry *stats_tcp_entry;
        int rc = -EINVAL;

@@ -269,8 +276,12 @@
        pthread_mutex_unlock(&stats_tcp_lock);

        return rc;
+#else
+       return 0;
+#endif /* !defined(__EMSCRIPTEN__) */
 }

+#if !defined(__EMSCRIPTEN__)
 static void stats_tcp_poll_timer_cb(void *data)
 {
        int i;
@@ -298,18 +309,22 @@
        if (osmo_tcp_stats_config->interval > 0)
                osmo_timer_schedule(&stats_tcp_poll_timer, 
osmo_tcp_stats_config->interval, 0);
 }
+#endif /* !defined(__EMSCRIPTEN__) */

 /*! Set the polling interval (common for all sockets)
  *  \param[in] interval Poll interval in seconds
  *  \returns 0 on success; negative on error */
 int osmo_stats_tcp_set_interval(int interval)
 {
+#if !defined(__EMSCRIPTEN__)
        osmo_tcp_stats_config->interval = interval;
        if (osmo_tcp_stats_config->interval > 0)
                osmo_timer_schedule(&stats_tcp_poll_timer, 
osmo_tcp_stats_config->interval, 0);
+#endif /* !defined(__EMSCRIPTEN__) */
        return 0;
 }

+#if !defined(__EMSCRIPTEN__)
 static __attribute__((constructor))
 void on_dso_load_stats_tcp(void)
 {
@@ -321,7 +336,8 @@

        osmo_timer_setup(&stats_tcp_poll_timer, stats_tcp_poll_timer_cb, NULL);
 }
+#endif /* !defined(__EMSCRIPTEN__) */

-#endif /* !EMBEDDED */
+#endif /* !defined(EMBEDDED) */

 /* @} */
diff --git a/src/core/tun.c b/src/core/tun.c
index 09a3f81..97475ad 100644
--- a/src/core/tun.c
+++ b/src/core/tun.c
@@ -67,7 +67,7 @@
  *     osmo_tundev_free(tundev);
  */

-#if (!EMBEDDED)
+#if (!EMBEDDED) && !defined(__EMSCRIPTEN__)

 #include <stdio.h>
 #include <stdlib.h>
@@ -584,6 +584,6 @@
 }


-#endif /* (!EMBEDDED) */
+#endif /* (!EMBEDDED) && !defined(__EMSCRIPTEN__) */

 /*! @} */
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am
index ecdd050..25735da 100644
--- a/src/vty/Makefile.am
+++ b/src/vty/Makefile.am
@@ -27,12 +27,17 @@
        stats_vty.c \
        talloc_ctx_vty.c \
        tdef_vty.c \
-       telnet_interface.c \
        utils.c \
        vector.c \
        vty.c \
        $(NULL)

+if HAVE_EMSCRIPTEN
+libosmovty_la_SOURCES += telnet_interface_dummy.c
+else
+libosmovty_la_SOURCES += telnet_interface.c
+endif
+
 libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined
 libosmovty_la_LIBADD = \
        $(top_builddir)/src/core/libosmocore.la \
diff --git a/src/vty/telnet_interface_dummy.c b/src/vty/telnet_interface_dummy.c
new file mode 100644
index 0000000..714cc30
--- /dev/null
+++ b/src/vty/telnet_interface_dummy.c
@@ -0,0 +1,79 @@
+/* (C) 2026 by Timur Davydov <[email protected]>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it 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 program 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.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/signal.h>
+
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/command.h>
+
+/*! \file telnet_interface_dummy.c
+ *  Telnet interface towards Osmocom VTY
+ *
+ *  This module contains the dummy code implementing a telnet server for VTY
+ *  access.
+ */
+
+int telnet_init(void *tall_ctx, void *priv, int port)
+{
+       return 0;
+}
+
+int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port)
+{
+       return 0;
+}
+
+int telnet_init_default(void *tall_ctx, void *priv, int default_port)
+{
+       return 0;
+}
+
+
+/*! close a telnet connection */
+int telnet_close_client(struct osmo_fd *fd)
+{
+       return 0;
+}
+
+bool vty_is_active(struct vty *vty)
+{
+       return false;
+}
+
+/*! callback from core VTY code about VTY related events */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+       return;
+}
+
+/*! Close all telnet connections and release the telnet socket */
+void telnet_exit(void)
+{
+}

--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/41813?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Ia8d5f4bb6570b5e055826f3a051e5e5896866e31
Gerrit-Change-Number: 41813
Gerrit-PatchSet: 1
Gerrit-Owner: Timur Davydov <[email protected]>

Reply via email to