From 818a9e99a4bdbc6e3b12636ceff36759a778e848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Mon, 16 Jan 2023 14:26:14 +0100 Subject: [PATCH] OS: Add `unset_environment`, better validate input Instead of returning an undocumented boolean error code, we do the validation checks that should ensure a successful result. Based on: - https://linux.die.net/man/3/setenv - https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable --- core/core_bind.cpp | 11 ++++++++--- core/core_bind.h | 3 ++- core/os/os.h | 3 ++- doc/classes/OS.xml | 12 ++++++++++-- drivers/unix/os_unix.cpp | 19 +++++++++++++------ drivers/unix/os_unix.h | 4 +++- platform/windows/os_windows.cpp | 13 +++++++++++-- platform/windows/os_windows.h | 3 ++- 8 files changed, 51 insertions(+), 17 deletions(-) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index ab433bd8f1b..96e1da9dde3 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -322,8 +322,12 @@ String OS::get_environment(const String &p_var) const { return ::OS::get_singleton()->get_environment(p_var); } -bool OS::set_environment(const String &p_var, const String &p_value) const { - return ::OS::get_singleton()->set_environment(p_var, p_value); +void OS::set_environment(const String &p_var, const String &p_value) const { + ::OS::get_singleton()->set_environment(p_var, p_value); +} + +void OS::unset_environment(const String &p_var) const { + ::OS::get_singleton()->unset_environment(p_var); } String OS::get_name() const { @@ -548,9 +552,10 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running); ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); + ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); ClassDB::bind_method(D_METHOD("get_environment", "variable"), &OS::get_environment); ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &OS::set_environment); - ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); + ClassDB::bind_method(D_METHOD("unset_environment", "variable"), &OS::unset_environment); ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name); ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name); diff --git a/core/core_bind.h b/core/core_bind.h index 7ef346d1c41..c0c87fd0099 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -162,7 +162,8 @@ public: bool has_environment(const String &p_var) const; String get_environment(const String &p_var) const; - bool set_environment(const String &p_var, const String &p_value) const; + void set_environment(const String &p_var, const String &p_value) const; + void unset_environment(const String &p_var) const; String get_name() const; String get_distribution_name() const; diff --git a/core/os/os.h b/core/os/os.h index b80efa47b73..4818e9281a1 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -167,7 +167,8 @@ public: virtual bool has_environment(const String &p_var) const = 0; virtual String get_environment(const String &p_var) const = 0; - virtual bool set_environment(const String &p_var, const String &p_value) const = 0; + virtual void set_environment(const String &p_var, const String &p_value) const = 0; + virtual void unset_environment(const String &p_var) const = 0; virtual String get_name() const = 0; virtual String get_distribution_name() const = 0; diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 6dab7b4ebe0..1bc81ffb42d 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -592,12 +592,12 @@ - + Sets the value of the environment variable [param variable] to [param value]. The environment variable will be set for the Godot process and any process executed with [method execute] after running [method set_environment]. The environment variable will [i]not[/i] persist to processes run after the Godot process was terminated. - [b]Note:[/b] Double-check the casing of [param variable]. Environment variable names are case-sensitive on all platforms except Windows. + [b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character. On Windows, there is a 32767 characters limit for the combined length of [param variable], [param value], and the [code]=[/code] and null terminator characters that will be registered in the environment block. @@ -637,6 +637,14 @@ [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows. + + + + + Removes the environment [param variable] from the current environment, if it exists. The environment variable will be removed for the Godot process and any process executed with [method execute] after running [method unset_environment]. The removal of the environment variable will [i]not[/i] persist to processes run after the Godot process was terminated. + [b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character. + + diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index c37b3d9c873..178f01b1855 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -411,10 +411,6 @@ bool OS_Unix::is_process_running(const ProcessID &p_pid) const { return true; } -bool OS_Unix::has_environment(const String &p_var) const { - return getenv(p_var.utf8().get_data()) != nullptr; -} - String OS_Unix::get_locale() const { if (!has_environment("LANG")) { return "en"; @@ -487,6 +483,10 @@ Error OS_Unix::set_cwd(const String &p_cwd) { return OK; } +bool OS_Unix::has_environment(const String &p_var) const { + return getenv(p_var.utf8().get_data()) != nullptr; +} + String OS_Unix::get_environment(const String &p_var) const { if (getenv(p_var.utf8().get_data())) { return getenv(p_var.utf8().get_data()); @@ -494,8 +494,15 @@ String OS_Unix::get_environment(const String &p_var) const { return ""; } -bool OS_Unix::set_environment(const String &p_var, const String &p_value) const { - return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0; +void OS_Unix::set_environment(const String &p_var, const String &p_value) const { + ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var)); + int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1); + ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var)); +} + +void OS_Unix::unset_environment(const String &p_var) const { + ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var)); + unsetenv(p_var.utf8().get_data()); } String OS_Unix::get_user_data_dir() const { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 416311307cb..03429622ae3 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -81,7 +81,9 @@ public: virtual bool has_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override; - virtual bool set_environment(const String &p_var, const String &p_value) const override; + virtual void set_environment(const String &p_var, const String &p_value) const override; + virtual void unset_environment(const String &p_var) const override; + virtual String get_locale() const override; virtual void initialize_debugging() override; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index b3831573cfe..130c5f7b97b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1166,8 +1166,17 @@ String OS_Windows::get_environment(const String &p_var) const { return ""; } -bool OS_Windows::set_environment(const String &p_var, const String &p_value) const { - return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data())); +void OS_Windows::set_environment(const String &p_var, const String &p_value) const { + ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var)); + Char16String var = p_var.utf16(); + Char16String value = p_value.utf16(); + ERR_FAIL_COND_MSG(var.length() + value.length() + 2 > 32767, vformat("Invalid definition for environment variable '%s', cannot exceed 32767 characters.", p_var)); + SetEnvironmentVariableW((LPCWSTR)(var.get_data()), (LPCWSTR)(value.get_data())); +} + +void OS_Windows::unset_environment(const String &p_var) const { + ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var)); + SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), nullptr); // Null to delete. } String OS_Windows::get_stdin_string() { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c33e0f67406..05110c26140 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -186,7 +186,8 @@ public: virtual bool has_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override; - virtual bool set_environment(const String &p_var, const String &p_value) const override; + virtual void set_environment(const String &p_var, const String &p_value) const override; + virtual void unset_environment(const String &p_var) const override; virtual Vector get_system_fonts() const override; virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;