---
-## 6F. Remaining Minor POSIX Gaps (Priority: Low, Effort: Small — 1-2 days)
+## 6F. Remaining Minor POSIX Gaps (Priority: Low, Effort: Small — 1-2 days) — ✅ DONE
### Kernel syscalls:
| Syscall | Description | Effort |
---
-## 6G. Bash & Busybox Ports (Priority: High, Effort: Medium — 1-2 weeks)
+## 6G. Bash & Busybox Ports (Priority: High, Effort: Medium — 1-2 weeks) — ✅ DONE
**Current state:** All kernel/library blockers resolved. Native toolchain (GCC 13.2 +
Binutils 2.42) and Newlib port are complete.
| Priority | Sub-tier | Rationale |
|----------|----------|-----------|
-| 1 | **6G** Bash/Busybox | Highest user-visible impact, all blockers resolved |
-| 2 | **6F** Minor POSIX gaps | Quick wins, improves compatibility |
+| ~~1~~ | ~~**6G** Bash/Busybox~~ | ✅ DONE |
+| ~~2~~ | ~~**6F** Minor POSIX gaps~~ | ✅ DONE |
| ~~3~~ | ~~**6A** Full SMP~~ | ✅ DONE (commit 1374a6f) |
| 4 | **6C** Rump Kernel | Unlocks USB, better drivers |
| 5 | **6E** USTAR initrd | Small, improves developer experience |
| 6C | Rump Kernel integration | 4-6 weeks | Medium |
| 6D | Intel HDA audio driver | 1-2 weeks | Low |
| 6E | USTAR initrd format | 2-3 days | Low |
-| 6F | Minor POSIX gaps (madvise, mntent, utmp) | 1-2 days | Low |
-| 6G | Bash & Busybox ports | 1-2 weeks | High |
+| 6F | Minor POSIX gaps (madvise, mntent, utmp) | ✅ DONE | Low |
+| 6G | Bash & Busybox ports | ✅ DONE | High |
**Total estimated effort:** 12-22 weeks (if done sequentially; many items are parallel).
SYSCALL_GETRUSAGE = 137,
SYSCALL_UMOUNT2 = 138,
SYSCALL_WAIT4 = 139,
+ SYSCALL_MADVISE = 140,
+ SYSCALL_EXECVEAT = 141,
};
#endif
--- /dev/null
+# AdrOS Third-Party Ports
+
+Cross-compiled software packages for AdrOS using the `i686-adros` toolchain.
+
+## Prerequisites
+
+Build the AdrOS toolchain first:
+
+```bash
+./toolchain/build.sh
+export PATH=/opt/adros-toolchain/bin:$PATH
+```
+
+## Available Ports
+
+### Bash 5.2.21
+
+Built as part of the toolchain. See `toolchain/build.sh`.
+
+```bash
+./toolchain/build.sh # builds bash along with toolchain
+# Binary: toolchain/build/bash/bash
+```
+
+### Busybox 1.36.1
+
+Minimal set of ~60 UNIX utilities in a single static binary.
+
+```bash
+./ports/busybox/build.sh
+# Binary: ports/busybox/build/busybox
+# Applets installed to: ports/busybox/install/
+```
+
+## Adding to AdrOS initrd
+
+After building, copy binaries into the initrd staging area:
+
+```bash
+# Bash
+cp toolchain/build/bash/bash rootfs/bin/bash
+
+# Busybox — install as individual symlinks
+cp ports/busybox/build/busybox rootfs/bin/busybox
+for cmd in ls cat cp mv rm mkdir grep sed awk sort; do
+ ln -sf busybox rootfs/bin/$cmd
+done
+
+# Rebuild the ISO
+make iso
+```
+
+## Adding New Ports
+
+1. Create `ports/<name>/build.sh` with download + configure + build steps
+2. Use `i686-adros-gcc` with `-static` and `-D_POSIX_VERSION=200112L`
+3. Add a defconfig or patch directory as needed
+4. Document in this README
--- /dev/null
+#
+# AdrOS minimal Busybox configuration
+#
+# Start with this defconfig and expand as needed.
+# Build: make CROSS_COMPILE=i686-adros- O=build defconfig
+# cp adros_defconfig build/.config
+# make CROSS_COMPILE=i686-adros- O=build oldconfig
+#
+CONFIG_STATIC=y
+CONFIG_CROSS_COMPILER_PREFIX="i686-adros-"
+CONFIG_EXTRA_CFLAGS="-Os -D_POSIX_VERSION=200112L"
+CONFIG_EXTRA_LDFLAGS="-static"
+
+# ---- Core applets ----
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_BUILTIN_ECHO=y
+CONFIG_ASH_BUILTIN_PRINTF=y
+CONFIG_ASH_BUILTIN_TEST=y
+CONFIG_ASH_CMDCMD=y
+CONFIG_ASH_EXPAND_PRMT=y
+CONFIG_ASH_GETOPTS=y
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+
+# ---- File utilities ----
+CONFIG_LS=y
+CONFIG_CAT=y
+CONFIG_CP=y
+CONFIG_MV=y
+CONFIG_RM=y
+CONFIG_MKDIR=y
+CONFIG_RMDIR=y
+CONFIG_LN=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_TOUCH=y
+CONFIG_HEAD=y
+CONFIG_TAIL=y
+CONFIG_WC=y
+CONFIG_DD=y
+CONFIG_TEE=y
+CONFIG_STAT=y
+
+# ---- Text utilities ----
+CONFIG_GREP=y
+CONFIG_SED=y
+CONFIG_AWK=y
+CONFIG_SORT=y
+CONFIG_UNIQ=y
+CONFIG_CUT=y
+CONFIG_TR=y
+CONFIG_ECHO=y
+CONFIG_PRINTF=y
+CONFIG_YES=y
+CONFIG_TRUE=y
+CONFIG_FALSE=y
+
+# ---- Process utilities ----
+CONFIG_PS=y
+CONFIG_KILL=y
+CONFIG_SLEEP=y
+
+# ---- System utilities ----
+CONFIG_MOUNT=y
+CONFIG_UMOUNT=y
+CONFIG_UNAME=y
+CONFIG_HOSTNAME=y
+CONFIG_ID=y
+CONFIG_WHOAMI=y
+CONFIG_PWD=y
+CONFIG_BASENAME=y
+CONFIG_DIRNAME=y
+CONFIG_ENV=y
+CONFIG_PRINTENV=y
+CONFIG_TEST=y
+CONFIG_EXPR=y
+CONFIG_SEQ=y
+CONFIG_CLEAR=y
+CONFIG_RESET=y
+CONFIG_XARGS=y
+CONFIG_FIND=y
+CONFIG_WHICH=y
+
+# ---- Disable features not yet supported ----
+# CONFIG_FEATURE_EDITING is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+# CONFIG_FEATURE_NETWORKING is not set
+# CONFIG_IFCONFIG is not set
+# CONFIG_PING is not set
+# CONFIG_WGET is not set
+# CONFIG_TELNET is not set
+# CONFIG_SYSLOGD is not set
+# CONFIG_KLOGD is not set
+# CONFIG_INIT is not set
+# CONFIG_HALT is not set
+# CONFIG_REBOOT is not set
+# CONFIG_POWEROFF is not set
--- /dev/null
+#!/usr/bin/env bash
+#
+# AdrOS — Busybox cross-compile script
+#
+# Builds a minimal Busybox for AdrOS using the i686-adros toolchain.
+#
+# Prerequisites:
+# - AdrOS toolchain built (toolchain/build.sh)
+# - PATH includes /opt/adros-toolchain/bin
+#
+# Usage:
+# ./ports/busybox/build.sh [--prefix /opt/adros-toolchain] [--jobs 4]
+#
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ADROS_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+
+# ---- Defaults ----
+PREFIX="/opt/adros-toolchain"
+TARGET="i686-adros"
+JOBS="$(nproc 2>/dev/null || echo 4)"
+BUSYBOX_VER="1.36.1"
+BUSYBOX_URL="https://busybox.net/downloads/busybox-${BUSYBOX_VER}.tar.bz2"
+
+SRC_DIR="$ADROS_ROOT/ports/busybox/src"
+BUILD_DIR="$ADROS_ROOT/ports/busybox/build"
+LOG_DIR="$ADROS_ROOT/ports/busybox/logs"
+DEFCONFIG="$SCRIPT_DIR/adros_defconfig"
+
+# ---- Parse args ----
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --prefix) PREFIX="$2"; shift 2 ;;
+ --jobs) JOBS="$2"; shift 2 ;;
+ --help|-h)
+ echo "Usage: $0 [--prefix DIR] [--jobs N]"
+ exit 0 ;;
+ *) echo "Unknown option: $1"; exit 1 ;;
+ esac
+done
+
+export PATH="$PREFIX/bin:$PATH"
+
+msg() { echo -e "\n\033[1;34m==> $1\033[0m"; }
+step() { echo " [OK] $1"; }
+die() { echo -e "\033[1;31mERROR: $1\033[0m" >&2; exit 1; }
+
+# Verify toolchain
+command -v "${TARGET}-gcc" >/dev/null 2>&1 || die "Toolchain not found. Run toolchain/build.sh first."
+
+# ---- Download ----
+mkdir -p "$SRC_DIR" "$BUILD_DIR" "$LOG_DIR"
+
+if [[ ! -d "$SRC_DIR/busybox-${BUSYBOX_VER}" ]]; then
+ msg "Downloading Busybox ${BUSYBOX_VER}..."
+ TARBALL="$SRC_DIR/busybox-${BUSYBOX_VER}.tar.bz2"
+ if [[ ! -f "$TARBALL" ]]; then
+ wget -q -O "$TARBALL" "$BUSYBOX_URL" || die "Download failed"
+ fi
+ tar xf "$TARBALL" -C "$SRC_DIR"
+ step "Busybox source extracted"
+fi
+
+# ---- Configure ----
+msg "Configuring Busybox..."
+cd "$BUILD_DIR"
+
+BUSYBOX_SRC="$SRC_DIR/busybox-${BUSYBOX_VER}"
+
+if [[ -f "$DEFCONFIG" ]]; then
+ cp "$DEFCONFIG" "$BUSYBOX_SRC/.config"
+ make -C "$BUSYBOX_SRC" O="$BUILD_DIR" oldconfig \
+ CROSS_COMPILE="${TARGET}-" \
+ 2>&1 | tee "$LOG_DIR/busybox-configure.log"
+else
+ # Generate minimal defconfig
+ make -C "$BUSYBOX_SRC" O="$BUILD_DIR" defconfig \
+ CROSS_COMPILE="${TARGET}-" \
+ 2>&1 | tee "$LOG_DIR/busybox-configure.log"
+fi
+
+step "Busybox configured"
+
+# ---- Build ----
+msg "Building Busybox..."
+make -C "$BUILD_DIR" -j"$JOBS" \
+ CROSS_COMPILE="${TARGET}-" \
+ CFLAGS="-Os -static -D_POSIX_VERSION=200112L" \
+ LDFLAGS="-static" \
+ 2>&1 | tee "$LOG_DIR/busybox-build.log"
+
+step "Busybox built: $BUILD_DIR/busybox"
+
+# ---- Install to initrd staging area ----
+msg "Installing Busybox applets..."
+INSTALL_DIR="$ADROS_ROOT/ports/busybox/install"
+rm -rf "$INSTALL_DIR"
+make -C "$BUILD_DIR" install \
+ CROSS_COMPILE="${TARGET}-" \
+ CONFIG_PREFIX="$INSTALL_DIR" \
+ 2>&1 | tee "$LOG_DIR/busybox-install.log"
+
+step "Busybox installed to $INSTALL_DIR"
+
+# ---- Summary ----
+echo ""
+echo "Busybox build complete!"
+echo ""
+echo " Binary: $BUILD_DIR/busybox"
+echo " Install: $INSTALL_DIR"
+echo ""
+echo " To add to AdrOS initrd:"
+echo " cp $BUILD_DIR/busybox rootfs/bin/"
+echo " # Create symlinks for desired applets"
+echo ""
+echo " Applets included:"
+ls "$INSTALL_DIR/bin/" 2>/dev/null | head -20
+echo " ..."
return;
}
+ if (syscall_no == SYSCALL_MADVISE) {
+ /* madvise — advisory only, always succeed (no-op) */
+ sc_ret(regs) = 0;
+ return;
+ }
+
+ if (syscall_no == SYSCALL_EXECVEAT) {
+ int dirfd = (int)sc_arg0(regs);
+ const char* user_path = (const char*)sc_arg1(regs);
+ const char* const* user_argv = (const char* const*)sc_arg2(regs);
+ const char* const* user_envp = (const char* const*)sc_arg3(regs);
+ /* uint32_t flags = sc_arg4(regs); -- AT_EMPTY_PATH etc, ignored */
+
+ /* Only AT_FDCWD supported for now */
+ if (dirfd != -100 /* AT_FDCWD */) {
+ sc_ret(regs) = (uint32_t)-ENOSYS;
+ return;
+ }
+
+ int r = syscall_execve_impl(regs, user_path, user_argv, user_envp);
+ if (r < 0) sc_ret(regs) = (uint32_t)r;
+ return;
+ }
+
sc_ret(regs) = (uint32_t)-ENOSYS;
}
{"readdir /bin" "\\[init\\] readdir /bin OK"}
{"gettimeofday" "\\[init\\] gettimeofday OK"}
{"mprotect" "\\[init\\] mprotect OK"}
+ {"madvise" "\\[init\\] madvise OK"}
{"getrlimit/setrlimit" "\\[init\\] getrlimit/setrlimit OK"}
{"uname" "\\[init\\] uname OK"}
{"SMP parallel fork" "\\[init\\] SMP parallel fork OK"}
SYSCALL_GETRLIMIT = 129,
SYSCALL_SETRLIMIT = 130,
SYSCALL_UNAME = 136,
+ SYSCALL_MADVISE = 140,
};
enum {
return __syscall_fix(ret);
}
+static int sys_madvise(uintptr_t addr, uint32_t len, uint32_t advice) {
+ int ret;
+ __asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_MADVISE), "b"(addr), "c"(len), "d"(advice) : "memory");
+ return __syscall_fix(ret);
+}
+
static int sys_getrlimit(int resource, struct rlimit* rlim) {
int ret;
__asm__ volatile("int $0x80" : "=a"(ret) : "a"(SYSCALL_GETRLIMIT), "b"(resource), "c"(rlim) : "memory");
}
}
+ /* ---- madvise test ---- */
+ {
+ int r = sys_madvise(0, 4096, 0 /* MADV_NORMAL */);
+ if (r == 0) {
+ static const char msg[] = "[init] madvise OK\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ } else {
+ static const char msg[] = "[init] madvise failed\n";
+ (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
+ }
+ }
+
/* ---- getrlimit/setrlimit test ---- */
{
struct rlimit rl;
--- /dev/null
+#ifndef ULIBC_MNTENT_H
+#define ULIBC_MNTENT_H
+
+#include <stdio.h>
+
+struct mntent {
+ char* mnt_fsname; /* Device or server for filesystem */
+ char* mnt_dir; /* Directory mounted on */
+ char* mnt_type; /* Type of filesystem: ufs, nfs, etc. */
+ char* mnt_opts; /* Comma-separated options for fs */
+ int mnt_freq; /* Dump frequency (in days) */
+ int mnt_passno; /* Pass number for `fsck` */
+};
+
+FILE* setmntent(const char* filename, const char* type);
+struct mntent* getmntent(FILE* fp);
+int addmntent(FILE* fp, const struct mntent* mnt);
+int endmntent(FILE* fp);
+char* hasmntopt(const struct mntent* mnt, const char* opt);
+
+#endif
int getc(FILE* fp);
int putc(int c, FILE* fp);
-typedef long ssize_t;
+#include <sys/types.h>
ssize_t getline(char** lineptr, size_t* n, FILE* stream);
ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream);
#define MAP_FAILED ((void*)-1)
+/* madvise advice values */
+#define MADV_NORMAL 0
+#define MADV_RANDOM 1
+#define MADV_SEQUENTIAL 2
+#define MADV_WILLNEED 3
+#define MADV_DONTNEED 4
+
void* mmap(void* addr, size_t length, int prot, int flags, int fd, int offset);
int munmap(void* addr, size_t length);
int mprotect(void* addr, size_t len, int prot);
+int madvise(void* addr, size_t length, int advice);
#endif
SYS_GETRUSAGE = 137,
SYS_UMOUNT2 = 138,
SYS_WAIT4 = 139,
+ SYS_MADVISE = 140,
+ SYS_EXECVEAT = 141,
};
/* Raw syscall wrappers — up to 5 args via INT 0x80 */
char* ttyname(int fd);
int pipe2(int fds[2], int flags);
int execle(const char* path, const char* arg, ...);
+int execveat(int dirfd, const char* path, char* const argv[], char* const envp[], int flags);
char* getlogin(void);
int getlogin_r(char* buf, size_t bufsize);
int tcgetpgrp(int fd);
--- /dev/null
+#ifndef ULIBC_UTMP_H
+#define ULIBC_UTMP_H
+
+#include <stdint.h>
+
+#define UT_LINESIZE 32
+#define UT_NAMESIZE 32
+#define UT_HOSTSIZE 256
+
+#define EMPTY 0
+#define RUN_LVL 1
+#define BOOT_TIME 2
+#define NEW_TIME 3
+#define OLD_TIME 4
+#define INIT_PROCESS 5
+#define LOGIN_PROCESS 6
+#define USER_PROCESS 7
+#define DEAD_PROCESS 8
+
+struct utmp {
+ short ut_type;
+ int ut_pid;
+ char ut_line[UT_LINESIZE];
+ char ut_id[4];
+ char ut_user[UT_NAMESIZE];
+ char ut_host[UT_HOSTSIZE];
+ int32_t ut_tv_sec;
+ int32_t ut_tv_usec;
+};
+
+void setutent(void);
+struct utmp* getutent(void);
+struct utmp* getutid(const struct utmp* ut);
+struct utmp* getutline(const struct utmp* ut);
+struct utmp* pututline(const struct utmp* ut);
+void endutent(void);
+void utmpname(const char* file);
+
+#endif
int mprotect(void* addr, size_t len, int prot) {
return __syscall_ret(_syscall3(SYS_MPROTECT, (int)addr, (int)len, prot));
}
+
+int madvise(void* addr, size_t length, int advice) {
+ return __syscall_ret(_syscall3(SYS_MADVISE, (int)addr, (int)length, advice));
+}
--- /dev/null
+#include "mntent.h"
+#include "stdio.h"
+#include "string.h"
+#include "stdlib.h"
+
+static struct mntent _mnt;
+static char _mnt_buf[512];
+
+FILE* setmntent(const char* filename, const char* type) {
+ return fopen(filename, type);
+}
+
+struct mntent* getmntent(FILE* fp) {
+ if (!fp) return NULL;
+
+ while (fgets(_mnt_buf, (int)sizeof(_mnt_buf), fp)) {
+ /* Skip comments and blank lines */
+ if (_mnt_buf[0] == '#' || _mnt_buf[0] == '\n' || _mnt_buf[0] == '\0')
+ continue;
+
+ /* Remove trailing newline */
+ size_t len = strlen(_mnt_buf);
+ if (len > 0 && _mnt_buf[len - 1] == '\n')
+ _mnt_buf[len - 1] = '\0';
+
+ /* Parse: fsname dir type opts freq passno */
+ char* p = _mnt_buf;
+ _mnt.mnt_fsname = p;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ while (*p == ' ' || *p == '\t') p++;
+
+ _mnt.mnt_dir = p;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ while (*p == ' ' || *p == '\t') p++;
+
+ _mnt.mnt_type = p;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ while (*p == ' ' || *p == '\t') p++;
+
+ _mnt.mnt_opts = p;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p) *p++ = '\0';
+ while (*p == ' ' || *p == '\t') p++;
+
+ _mnt.mnt_freq = atoi(p);
+ while (*p && *p != ' ' && *p != '\t') p++;
+ while (*p == ' ' || *p == '\t') p++;
+
+ _mnt.mnt_passno = atoi(p);
+
+ return &_mnt;
+ }
+
+ return NULL;
+}
+
+int addmntent(FILE* fp, const struct mntent* mnt) {
+ if (!fp || !mnt) return 1;
+ fprintf(fp, "%s %s %s %s %d %d\n",
+ mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type,
+ mnt->mnt_opts, mnt->mnt_freq, mnt->mnt_passno);
+ return 0;
+}
+
+int endmntent(FILE* fp) {
+ if (fp) fclose(fp);
+ return 1; /* always returns 1 per POSIX */
+}
+
+char* hasmntopt(const struct mntent* mnt, const char* opt) {
+ if (!mnt || !mnt->mnt_opts || !opt) return NULL;
+ return strstr(mnt->mnt_opts, opt);
+}
return __syscall_ret(_syscall3(SYS_EXECVE, (int)path, (int)argv, (int)envp));
}
+int execveat(int dirfd, const char* path, char* const argv[], char* const envp[], int flags) {
+ return __syscall_ret(_syscall5(SYS_EXECVEAT, dirfd, (int)path, (int)argv, (int)envp, flags));
+}
+
int getpid(void) {
return _syscall0(SYS_GETPID);
}
--- /dev/null
+#include "utmp.h"
+#include <stddef.h>
+
+/* Stub implementation — AdrOS does not maintain utmp/wtmp records yet.
+ * These stubs satisfy link-time dependencies for programs like login, w, who. */
+
+static struct utmp _utmp_entry;
+
+void setutent(void) {
+ /* no-op */
+}
+
+struct utmp* getutent(void) {
+ return NULL; /* no entries */
+}
+
+struct utmp* getutid(const struct utmp* ut) {
+ (void)ut;
+ return NULL;
+}
+
+struct utmp* getutline(const struct utmp* ut) {
+ (void)ut;
+ return NULL;
+}
+
+struct utmp* pututline(const struct utmp* ut) {
+ if (!ut) return NULL;
+ _utmp_entry = *ut;
+ return &_utmp_entry;
+}
+
+void endutent(void) {
+ /* no-op */
+}
+
+void utmpname(const char* file) {
+ (void)file;
+}