From 967758d4644c545b89b97c2ba2becdd42e42eac9 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 30 Apr 2023 14:46:34 +0200 Subject: [PATCH] libc: Implement setenv() and unsetenv() --- apps/su.cpp | 6 ++ libc/include/stdlib.h | 6 ++ libc/src/env.cpp | 182 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 174 insertions(+), 20 deletions(-) diff --git a/apps/su.cpp b/apps/su.cpp index 7bb3176f..7aa36f34 100644 --- a/apps/su.cpp +++ b/apps/su.cpp @@ -92,8 +92,14 @@ Result luna_main(int argc, char** argv) setuid(entry->pw_uid); chdir(entry->pw_dir); + if (setenv("HOME", entry->pw_dir, 1) < 0) + { + perror("setenv"); + return 1; + } execl(entry->pw_shell, entry->pw_shell, NULL); + perror("execl"); return 1; } diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index 799fbc1c..65b72f95 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -106,6 +106,12 @@ extern "C" /* Get the value of an environment variable. */ char* getenv(const char* key); + /* Set the value of an environment variable. */ + int setenv(const char* key, const char* value, int overwrite); + + /* Remove a variable from the environment. */ + int unsetenv(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/src/env.cpp b/libc/src/env.cpp index 93505aa0..c666d6a7 100644 --- a/libc/src/env.cpp +++ b/libc/src/env.cpp @@ -1,31 +1,173 @@ -#include +#include +#include + +#include +#include + +extern "C" char** environ; +Vector g_dynamic_env = {}; +bool env_is_dynamic { false }; + +/* If an environment variable matching key is found, stores its value in value and returns its index. Otherwise, + * returns -1. */ +static isize _findenv(const char* key, char** value) +{ + 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)) + { + if (value) *value = delim + 1; + return (env - environ) - 1; + } + } + + return -1; +} + +/* Move environ to a heap-allocated array. */ +static Result _try_move_env() +{ + char** env = environ; + g_dynamic_env.clear(); + auto guard = make_scope_guard([] { + for (auto element : g_dynamic_env) + { + if (element) free(element); + } + }); + + while (*env) + { + char* ptr = strdup(*(env++)); + if (!ptr) return err(errno); + + auto ptr_guard = make_scope_guard([=] { free(ptr); }); + TRY(g_dynamic_env.try_append(ptr)); + ptr_guard.deactivate(); + } + + TRY(g_dynamic_env.try_append(nullptr)); + + guard.deactivate(); + + env_is_dynamic = true; + environ = g_dynamic_env.data(); + + return {}; +} + +static int _move_env() +{ + auto rc = _try_move_env(); + if (rc.has_error()) + { + errno = rc.error(); + return -1; + } + return 0; +} + +static void _update_env() +{ + environ = g_dynamic_env.data(); +} extern "C" { char** environ = nullptr; - char* getenv(const char* key) + int unsetenv(const char* key) { - char** env = environ; - size_t len = strlen(key); - - while (*env) + if (!key || *key == 0 || strchr(key, '=')) { - // 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; } + errno = EINVAL; + return -1; } - return nullptr; + auto index = _findenv(key, nullptr); + if (index < 0) return 0; + + if (!env_is_dynamic) + { + if (_move_env() < 0) return -1; + } + + g_dynamic_env.remove_at(index); + _update_env(); + + return 0; + } + + int setenv(const char* key, const char* value, int overwrite) + { + if (!key || *key == 0 || strchr(key, '=')) + { + errno = EINVAL; + return -1; + } + + auto index = _findenv(key, nullptr); + if (index >= 0 && !overwrite) return 0; + + if (!env_is_dynamic) + { + if (_move_env() < 0) return -1; + } + + usize len = strlen(key) + strlen(value) + 2; + + char* str = (char*)calloc(len, 1); + if (!str) return -1; + auto guard = make_scope_guard([=] { free(str); }); + + strncpy(str, key, strlen(key) + 1); + strncat(str, "=", 2); + strncat(str, value, strlen(value) + 1); + + if (index >= 0) + { + free(environ[index]); + environ[index] = str; + guard.deactivate(); + return 0; + } + + // Add a new NULL at the end of the array and replace the previous one with our string. + index = g_dynamic_env.size() - 1; + + auto rc = g_dynamic_env.try_append(nullptr); + if (rc.has_error()) + { + errno = rc.error(); + return -1; + } + guard.deactivate(); + _update_env(); + + environ[index] = str; + + return 0; + } + + char* getenv(const char* key) + { + char* result; + if (_findenv(key, &result) < 0) return nullptr; + return result; } }