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 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) +{ +}