From 3a28771520fb681b30282c4460a4342a02834543 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 7 Apr 2023 15:03:38 +0200 Subject: [PATCH] kernel+libc+apps: Add support for environment variables --- apps/CMakeLists.txt | 1 + apps/env.cpp | 9 +++++++++ apps/init.cpp | 3 ++- kernel/src/sys/exec.cpp | 12 +++++++++--- kernel/src/thread/Scheduler.cpp | 9 +++++++++ libc/CMakeLists.txt | 1 + libc/include/stdlib.h | 3 ++- libc/include/unistd.h | 4 +++- libc/src/arch/x86_64/crt0.S | 2 ++ libc/src/env.cpp | 31 +++++++++++++++++++++++++++++++ libc/src/init.cpp | 8 +++++++- libc/src/unistd.cpp | 9 ++++++++- libluna/include/luna/Syscall.h | 2 +- 13 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 apps/env.cpp create mode 100644 libc/src/env.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index cbd54d8f..48c143e3 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -8,6 +8,7 @@ endfunction() luna_app(init.cpp init) luna_app(sh.cpp sh) +luna_app(env.cpp env) luna_app(cat.cpp cat) target_link_libraries(cat PRIVATE os) diff --git a/apps/env.cpp b/apps/env.cpp new file mode 100644 index 00000000..2db3431b --- /dev/null +++ b/apps/env.cpp @@ -0,0 +1,9 @@ +#include + +extern char** environ; + +int main() +{ + char** env = environ; + while (*env) { puts(*(env++)); } +} diff --git a/apps/init.cpp b/apps/init.cpp index 5062fba1..4ed10a06 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -43,7 +43,8 @@ int main() if (ret == 0) { char* argv[] = { "/bin/sh", NULL }; - execv(argv[0], argv); + char* envp[] = { "PATH=/bin:/sbin", NULL }; + execve(argv[0], argv, envp); perror("execv"); return 1; } diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 9cef5d2e..31684d7d 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -31,12 +31,13 @@ static Result> copy_string_vector_from_userspace(u64 address) return result; } -Result sys_exec(Registers* regs, SyscallArgs args) +Result sys_execve(Registers* regs, SyscallArgs args) { auto path = TRY(MemoryManager::strdup_from_user(args[0])); auto argv = TRY(copy_string_vector_from_userspace(args[1])); + auto envp = TRY(copy_string_vector_from_userspace(args[2])); - // FIXME: Make sure argv is not too big. + // FIXME: Make sure argv & envp are not too big. auto inode = TRY(VFS::resolve_path(path.chars())); @@ -56,6 +57,11 @@ Result sys_exec(Registers* regs, SyscallArgs args) u64 user_argv = TRY(image->push_string_vector_on_stack(argv)); usize user_argc = argv.size(); + kdbgln("exec: copying envp to image memory (envc = %zu)", envp.size()); + + u64 user_envp = TRY(image->push_string_vector_on_stack(envp)); + usize user_envc = envp.size(); + // From now on, nothing should fail. kinfoln("exec: image load ok, will now replace existing process image"); @@ -77,7 +83,7 @@ Result sys_exec(Registers* regs, SyscallArgs args) MMU::switch_page_directory(current->directory); - current->set_arguments(user_argc, user_argv, 0, 0); + current->set_arguments(user_argc, user_argv, user_envc, user_envp); memcpy(regs, ¤t->regs, sizeof(*regs)); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 41865055..afd5aadb 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -115,13 +115,22 @@ namespace Scheduler thread->name = name; thread->parent_id = 0; + Vector args; + auto name_string = TRY(String::from_cstring(name)); + TRY(args.try_append(move(name_string))); + + Vector env; + auto guard = make_scope_guard([&] { delete thread; }); auto image = TRY(ThreadImage::try_load_from_elf(inode)); + u64 argv = TRY(image->push_string_vector_on_stack(args)); + u64 envp = TRY(image->push_string_vector_on_stack(env)); guard.deactivate(); image->apply(thread); + thread->set_arguments(args.size(), argv, env.size(), envp); kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(), thread->sp(), thread->kernel_stack.top()); diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 11e12f98..e63b4626 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES src/init.cpp src/dirent.cpp src/setjmp.cpp + src/env.cpp src/sys/stat.cpp src/sys/mman.cpp src/sys/wait.cpp diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index 3799a971..9393eedd 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -102,7 +102,8 @@ extern "C" int system(const char*); - char* getenv(const char*); + /* Get the value of an environment variable. */ + char* getenv(const char* key); void qsort(void*, size_t, size_t, int (*)(const void*, const void*)); void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*)); diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 8944b8bb..859ba4bb 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -34,7 +34,9 @@ extern "C" /* Replace the current process with another one. On success, does not return. */ int execl(const char* path, const char* arg, ...); - int execve(const char*, char* const*, char* const*); + /* Replace the current process with another one. On success, does not return. */ + int execve(const char* path, char* const* argv, char* const* envp); + int execvp(const char*, char* const*); /* Call the operating system kernel for a specific service. */ diff --git a/libc/src/arch/x86_64/crt0.S b/libc/src/arch/x86_64/crt0.S index b8e3ae98..a128c99a 100644 --- a/libc/src/arch/x86_64/crt0.S +++ b/libc/src/arch/x86_64/crt0.S @@ -12,12 +12,14 @@ _start: pushq %rsi pushq %rdi + pushq %rcx call libc_init # Run the global constructors. call _init + popq %rdx popq %rdi popq %rsi diff --git a/libc/src/env.cpp b/libc/src/env.cpp new file mode 100644 index 00000000..93505aa0 --- /dev/null +++ b/libc/src/env.cpp @@ -0,0 +1,31 @@ +#include + +extern "C" +{ + char** environ = nullptr; + + char* getenv(const char* key) + { + char** env = environ; + size_t len = strlen(key); + + while (*env) + { + // Retrieve an environment variable from environ. + char* var = *(env++); + + // Check for an equals sign, else we just skip this one. + char* delim = strchr(var, '='); + if (!delim) continue; + + // Get the length of this variable's key (the part before the equals sign) + size_t key_length = strcspn(var, "="); + if (len != key_length) continue; + + // If the keys match, we found it! Return the value (which is just after the equals sign) + if (!memcmp(key, var, key_length)) { return delim + 1; } + } + + return nullptr; + } +} diff --git a/libc/src/init.cpp b/libc/src/init.cpp index 3c98b054..a035db2a 100644 --- a/libc/src/init.cpp +++ b/libc/src/init.cpp @@ -1,10 +1,16 @@ +#include #include #include +extern char** environ; + extern "C" { - void libc_init() + void libc_init(int argc, char** argv, int envc, char** envp) { + ignore(argc, argv, envc); + environ = envp; + stdin = fdopen(STDIN_FILENO, "r"); stdout = fdopen(STDOUT_FILENO, "w"); stderr = fdopen(STDERR_FILENO, "w"); diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index f4bc4ee4..423bf17b 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -9,6 +9,8 @@ extern "C" long arch_invoke_syscall(long, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); +extern char** environ; + extern "C" { pid_t fork(void) @@ -29,7 +31,12 @@ extern "C" int execv(const char* path, char* const* argv) { - long rc = syscall(SYS_exec, path, argv); + return execve(path, argv, environ); + } + + int execve(const char* path, char* const* argv, char* const* envp) + { + long rc = syscall(SYS_execve, path, argv, envp); __errno_return(rc, int); } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 1d00a4a4..113a00db 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -2,7 +2,7 @@ #define enumerate_syscalls(_e) \ _e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \ - _e(lseek) _e(mkdir) _e(exec) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) + _e(lseek) _e(mkdir) _e(execve) _e(mknod) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) enum Syscalls {