logging: Support nonblocking-io for log target stderr & file, make it default
The nonblocking-io used to be the mode used until recently, where wq only was used if the write buffer was full. This is more performant that always waiting for poll() to write, plus the fact that we write to system synchronously, hence seeing output immediatelly or not losing it if program crashes. Furthermore, It turns out using wq in multithreaded programs may cause problems, so better switch back to using nonblocking-io by default.
This way new threads (!=main) don't need to explicitly call osmo_iofd_init() explicitly before using osmo_io. This is specially useful for threads using osmo_io indirectly, eg, when logging.
The osmo_iofd_setup() API is really the only main API which requires initialization. All other APIs requiring it come after osmo_iofd_setup() since they use an osmo_iofd pointer.
Add Emscripten build support and JS callback logging backend
This change enables building libosmocore for sandboxed, non-POSIX environments, specifically WebAssembly targets produced via the Emscripten toolchain.
The broader motivation is to allow partial execution of selected Osmocom components in isolated runtime environments where direct access to hardware and traditional operating system facilities (filesystem, sockets, privileged execution) is not available.
One intended use case is running a GSM 2G base station where the radio-facing components are executed inside a web environment, while the remaining Osmocom stack and core network components continue to run unchanged on a conventional backend server. In this model, highly stripped-down variants of osmo-bts and osmo-trx are built as static WebAssembly libraries and executed in the browser, while depending on core libraries such as libosmocore, libosmo-netif, and libosmo-abis.
A practical advantage of this approach is that no deployment or privileged setup is required on the radio endpoint side. A user can instantiate a radio endpoint with minimal configuration, while all stateful logic and operational complexity remains centralized on the backend. Multiple such radio endpoints may connect to the same backend core network, effectively forming a single logical network from the core network perspective, independent of the physical location of the radio endpoints.
Existing libosmocore build logic and platform assumptions rely on the availability of POSIX APIs and OS services which are not present in WebAssembly runtimes. This currently prevents libosmocore from being built for such targets without targeted, build-time adjustments. This patch introduces the minimal set of changes required to enable such builds, without affecting native platforms.
As part of this groundwork, a minimal callback-based logging hook is introduced. When building for Emscripten, this hook allows forwarding log messages to an external environment via a user-provided JavaScript callback. This enables integration with browser-side logging or UI infrastructure without introducing new logging backends or runtime dependencies. For all other build targets, the hook resolves to a no-op implementation and does not alter existing logging behavior.
No runtime behavior, protocol semantics, or network interactions are changed by this patch. All modifications are strictly limited to build-time and platform-specific code paths and are only active when targeting Emscripten. Native builds and existing deployment scenarios remain unaffected.
This patch is intended as groundwork. Follow-up changes, proposed separately and incrementally, may extend similar support to other Osmocom components such as libosmo-netif, libosmo-abis, osmo-bts, and osmo-trx, while keeping all such changes optional and isolated from native builds.
Reuse (struct log_target)->tgt_file->wqueue->except_cb to store the iofd pointer internally, since we are not allowed to change the struct log_target because it's public and we don't want to break the ABI. Using the wqueue except_cb is fine since that field was never used.
logging_file: Request up to 8 iofd write buffers if available
This should help in decreasing latency between submitting a logging line and getting it written to file. It should, for the same reason, optimize CPU use.
logging: Also Avoid infinite loop/deadlock in log_check_level()
A LOG() macro being called inside an already-logging path will first call log_check_level() before calling osmo_vlogp(). Since calling happens with the mutex hold, it has been seen to deadlock in eg. osmo-hnbgw.
If eg. gsmtap_log target is requested to log a message but the iofd wqueue is full, it may try to log an error message, which will create an infinite stack loop.
logging_file: Fix log_target_file_switch_to_stream on stderr
In stderr log target, the bfd of the wq was left registered despite freeing the wq, which ended up in the callback being called and the freed wq being used.
core: guard TCP stats on availability of linux/tcp.h
Detect availability of linux/tcp.h at configure time and build TCP statistics code conditionally based on that.
This replaces platform-specific conditionals with proper feature checks and avoids build failures on systems lacking Linux TCP headers or types (e.g. non-Linux or cross-build environments).
When TCP_INFO support is unavailable, the public API remains unchanged, but all related functions return -ENOTSUP.
No functional changes on systems where linux/tcp.h are available.
core: always build tun.c and gate TUN support by headers
Always build tun.c and move platform-specific checks into the implementation.
Guard the TUN-specific code paths based on the availability of linux/if_tun.h detected at configure time. If TUN support is not available, osmo_tundev_open() returns -ENOTSUP.
This keeps the public TUN API available while disabling unsupported functionality in a capability-based way.
Only include Linux-specific netlink headers when the libmnl backend is enabled.
The code paths requiring <linux/if_link.h> and <linux/rtnetlink.h> are entirely guarded by ENABLE_LIBMNL conditionals, so the headers should not be included unconditionally.
This avoids pulling in Linux-only headers when the corresponding backend is not built.
osmo_io: Fix misalignment of iofd->cmsg used as struct cmsghdr
Fixes following ASAN runtime error: """ src/stream.c:398:47: runtime error: member access within misaligned address 0x516000012b81 for type 'struct cmsghdr', which requires 8 byte alignment 0x516000012b81: note: pointer points here 51 00 00 00 30 00 00 00 00 00 00 00 84 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ^ """
Current GCC documentation [1] states that __attribute__ ((__aligned__ (alignment))) is the older sytnax. The newer _Alignas mimics the standarized alignas() in C23. Since we build with CLAGS=-std=gnu11 we are fine using _Alignas, which was introduced in C11.
socket_compat.h improvements to always have struct osmo_sockaddr available
Having a #if (!EMBEDDED) based on compiler defined variable is not a good idea. Instead, add minimal sys/socket.h and netient/in.h to always have the definition of struct osmo_sockaddr available.
remove references to define EMBEDDED in public headers
It's not a good idea to base opt-in/out of code in public headers based on some specific non-standard define being set at compile time when calling the compiler. Get rid of those references; Now that osmocom/core/socket.h can be included thanks to improved osmocom/core/socket_compat.h, it shouldn't be a problem.
We already have all other log_target implementations each on its own file. Move the file (and stderr, a specific case of file) into its own file too, properly separating most file-specific logic from general logging logic.
The do-while loop is entered: * First time: "tail_msg != NULL" (since it was set to "msg"). * Subsequent times: coming from "while" condition: ** tail_msg != NULL ** res == IOFD_SEG_ACT_HANDLE_MORE, which means iofd->pending != NULL.
Hence, always either "iofd->pending" or "tail_msg" is valid when entering the loop, and hence "msg" will also be valid.
sys/socket.h may not always be available (eg. arm-none-eabi toolchain). In the header file we only really need the forward declaration of struct msghdr, so simply do that.
socket.h: Include netinet/in.h instead of arpa/inet.h
In socket.h we are mainly interested in netinet/int.h, since that's the one defining INET_ADDRSTRLEN, struct sockaddr_in, struct in_addr, ec.
Header arpa/inet.h usually defines several functions which use those, like htonl(), inet_ntop(), etc.
See POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/arpa_inet.h.html#tag_13_03 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/netinet_in.h.html
build: keep netns API public and gate features with HAVE_*
The netns API was previously guarded by platform-specific preprocessor conditions (e.g. !EMBEDDED and defined(__linux__)), which caused the netns headers and implementation to be entirely excluded from non-Linux builds such as Emscripten. This in turn led to build failures in code depending on the netns API, as the symbols and declarations were not available at all.
Always build netns.c and keep the public declarations available for non-embedded builds. Move platform/feature conditionals into the implementation: guard the netns code paths with HAVE_SETNS/HAVE_UNSHARE/ HAVE_MOUNT and HAVE_CLONE_NEWNET, and return -ENOTSUP when the required functionality is not available.
Add configure-time checks for the required headers/functions and for the CLONE_NEWNET declaration, defining the corresponding HAVE_* macros.
No functional changes intended for platforms where netns is available.
core: fix printf format casts for struct timeval fields
Explicitly cast struct timeval fields to long when passing them to printf-style functions.
This avoids compiler warnings/errors on platforms where tv_sec or tv_usec are not exactly of type long, while keeping the existing log and control output unchanged.
Previously if we recevied a big chunk (near UINT16_MAX), we would hit the following assert in iofd_msgb_alloc2(), because our msgb implementation can only handle sizes up to 16 bit length, counting headroom + data length: "OSMO_ASSERT(size + headroom <= 0xffff);".
This new improved logic allows handling segments of up to ~UINT16_MAX which may come in in multiple read calls. It also adds logic to catch received segments greater than UINT16_MAX (or buggy segment_cb or buggy peer transmitting to us) and notifies the user of a broken stream through read_cb() in that case.
test_segmentation_uint16_max() is added to osmo_io_test, which would abort without this patch applied.
osmo_io: Propagate segment_cb errors to the read_cb
Previous logic was to drop an entire msgb in the stream and pretend it can continue working that way, which clearly makes no sense. Instead, if segment_cb detects some problem (eg. buggy peer sending corrupted data according to protocol), propagate the issue through read_cb() so that the app can act on it, ie. most likely close the connection.
Remove unused private API log_target_create_file_stream()
This function was never added to the logging.h (despite it was not static and it was inside libosmocore.map). It is actually used nowhere, and changing it to static made the compiler warn about it. Remove it.
Always build the public osmo_sock_multiaddr_* helper functions, independent of HAVE_LIBSCTP.
Move the HAVE_LIBSCTP checks inside the functions and limit them to the SCTP-specific code paths: when IPPROTO_SCTP is requested without libsctp support, return -ENOTSUP, while non-SCTP protocols continue to work as before.
This fixes link failures in libosmo-netif when building with --disable-libsctp.
Since --enable-embedded was reworked in 9197c1ac176ab8aa33f82e059404dcd482577a31, it is no longer needed to pass those flags explicitly for proper checking, since --enable-embedded preset takes care of enabling/disabling them and do the related checks.
gsm0808: fix wrong codec defaults for OFR_AMR_WB 3GPP TS 48.008 chapter 3.2.2.103 states that the configuration bits of OFR_AMR_WB are coded as follows: "S0, S2, S4 indicates the supported Codec Configurations. S1, S3, S5, S6, S7 are reserved and coded with zeroes." The current default setting of 0x3F violates this requirement. Lets set the "forbidden" settings zu zero and keep only the allowed ones. Change-Id: I4a481def59e9c98cfdcafc2b80c0ac7df0c14130
gsm0808: add function to convert amr gsm0408 setings to gsm0808 Add a function to convert struct gsm48_multi_rate_conf, which holds the codec settings for AMR, to S0-S15 bit representation as defined in 3GPP TS 48.008 3.2.2.49 Change-Id: I4e656731b16621736c7a2f4e64d9ce63b1064e98 Related: OS#3548