/* narOS kernel identity shim (NAROS.md §2.3).
 *
 * narOS does not build its own kernel: containers share the host's (on Cloudflare
 * Containers that is a Firecracker microVM kernel, whose release string carries a
 * `-cloudflare-firecracker` suffix) and the VM tiers boot an externally-fetched
 * vmlinux (scripts/fetch-kernel.sh). So there is no CONFIG_LOCALVERSION to set, and
 * every uname(2)-derived identity — including the `OS Version:` line agent harnesses
 * put in their environment block, which is os.type() + os.release() — reports the
 * host kernel with no narOS in it at all, no matter what /etc/os-release says.
 *
 * This interposer, preloaded via /etc/ld.so.preload, appends the narOS release tag to
 * utsname.release the way a distro kernel package does (Debian's own kernels report
 * `6.1.0-18-amd64`), so the identity is true at the syscall layer rather than only in
 * files a caller has to know to read.
 *
 * Deliberately narrow: `sysname` stays "Linux". It is the single most-switched-on
 * uname field in build tooling — autoconf's config.guess, CMAKE_SYSTEM_NAME, node-gyp,
 * the Go and rustup installers all compare it against "Linux" and fall through to
 * "unsupported platform" otherwise. `version` is likewise untouched.
 *
 * Truth comes from syscall(SYS_uname) rather than dlsym(RTLD_NEXT): a library in
 * /etc/ld.so.preload is loaded into *every* dynamically-linked process on the system,
 * including early boot and dpkg's own maintainer scripts, so it must not depend on
 * libdl being resolvable or risk recursing through an interposed symbol.
 *
 * Escape hatch: NAROS_UNAME_PASSTHROUGH=1 returns the kernel's answer verbatim. This
 * exists because /lib/modules/`uname -r` is a real path — kmod, depmod and udev resolve
 * modules through it, so the VM tiers need the untagged release to find their external
 * kernel's modules (see nucleic-modsetup.service, which also symlinks the tagged name
 * onto the real one so autoloading works without the env var).
 *
 * Statically linked binaries, and Go programs that issue the raw syscall themselves,
 * bypass this by construction — the tag is an identity marker, never a security or
 * correctness boundary.
 */
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <unistd.h>

/* Baked at build time from os/VERSION, e.g. "-naros26.07". */
#ifndef NAROS_KERNEL_TAG
#define NAROS_KERNEL_TAG "-naros"
#endif

static int naros_passthrough(void)
{
    const char *v = getenv("NAROS_UNAME_PASSTHROUGH");
    return v != NULL && *v != '\0' && strcmp(v, "0") != 0;
}

int uname(struct utsname *buf)
{
    long rc = syscall(SYS_uname, buf);
    if (rc != 0 || buf == NULL)
        return (int)rc;
    if (naros_passthrough())
        return 0;
    /* Idempotent: a re-exec through another preloading process must not stack tags. */
    if (strstr(buf->release, NAROS_KERNEL_TAG) != NULL)
        return 0;

    size_t have = strnlen(buf->release, sizeof(buf->release));
    size_t tag = sizeof(NAROS_KERNEL_TAG) - 1;
    /* utsname.release is a fixed 65-byte field; leave it untagged rather than truncate
     * the real kernel release, which callers parse for version comparisons. */
    if (have + tag + 1 > sizeof(buf->release))
        return 0;
    memcpy(buf->release + have, NAROS_KERNEL_TAG, tag + 1);
    return 0;
}
