diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index f4f5cab6..77022246 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -57,6 +57,8 @@ namespace Kernel int block_until_exit(); BAN::ErrorOr wait(pid_t pid, int* stat_loc, int options); + BAN::ErrorOr setenvp(char** envp); + BAN::ErrorOr open(BAN::StringView, int); BAN::ErrorOr close(int fd); BAN::ErrorOr read(int fd, void* buffer, size_t count); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index b69637a9..98b757f0 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -389,6 +389,13 @@ namespace Kernel return ret; } + BAN::ErrorOr Process::setenvp(char** envp) + { + LockGuard _(m_lock); + m_userspace_info.envp = envp; + return {}; + } + void Process::load_elf(LibELF::ELF& elf) { ASSERT(elf.is_native()); diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index 034c52aa..51505a42 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -136,6 +136,14 @@ namespace Kernel return 0; } + long sys_setenvp(char** envp) + { + auto ret = Process::current().setenvp(envp); + if (ret.is_error()) + return -ret.error().get_error_code(); + return 0; + } + extern "C" long sys_fork_trampoline(); extern "C" long cpp_syscall_handler(int syscall, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5) @@ -204,6 +212,9 @@ namespace Kernel case SYS_STAT: ret = sys_stat((const char*)arg1, (struct stat*)arg2, (int)arg3); break; + case SYS_SETENVP: + ret = sys_setenvp((char**)arg1); + break; default: Kernel::panic("Unknown syscall {}", syscall); } diff --git a/libc/include/sys/syscall.h b/libc/include/sys/syscall.h index 787ef2aa..ad8b5cb3 100644 --- a/libc/include/sys/syscall.h +++ b/libc/include/sys/syscall.h @@ -23,6 +23,7 @@ __BEGIN_DECLS #define SYS_REALLOC 16 #define SYS_WAIT 17 #define SYS_STAT 18 +#define SYS_SETENVP 19 __END_DECLS diff --git a/libc/stdlib.cpp b/libc/stdlib.cpp index a04c16c6..3c614dc7 100644 --- a/libc/stdlib.cpp +++ b/libc/stdlib.cpp @@ -7,6 +7,8 @@ #include #include +extern "C" char** environ; + extern "C" void _fini(); void abort(void) @@ -54,11 +56,119 @@ int atoi(const char* str) return negative ? -res : res; } -char* getenv(const char*) +char* getenv(const char* name) { + if (environ == nullptr) + return nullptr; + size_t len = strlen(name); + for (int i = 0; environ[i]; i++) + if (strncmp(name, environ[i], len) == 0) + if (environ[i][len] == '=') + return environ[i] + len + 1; return nullptr; } +int setenv(const char* name, const char* val, int overwrite) +{ + if (name == nullptr || !name[0] || strchr(name, '=')) + { + errno = EINVAL; + return -1; + } + + if (!overwrite && getenv(name)) + return 0; + + size_t namelen = strlen(name); + size_t vallen = strlen(val); + + char* string = (char*)malloc(namelen + vallen + 2); + memcpy(string, name, namelen); + string[namelen] = '='; + memcpy(string + namelen + 1, val, vallen); + string[namelen + vallen + 1] = '\0'; + + return putenv(string); +} + +int unsetenv(const char* name) +{ + if (name == nullptr || !name[0] || strchr(name, '=')) + { + errno = EINVAL; + return -1; + } + + size_t len = strlen(name); + + bool found = false; + for (int i = 0; environ[i]; i++) + { + if (!found && strncmp(environ[i], name, len) == 0 && environ[i][len] == '=') + { + free(environ[i]); + found = true; + } + if (found) + environ[i] = environ[i + 1]; + } + + return 0; +} + +int putenv(char* string) +{ + if (string == nullptr || !string[0]) + { + errno = EINVAL; + return -1; + } + + int cnt = 0; + for (int i = 0; string[i]; i++) + if (string[i] == '=') + cnt++; + if (cnt != 1) + { + errno = EINVAL; + return -1; + } + + int namelen = strchr(string, '=') - string; + for (int i = 0; environ[i]; i++) + { + if (strncmp(environ[i], string, namelen + 1) == 0) + { + free(environ[i]); + environ[i] = string; + return 0; + } + } + + int env_count = 0; + while (environ[env_count]) + env_count++; + + char** new_envp = (char**)malloc(sizeof(char*) * (env_count + 2)); + if (new_envp == nullptr) + { + errno = ENOMEM; + return -1; + } + + for (int i = 0; i < env_count; i++) + new_envp[i] = environ[i]; + new_envp[env_count] = string; + new_envp[env_count + 1] = nullptr; + + free(environ); + environ = new_envp; + + if (syscall(SYS_SETENVP, environ) == -1) + return -1; + return 0; +} + void* malloc(size_t bytes) { long res = syscall(SYS_ALLOC, bytes); diff --git a/libc/unistd.cpp b/libc/unistd.cpp index d7071841..32553888 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -157,6 +157,12 @@ long syscall(long syscall, ...) ret = Kernel::syscall(SYS_STAT, (uintptr_t)path, (uintptr_t)buf, flags); break; } + case SYS_SETENVP: + { + char** envp = va_arg(args, char**); + ret = Kernel::syscall(SYS_SETENVP, (uintptr_t)envp); + break; + } default: puts("LibC: Unhandeled syscall"); ret = -ENOSYS;