Timur Davydov has uploaded this change for review.
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 <dtv.comp@gmail.com>
+ * 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 <dtv.comp@gmail.com>
+ * 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 change 41813. To unsubscribe, or for help writing mail filters, visit settings.