diff --git a/.gitignore b/.gitignore index d7e911e..146337c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,8 @@ programs/src/*/obj/ programs/libs/*/obj/ programs/lib/libc/liblibc.a programs/lib/libc/liblibc-full.a -programs/lib/libc/obj/libc-merged.o +programs/lib/libc/*.o +programs/lib/libc/obj/ programs/lib/libloader/liblibloader.a programs/gui/icons/ CLAUDE.md diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 1c68e2b..888cadb 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 104 +#define MONTAUK_BUILD_NUMBER 106 diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp index 1e10ce1..c52f30a 100644 --- a/kernel/src/Api/Process.hpp +++ b/kernel/src/Api/Process.hpp @@ -43,14 +43,16 @@ namespace montauk::abi { return Sched::LookupExitCode(pid); } - static int Sys_Spawn(const char* path, const char* args) { + static int Sys_Spawn(const char* path, const char* args, + const char* environment = nullptr, uint32_t environmentLength = 0) { char resolved[256]; if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1; auto* parent = Sched::GetCurrentProcessPtr(); int parentSlot = Ipc::CurrentSlot(); bool inheritRedirection = parent && parent->redirected; - int childPid = Sched::Spawn(resolved, args, !inheritRedirection); + int childPid = Sched::Spawn(resolved, args, !inheritRedirection, + environment, environmentLength); if (childPid < 0) return childPid; if (inheritRedirection) { @@ -107,6 +109,24 @@ namespace montauk::abi { return i; } + static int Sys_GetEnviron(char* buf, uint64_t maxLen) { + auto* proc = Sched::GetCurrentProcessPtr(); + if (proc == nullptr || buf == nullptr || maxLen == 0) return -1; + if (maxLen < proc->environmentLength) return -2; + for (uint32_t i = 0; i < proc->environmentLength; i++) + buf[i] = proc->environment[i]; + return (int)proc->environmentLength; + } + + static int Sys_SetEnviron(const char* buf, uint64_t len) { + auto* proc = Sched::GetCurrentProcessPtr(); + if (proc == nullptr || buf == nullptr || len == 0 || + len > Sched::Process::EnvironmentBytes || buf[len - 1] != '\0') return -1; + for (uint64_t i = 0; i < len; i++) proc->environment[i] = buf[i]; + proc->environmentLength = (uint32_t)len; + return 0; + } + static int Sys_ProcList(ProcInfo* buf, int maxCount) { if (buf == nullptr || maxCount <= 0) return 0; int count = 0; diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index c76a6da..8aa3417 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -149,8 +149,25 @@ namespace montauk::abi { if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; return (int64_t)Sys_Spawn((const char*)frame->arg1, UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr); + case SYS_SPAWN_ENV: + if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1; + if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1; + if (frame->arg3 == 0 || frame->arg4 == 0 || + frame->arg4 > Sched::Process::EnvironmentBytes || + !UserMemory::Range(frame->arg3, frame->arg4, false) || + ((const char*)frame->arg3)[frame->arg4 - 1] != '\0') return -1; + return (int64_t)Sys_Spawn((const char*)frame->arg1, + UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr, + (const char*)frame->arg3, (uint32_t)frame->arg4); case SYS_WAITPID: return Sys_WaitPid((int)frame->arg1); + case SYS_GETENVIRON: + if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1; + return Sys_GetEnviron((char*)frame->arg1, frame->arg2); + case SYS_SETENVIRON: + if (frame->arg2 == 0 || frame->arg2 > Sched::Process::EnvironmentBytes || + !UserMemory::Range(frame->arg1, frame->arg2, false)) return -1; + return Sys_SetEnviron((const char*)frame->arg1, frame->arg2); case SYS_FBINFO: if (!UserMemory::Writable(frame->arg1)) return -1; Sys_FbInfo((FbInfo*)frame->arg1); diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index 1f58638..f5caa9f 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -315,6 +315,9 @@ namespace montauk::abi { static constexpr uint64_t SYS_MMAP_ANON = 168; static constexpr uint64_t SYS_MUNMAP = 169; static constexpr uint64_t SYS_MPROTECT = 170; + static constexpr uint64_t SYS_GETENVIRON = 171; + static constexpr uint64_t SYS_SETENVIRON = 172; + static constexpr uint64_t SYS_SPAWN_ENV = 173; // Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM). static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index 4fbf76f..9806a91 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -294,6 +294,8 @@ namespace Sched { processTable[i].heapNext = 0; processTable[i].readdirCursor = 0; processTable[i].args[0] = '\0'; + processTable[i].environment[0] = '\0'; + processTable[i].environmentLength = 1; processTable[i].user[0] = '\0'; processTable[i].cwd[0] = '\0'; processTable[i].runningOnCpu = -1; @@ -337,7 +339,8 @@ namespace Sched { << " process slots, " << (uint64_t)TimeSliceMs << " ms time slice)"; } - int Spawn(const char* vfsPath, const char* args, bool startReady) { + int Spawn(const char* vfsPath, const char* args, bool startReady, + const char* environment, uint32_t environmentLength) { schedLock.Acquire(); int slot = -1; @@ -511,6 +514,9 @@ namespace Sched { auto* currentCpu = Smp::GetCurrentCpuData(); int parentSlot = currentCpu ? currentCpu->currentSlot : -1; + int parentPrimarySlot = parentSlot; + if (parentPrimarySlot >= 0 && processTable[parentPrimarySlot].primarySlot >= 0) + parentPrimarySlot = processTable[parentPrimarySlot].primarySlot; // Copy arguments string into process proc.args[0] = '\0'; @@ -522,6 +528,23 @@ namespace Sched { proc.args[i] = '\0'; } + // posix_spawn(envp) supplies an explicit snapshot. Plain spawn calls + // omit it and inherit the environment stored on the parent process. + if (environment != nullptr && environmentLength > 0 && + environmentLength <= Process::EnvironmentBytes) { + for (uint32_t i = 0; i < environmentLength; i++) + proc.environment[i] = environment[i]; + proc.environmentLength = environmentLength; + } else if (parentPrimarySlot >= 0) { + auto& parent = processTable[parentPrimarySlot]; + for (uint32_t i = 0; i < parent.environmentLength; i++) + proc.environment[i] = parent.environment[i]; + proc.environmentLength = parent.environmentLength; + } else { + proc.environment[0] = '\0'; + proc.environmentLength = 1; + } + // Inherit user string from parent, or default to "system" if no parent { if (parentSlot >= 0) { diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index 6d3add1..95e3b45 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -65,6 +65,7 @@ namespace Sched { }; struct Process { + static constexpr uint32_t EnvironmentBytes = 4096; int pid; ProcessState state; int waitingForPid; // PID this process is blocked on (-1 if none) @@ -82,6 +83,8 @@ namespace Sched { uint64_t heapNext; // Simple bump allocator for user heap uint32_t readdirCursor; // Next SYS_READDIR scratch slot char args[4096]; // Command-line arguments (set by parent via Spawn) + char environment[EnvironmentBytes]; // NUL-separated NAME=VALUE entries + uint32_t environmentLength; char user[32]; // Owner user name (inherited from parent on spawn) char cwd[256]; // Absolute current working directory @@ -139,7 +142,8 @@ namespace Sched { }; void Initialize(); - int Spawn(const char* vfsPath, const char* args = nullptr, bool startReady = true); + int Spawn(const char* vfsPath, const char* args = nullptr, bool startReady = true, + const char* environment = nullptr, uint32_t environmentLength = 0); int StartProcess(int pid); void Schedule(); diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index f42c81f..5f20e52 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -233,6 +233,9 @@ namespace montauk::abi { static constexpr uint64_t SYS_MMAP_ANON = 168; static constexpr uint64_t SYS_MUNMAP = 169; static constexpr uint64_t SYS_MPROTECT = 170; + static constexpr uint64_t SYS_GETENVIRON = 171; + static constexpr uint64_t SYS_SETENVIRON = 172; + static constexpr uint64_t SYS_SPAWN_ENV = 173; // Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM). static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz diff --git a/programs/include/libc/montauk.h b/programs/include/libc/montauk.h index 777f13c..fd7a63b 100644 --- a/programs/include/libc/montauk.h +++ b/programs/include/libc/montauk.h @@ -73,6 +73,9 @@ extern "C" { #define MTK_SYS_MOUSESTATE 47 #define MTK_SYS_SETMOUSEBOUNDS 48 #define MTK_SYS_SPAWN_REDIR 49 +#define MTK_SYS_GETENVIRON 171 +#define MTK_SYS_SETENVIRON 172 +#define MTK_SYS_SPAWN_ENV 173 #define MTK_SYS_CHILDIO_READ 50 #define MTK_SYS_CHILDIO_WRITE 51 #define MTK_SYS_CHILDIO_WRITEKEY 52 diff --git a/programs/include/libc/spawn.h b/programs/include/libc/spawn.h index bfe17bc..75a69d5 100644 --- a/programs/include/libc/spawn.h +++ b/programs/include/libc/spawn.h @@ -15,7 +15,7 @@ extern "C" { * Limitations (kernel spawn model): * - argv is joined into a single args string; arguments containing * spaces are rejected with EINVAL (no quoting in the kernel). - * - envp is ignored (no environment transfer on spawn). + * - envp is copied into the child; NULL inherits the current environment. * - file actions must be empty: stdio redirection needs kernel * support that does not exist yet, so any recorded action makes * posix_spawn fail with ENOTSUP rather than misbehave silently. diff --git a/programs/lib/libc/crt/crt1.c b/programs/lib/libc/crt/crt1.c index b1b6014..f23743b 100644 --- a/programs/lib/libc/crt/crt1.c +++ b/programs/lib/libc/crt/crt1.c @@ -34,8 +34,10 @@ static inline long _sys2(long nr, long a1, long a2) { #define SYS_EXIT 0 #define SYS_GETARGS 25 #define SYS_GETEXECPATH 151 +#define SYS_GETENVIRON 171 extern int main(int argc, char** argv); +extern void __libc_init_environ(const char *blob, unsigned long len); /* Global constructor/destructor tables. The linker provides these boundary symbols whenever .preinit_array/.init_array/.fini_array @@ -72,9 +74,13 @@ static void _run_fini_array(void) { void _start(void) { /* Static: 4 KiB args + 256 argv slots would crowd a 32 KiB stack. */ static char argbuf[4096]; + static char envbuf[4096]; static char pathbuf[256]; static char* argv[256]; int len = (int)_sys2(SYS_GETARGS, (long)argbuf, (long)sizeof(argbuf)); + int envlen = (int)_sys2(SYS_GETENVIRON, (long)envbuf, (long)sizeof(envbuf)); + if (envlen > 0) + __libc_init_environ(envbuf, (unsigned long)envlen); int argc = 0; diff --git a/programs/lib/libc/crt1.o b/programs/lib/libc/crt1.o deleted file mode 100644 index 762ab27..0000000 Binary files a/programs/lib/libc/crt1.o and /dev/null differ diff --git a/programs/lib/libc/crti.o b/programs/lib/libc/crti.o deleted file mode 100644 index 85cb644..0000000 Binary files a/programs/lib/libc/crti.o and /dev/null differ diff --git a/programs/lib/libc/crtn.o b/programs/lib/libc/crtn.o deleted file mode 100644 index 3321508..0000000 Binary files a/programs/lib/libc/crtn.o and /dev/null differ diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index 03e02f7..a49da25 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -123,6 +123,9 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) { #define SYS_MMAP_ANON 168 #define SYS_MUNMAP 169 #define SYS_MPROTECT 170 +#define SYS_GETENVIRON 171 +#define SYS_SETENVIRON 172 +#define SYS_SPAWN_ENV 173 /* ======================================================================== errno @@ -164,9 +167,15 @@ struct _DIR { struct _env_entry { char *name; char *value; + char *string; }; static struct _env_entry _env_entries[64]; +static char *_environ_vector[65]; +char **environ = _environ_vector; +static int _environ_initialized; +static int _environ_loading; +void __libc_init_environ(const char *blob, size_t len); static sighandler_t _signal_handlers[32]; static const char *_weekday_short[] = { @@ -354,6 +363,39 @@ static int _alloc_env_slot(void) { return -1; } +static void _rebuild_environ(void) { + int out = 0; + for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) { + if (_env_entries[i].string != NULL) + _environ_vector[out++] = _env_entries[i].string; + } + _environ_vector[out] = NULL; +} + +static int _sync_environ_to_kernel(void) { + static char blob[4096]; + size_t out = 0; + for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) { + if (_env_entries[i].string == NULL) continue; + size_t len = strlen(_env_entries[i].string) + 1; + if (out + len >= sizeof(blob)) return -1; + memcpy(blob + out, _env_entries[i].string, len); + out += len; + } + blob[out++] = '\0'; + return _zos_syscall2(SYS_SETENVIRON, (long)blob, (long)out) < 0 ? -1 : 0; +} + +static void _ensure_environ_initialized(void) { + if (_environ_initialized) return; + static char blob[4096]; + int len = (int)_zos_syscall2(SYS_GETENVIRON, (long)blob, (long)sizeof(blob)); + if (len > 0) + __libc_init_environ(blob, (size_t)len); + else + _environ_initialized = 1; +} + /* ======================================================================== string.h functions ======================================================================== */ @@ -1053,6 +1095,7 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) { char *getenv(const char *name) { if (name == NULL || name[0] == '\0') return NULL; + _ensure_environ_initialized(); int slot = _find_env_slot(name); if (slot < 0) return NULL; @@ -1064,36 +1107,58 @@ int setenv(const char *name, const char *value, int overwrite) { errno = EINVAL; return -1; } + _ensure_environ_initialized(); int slot = _find_env_slot(name); if (slot >= 0 && !overwrite) { return 0; } + size_t required = 1 + strlen(name) + strlen(value) + 2; /* final NUL, '=', entry NUL */ + for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) { + if (i != slot && _env_entries[i].string != NULL) + required += strlen(_env_entries[i].string) + 1; + } + if (required >= 4096) { + errno = E2BIG; + return -1; + } + if (slot < 0) { slot = _alloc_env_slot(); if (slot < 0) { errno = ENOMEM; return -1; } - } else { - free(_env_entries[slot].name); - free(_env_entries[slot].value); - _env_entries[slot].name = NULL; - _env_entries[slot].value = NULL; } - _env_entries[slot].name = strdup(name); - _env_entries[slot].value = strdup(value); - if (_env_entries[slot].name == NULL || _env_entries[slot].value == NULL) { - free(_env_entries[slot].name); - free(_env_entries[slot].value); - _env_entries[slot].name = NULL; - _env_entries[slot].value = NULL; + char *new_name = strdup(name); + char *new_value = strdup(value); + char *new_string = (char *)malloc(strlen(name) + strlen(value) + 2); + if (new_name == NULL || new_value == NULL || new_string == NULL) { + free(new_name); + free(new_value); + free(new_string); errno = ENOMEM; return -1; } + strcpy(new_string, name); + strcat(new_string, "="); + strcat(new_string, value); + free(_env_entries[slot].name); + free(_env_entries[slot].value); + free(_env_entries[slot].string); + _env_entries[slot].name = new_name; + _env_entries[slot].value = new_value; + _env_entries[slot].string = new_string; + _rebuild_environ(); + + if (!_environ_loading && _sync_environ_to_kernel() < 0) { + errno = EIO; + return -1; + } + return 0; } @@ -1102,14 +1167,22 @@ int unsetenv(const char *name) { errno = EINVAL; return -1; } + _ensure_environ_initialized(); int slot = _find_env_slot(name); if (slot < 0) return 0; free(_env_entries[slot].name); free(_env_entries[slot].value); + free(_env_entries[slot].string); _env_entries[slot].name = NULL; _env_entries[slot].value = NULL; + _env_entries[slot].string = NULL; + _rebuild_environ(); + if (_sync_environ_to_kernel() < 0) { + errno = EIO; + return -1; + } return 0; } @@ -4114,7 +4187,6 @@ int posix_spawn(pid_t *pid, const char *path, const posix_spawnattr_t *attr, char *const argv[], char *const envp[]) { (void)attr; - (void)envp; /* no environment transfer on Montauk spawn */ if (path == NULL) { return EINVAL; @@ -4148,7 +4220,24 @@ int posix_spawn(pid_t *pid, const char *path, } argsbuf[o] = '\0'; - long child = _zos_syscall2(SYS_SPAWN, (long)path, (long)argsbuf); + long child; + if (envp == NULL) { + child = _zos_syscall2(SYS_SPAWN, (long)path, (long)argsbuf); + } else { + static char envbuf[4096]; + size_t envlen = 0; + for (int i = 0; envp[i] != NULL; i++) { + const char *eq = strchr(envp[i], '='); + if (eq == NULL || eq == envp[i]) return EINVAL; + size_t len = strlen(envp[i]) + 1; + if (envlen + len >= sizeof(envbuf)) return E2BIG; + memcpy(envbuf + envlen, envp[i], len); + envlen += len; + } + envbuf[envlen++] = '\0'; + child = _zos_syscall4(SYS_SPAWN_ENV, (long)path, (long)argsbuf, + (long)envbuf, (long)envlen); + } if (child < 0) { return ENOENT; } @@ -4192,12 +4281,37 @@ int posix_spawnp(pid_t *pid, const char *file, return ENOENT; } -/* The libc environment lives in name/value slots (getenv/setenv), not - NAME=VALUE strings, and exec* is unimplemented, so nothing can - consume a populated environ yet. An empty, valid vector satisfies - POSIX-shaped code until the posix_spawn work builds the real one. */ -static char *_environ_empty[] = { NULL }; -char **environ = _environ_empty; +/* Called by crt1 before constructors. The kernel stores the inherited + environment as consecutive NAME=VALUE strings followed by an extra NUL. */ +void __libc_init_environ(const char *blob, size_t len) { + if (_environ_initialized || blob == NULL || len == 0) return; + _environ_initialized = 1; + _environ_loading = 1; + + size_t off = 0; + while (off < len && blob[off] != '\0') { + size_t end = off; + while (end < len && blob[end] != '\0') end++; + if (end == len) break; + + size_t eq = off; + while (eq < end && blob[eq] != '=') eq++; + if (eq > off && eq < end) { + size_t name_len = eq - off; + char *name = (char *)malloc(name_len + 1); + if (name == NULL) break; + memcpy(name, blob + off, name_len); + name[name_len] = '\0'; + if (setenv(name, blob + eq + 1, 1) < 0) { + free(name); + break; + } + free(name); + } + off = end + 1; + } + _environ_loading = 0; +} /* No fork/exec process model on Montauk (spawn-style only, see SYS_SPAWN). These exist so POSIX code links; they fail cleanly at diff --git a/programs/lib/libc/obj/libc.o b/programs/lib/libc/obj/libc.o deleted file mode 100644 index dceae4e..0000000 Binary files a/programs/lib/libc/obj/libc.o and /dev/null differ diff --git a/programs/lib/libc/obj/printf_float.o b/programs/lib/libc/obj/printf_float.o deleted file mode 100644 index a7bbae9..0000000 Binary files a/programs/lib/libc/obj/printf_float.o and /dev/null differ diff --git a/programs/lib/libc/obj/setjmp.o b/programs/lib/libc/obj/setjmp.o deleted file mode 100644 index 77d999e..0000000 Binary files a/programs/lib/libc/obj/setjmp.o and /dev/null differ diff --git a/programs/man/spawn.2 b/programs/man/spawn.2 index decb85e..4d14864 100644 --- a/programs/man/spawn.2 +++ b/programs/man/spawn.2 @@ -59,6 +59,10 @@ The argument string is set by the parent when calling spawn(). If no arguments were provided, the buffer will be empty. + A spawned child inherits a snapshot of its parent's environment. Libc + exposes it through getenv(), setenv(), and environ. posix_spawn() can + instead provide an explicit envp for the child. + .SH NOTES The _start() entry point receives no argc/argv. Use getargs() to retrieve the argument string passed by the parent process. diff --git a/programs/man/syscalls.2 b/programs/man/syscalls.2 index e24d26b..ac74158 100644 --- a/programs/man/syscalls.2 +++ b/programs/man/syscalls.2 @@ -39,7 +39,8 @@ int montauk::getpid(); .B SYS_SPAWN (20) - Spawn a new process from an ELF binary on the VFS. + Spawn a new process from an ELF binary on the VFS. The child inherits + a snapshot of the caller's environment. int montauk::spawn(const char* path, const char* args = nullptr); .B SYS_WAITPID (23) @@ -50,6 +51,11 @@ Get the argument string passed to this process at spawn time. int montauk::getargs(char* buf, uint64_t maxLen); +.B SYS_GETENVIRON (171), SYS_SETENVIRON (172), SYS_SPAWN_ENV (173) + Libc process-environment transport. Environment data is encoded as + consecutive NAME=VALUE strings with a final empty string. Applications + normally use getenv(3), setenv(3), environ, and posix_spawn(3). + .B SYS_PROCLIST (61) List running processes (pid, parent, state, name, heap usage, accumulated CPU time). diff --git a/programs/src/tcc/montauk_main.c b/programs/src/tcc/montauk_main.c index ec1e39f..e2727df 100644 --- a/programs/src/tcc/montauk_main.c +++ b/programs/src/tcc/montauk_main.c @@ -39,6 +39,9 @@ static inline long _sys2(long nr, long a1, long a2) { #define SYS_GETSIZE 8 #define SYS_GETARGS 25 +#define SYS_GETENVIRON 171 + +void __libc_init_environ(const char *blob, unsigned long len); /* fdopen, strtoll and strtoull used to be defined here; they moved into the Montauk libc with the native binutils port. */ @@ -50,6 +53,11 @@ static inline long _sys2(long nr, long a1, long a2) { int main(int argc, char **argv); void _start(void) { + static char envbuf[4096]; + int envlen = (int)_sys2(SYS_GETENVIRON, (long)envbuf, sizeof(envbuf)); + if (envlen > 0) + __libc_init_environ(envbuf, (unsigned long)envlen); + /* Get command-line arguments from kernel */ char argbuf[256]; int len = (int)_sys2(SYS_GETARGS, (long)argbuf, sizeof(argbuf)); diff --git a/template/docs/syscalls.md b/template/docs/syscalls.md index 4bd03b4..b1e5496 100644 --- a/template/docs/syscalls.md +++ b/template/docs/syscalls.md @@ -32,7 +32,7 @@ void exit(int code); // Terminate process (nore void yield(); // Yield CPU to scheduler void sleep_ms(uint64_t ms); // Sleep for milliseconds int getpid(); // Get current process ID -int spawn(const char* path, const char* args); // Spawn child process (-1 on error) +int spawn(const char* path, const char* args); // Spawn child, inheriting environment int waitpid(int pid); // Wait for process to exit int kill(int pid); // Kill a process int proclist(ProcInfo* buf, int max); // List all processes (returns count) diff --git a/template/sysroot/include/Api/Syscall.hpp b/template/sysroot/include/Api/Syscall.hpp index 9fc1f28..9fb8c76 100644 --- a/template/sysroot/include/Api/Syscall.hpp +++ b/template/sysroot/include/Api/Syscall.hpp @@ -208,6 +208,9 @@ namespace montauk::abi { // Absolute path of the running executable (for argv[0]). static constexpr uint64_t SYS_GETEXECPATH = 151; // (index, flags) -> new front index; index=-1 queries support (1/0); flags bit0 = wait vsync + static constexpr uint64_t SYS_GETENVIRON = 171; + static constexpr uint64_t SYS_SETENVIRON = 172; + static constexpr uint64_t SYS_SPAWN_ENV = 173; // Path metadata (size, timestamps, mode). (const char* path, FileStat* out) -> 0, -1 on error/unsupported. static constexpr uint64_t SYS_STAT = 152; diff --git a/template/sysroot/include/libc/montauk.h b/template/sysroot/include/libc/montauk.h index 66b95ca..1b57154 100644 --- a/template/sysroot/include/libc/montauk.h +++ b/template/sysroot/include/libc/montauk.h @@ -73,6 +73,9 @@ extern "C" { #define MTK_SYS_MOUSESTATE 47 #define MTK_SYS_SETMOUSEBOUNDS 48 #define MTK_SYS_SPAWN_REDIR 49 +#define MTK_SYS_GETENVIRON 171 +#define MTK_SYS_SETENVIRON 172 +#define MTK_SYS_SPAWN_ENV 173 #define MTK_SYS_CHILDIO_READ 50 #define MTK_SYS_CHILDIO_WRITE 51 #define MTK_SYS_CHILDIO_WRITEKEY 52 diff --git a/template/sysroot/include/libc/spawn.h b/template/sysroot/include/libc/spawn.h index bfe17bc..75a69d5 100644 --- a/template/sysroot/include/libc/spawn.h +++ b/template/sysroot/include/libc/spawn.h @@ -15,7 +15,7 @@ extern "C" { * Limitations (kernel spawn model): * - argv is joined into a single args string; arguments containing * spaces are rejected with EINVAL (no quoting in the kernel). - * - envp is ignored (no environment transfer on spawn). + * - envp is copied into the child; NULL inherits the current environment. * - file actions must be empty: stdio redirection needs kernel * support that does not exist yet, so any recorded action makes * posix_spawn fail with ENOTSUP rather than misbehave silently. diff --git a/toolchain/patches/gcc-14.2.0-montauk.patch b/toolchain/patches/gcc-14.2.0-montauk.patch index d5f732b..8976d15 100644 --- a/toolchain/patches/gcc-14.2.0-montauk.patch +++ b/toolchain/patches/gcc-14.2.0-montauk.patch @@ -66,12 +66,12 @@ prefix_from_env ("PATH", &path); +#ifdef __montauk__ -+ /* MontaukOS does not pass environment variables across spawn yet, so -+ COMPILER_PATH and PATH above are always empty. Derive the compiler -+ search path from argv[0] instead: the driver invokes collect2 by its -+ full path (the kernel provides the real exec path in argv[0]), and -+ `ld` lives in the same libexec directory. Also seed PATH with the -+ SDK bin directories so the nm/strip/tooldir fallbacks work. */ ++ /* Keep a fallback for early boot and deliberately minimal environments. ++ Derive the compiler search path from argv[0]: the driver invokes ++ collect2 by its full path (the kernel provides the real exec path in ++ argv[0]), and `ld` lives in the same libexec directory. Also seed PATH ++ with the SDK bin directories when the caller did not provide one. */ ++ + if (cpath.plist == NULL && argv[0] != NULL) + { + const char *slash = strrchr (argv[0], '/');