From: Tulio A M Mendes Date: Sat, 14 Mar 2026 13:58:06 +0000 (-0300) Subject: feat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=2d2769ed0ccd36773b0b0368da0e26a1b8d2f45d;p=AdrOS.git feat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure Tier 6F — Minor POSIX gaps: - madvise kernel syscall (no-op, always succeeds) + ulibc wrapper - execveat kernel syscall (AT_FDCWD mode, delegates to execve) + ulibc wrapper - mntent.h + mntent.c: setmntent/getmntent/addmntent/endmntent/hasmntopt with full /etc/fstab-style whitespace-delimited parsing - utmp.h + utmp.c: setutent/getutent/getutid/getutline/pututline/endutent stubs for login record compatibility (Bash, w, who, login) - Fix duplicate ssize_t typedef: stdio.h now includes sys/types.h instead of redefining ssize_t as 'long' (was conflicting with int32_t in types.h) - MADV_NORMAL/RANDOM/SEQUENTIAL/WILLNEED/DONTNEED constants in sys/mman.h - New smoke test: madvise (103/103 pass) Tier 6G — Bash & Busybox port infrastructure: - Bash: already integrated in toolchain/build.sh (cross-compiles with i686-adros-gcc, static, config.cache for cross-compilation) - Busybox: ports/busybox/build.sh — download, configure, cross-compile script with adros_defconfig (~60 applets: ash, ls, cat, cp, grep, sed, awk, sort, ps, kill, mount, find, xargs, etc.) - ports/busybox/adros_defconfig — minimal static config for AdrOS - ports/README.md — documentation for building and installing ports Tests: 103/103 smoke, cppcheck clean, 64/64 host tests --- diff --git a/docs/TIER6_PLAN.md b/docs/TIER6_PLAN.md index 9cce898..8978aa5 100644 --- a/docs/TIER6_PLAN.md +++ b/docs/TIER6_PLAN.md @@ -130,7 +130,7 @@ non-standard. --- -## 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 | @@ -155,7 +155,7 @@ non-standard. --- -## 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. @@ -178,8 +178,8 @@ 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 | @@ -197,7 +197,7 @@ Binutils 2.42) and Newlib port are complete. | 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). diff --git a/include/syscall.h b/include/syscall.h index 78b5f0c..4086a8f 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -170,6 +170,8 @@ enum { SYSCALL_GETRUSAGE = 137, SYSCALL_UMOUNT2 = 138, SYSCALL_WAIT4 = 139, + SYSCALL_MADVISE = 140, + SYSCALL_EXECVEAT = 141, }; #endif diff --git a/ports/README.md b/ports/README.md new file mode 100644 index 0000000..1200e73 --- /dev/null +++ b/ports/README.md @@ -0,0 +1,58 @@ +# 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//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 diff --git a/ports/busybox/adros_defconfig b/ports/busybox/adros_defconfig new file mode 100644 index 0000000..ae0aa5c --- /dev/null +++ b/ports/busybox/adros_defconfig @@ -0,0 +1,99 @@ +# +# 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 diff --git a/ports/busybox/build.sh b/ports/busybox/build.sh new file mode 100755 index 0000000..463cbdd --- /dev/null +++ b/ports/busybox/build.sh @@ -0,0 +1,119 @@ +#!/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 " ..." diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index d12f6fa..bb7cfb9 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -4978,6 +4978,30 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) 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; } diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index 4c7551c..bb9bd90 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -141,6 +141,7 @@ set tests { {"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"} diff --git a/user/fulltest.c b/user/fulltest.c index 4ee9f0f..f491441 100644 --- a/user/fulltest.c +++ b/user/fulltest.c @@ -148,6 +148,7 @@ enum { SYSCALL_GETRLIMIT = 129, SYSCALL_SETRLIMIT = 130, SYSCALL_UNAME = 136, + SYSCALL_MADVISE = 140, }; enum { @@ -1348,6 +1349,12 @@ static int sys_mprotect(uintptr_t addr, uint32_t len, uint32_t prot) { 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"); @@ -4275,6 +4282,18 @@ void _start(void) { } } + /* ---- 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; diff --git a/user/ulibc/include/mntent.h b/user/ulibc/include/mntent.h new file mode 100644 index 0000000..662f1f6 --- /dev/null +++ b/user/ulibc/include/mntent.h @@ -0,0 +1,21 @@ +#ifndef ULIBC_MNTENT_H +#define ULIBC_MNTENT_H + +#include + +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 diff --git a/user/ulibc/include/stdio.h b/user/ulibc/include/stdio.h index 3cc79f5..c504894 100644 --- a/user/ulibc/include/stdio.h +++ b/user/ulibc/include/stdio.h @@ -77,7 +77,7 @@ int ungetc(int c, FILE* fp); int getc(FILE* fp); int putc(int c, FILE* fp); -typedef long ssize_t; +#include ssize_t getline(char** lineptr, size_t* n, FILE* stream); ssize_t getdelim(char** lineptr, size_t* n, int delim, FILE* stream); diff --git a/user/ulibc/include/sys/mman.h b/user/ulibc/include/sys/mman.h index d74c53d..f0964c9 100644 --- a/user/ulibc/include/sys/mman.h +++ b/user/ulibc/include/sys/mman.h @@ -15,8 +15,16 @@ #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 diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h index 8bd6cba..6182d2e 100644 --- a/user/ulibc/include/syscall.h +++ b/user/ulibc/include/syscall.h @@ -142,6 +142,8 @@ enum { 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 */ diff --git a/user/ulibc/include/unistd.h b/user/ulibc/include/unistd.h index 57ca07f..ab2c301 100644 --- a/user/ulibc/include/unistd.h +++ b/user/ulibc/include/unistd.h @@ -102,6 +102,7 @@ int gethostname(char* name, size_t len); 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); diff --git a/user/ulibc/include/utmp.h b/user/ulibc/include/utmp.h new file mode 100644 index 0000000..2de9a0a --- /dev/null +++ b/user/ulibc/include/utmp.h @@ -0,0 +1,39 @@ +#ifndef ULIBC_UTMP_H +#define ULIBC_UTMP_H + +#include + +#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 diff --git a/user/ulibc/src/mman.c b/user/ulibc/src/mman.c index 1aaf17b..0a062c5 100644 --- a/user/ulibc/src/mman.c +++ b/user/ulibc/src/mman.c @@ -19,3 +19,7 @@ int munmap(void* addr, size_t length) { 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)); +} diff --git a/user/ulibc/src/mntent.c b/user/ulibc/src/mntent.c new file mode 100644 index 0000000..30c28ae --- /dev/null +++ b/user/ulibc/src/mntent.c @@ -0,0 +1,76 @@ +#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); +} diff --git a/user/ulibc/src/unistd.c b/user/ulibc/src/unistd.c index e4c6d25..ab4bc1e 100644 --- a/user/ulibc/src/unistd.c +++ b/user/ulibc/src/unistd.c @@ -44,6 +44,10 @@ int execve(const char* path, const char* const* argv, const char* const* envp) { 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); } diff --git a/user/ulibc/src/utmp.c b/user/ulibc/src/utmp.c new file mode 100644 index 0000000..ec14d3b --- /dev/null +++ b/user/ulibc/src/utmp.c @@ -0,0 +1,39 @@ +#include "utmp.h" +#include + +/* 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; +}