From 028d76e5d58f1545f29b76c39e18c5b5da0c16e3 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 22 Sep 2019 16:02:28 +0900 Subject: env: use putenv_s for LC_ALL, LANG, etc. #11050 Problem: ":lang messages en_US.UTF-8" no longer overrides the language detected from the environment (at startup). Solution: In os_setenv, special-case "LC_ALL", "LANG", et al. to use putenv_s instead of uv_os_setenv. fixes #11045 --- src/nvim/os/env.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index f5dbf0694e..54fdd7961c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -135,7 +135,16 @@ int os_setenv(const char *name, const char *value, int overwrite) } #endif uv_mutex_lock(&mutex); - int r = uv_os_setenv(name, value); + int r; +#ifdef WIN32 + // libintl uses getenv() for LC_ALL/LANG/etc., so we must use _putenv_s(). + if (striequal(name, "LC_ALL") || striequal(name, "LANGUAGE") + || striequal(name, "LANG") || striequal(name, "LC_MESSAGES")) { + r = _putenv_s(name, value); // NOLINT + assert(r == 0); + } +#endif + r = uv_os_setenv(name, value); assert(r != UV_EINVAL); // Destroy the old map item. Do this AFTER uv_os_setenv(), because `value` // could be a previous os_getenv() result. -- cgit From bb6b1267e7532e0c2e065c4e9b5552623062c70f Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 11 Sep 2019 13:48:09 +0200 Subject: Revert "win/os_env_exists(): workaround libuv bug #10734" This reverts commit 278c5d452c2cbc436a9cc317407ae6021a226c3a. --- src/nvim/os/env.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 54fdd7961c..13853016d1 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -102,9 +102,6 @@ bool os_env_exists(const char *name) assert(r != UV_EINVAL); if (r != 0 && r != UV_ENOENT && r != UV_ENOBUFS) { ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); -#ifdef WIN32 - return (r == UV_UNKNOWN); -#endif } return (r == 0 || r == UV_ENOBUFS); } -- cgit From b4ea09cc064034f43808662c40cc1fff14284432 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sat, 5 Oct 2019 00:18:24 +0900 Subject: Fix potential deadlock #11151 ELOG may call os_getenv and os_setenv internally. In that case, a deadlock occurs. --- src/nvim/os/env.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 13853016d1..ae61e54993 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -55,6 +55,7 @@ const char *os_getenv(const char *name) return NULL; } uv_mutex_lock(&mutex); + int r = 0; if (pmap_has(cstr_t)(envmap, name) && !!(e = (char *)pmap_get(cstr_t)(envmap, name))) { if (e[0] != '\0') { @@ -67,7 +68,7 @@ const char *os_getenv(const char *name) pmap_del2(envmap, name); } e = xmalloc(size); - int r = uv_os_getenv(name, e, &size); + r = uv_os_getenv(name, e, &size); if (r == UV_ENOBUFS) { e = xrealloc(e, size); r = uv_os_getenv(name, e, &size); @@ -75,14 +76,15 @@ const char *os_getenv(const char *name) if (r != 0 || size == 0 || e[0] == '\0') { xfree(e); e = NULL; - if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) { - ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); - } goto end; } pmap_put(cstr_t)(envmap, xstrdup(name), e); end: + // Must do this before ELOG, log.c may call os_setenv. uv_mutex_unlock(&mutex); + if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) { + ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); + } return (e == NULL || size == 0 || e[0] == '\0') ? NULL : e; } @@ -146,10 +148,11 @@ int os_setenv(const char *name, const char *value, int overwrite) // Destroy the old map item. Do this AFTER uv_os_setenv(), because `value` // could be a previous os_getenv() result. pmap_del2(envmap, name); + // Must do this before ELOG, log.c may call os_setenv. + uv_mutex_unlock(&mutex); if (r != 0) { ELOG("uv_os_setenv(%s) failed: %d %s", name, r, uv_err_name(r)); } - uv_mutex_unlock(&mutex); return r == 0 ? 0 : -1; } @@ -163,10 +166,11 @@ int os_unsetenv(const char *name) uv_mutex_lock(&mutex); pmap_del2(envmap, name); int r = uv_os_unsetenv(name); + // Must do this before ELOG, log.c may call os_setenv. + uv_mutex_unlock(&mutex); if (r != 0) { ELOG("uv_os_unsetenv(%s) failed: %d %s", name, r, uv_err_name(r)); } - uv_mutex_unlock(&mutex); return r == 0 ? 0 : -1; } -- cgit From 6fd6f4683d19d5ca18f48c1d1f0d87113e80368e Mon Sep 17 00:00:00 2001 From: erw7 Date: Mon, 21 Oct 2019 07:47:08 +0900 Subject: TUI/thread: guard env map from potential race with unibilium #11259 unibi_from_term calls getenv internally, so exclusive control is required. --- src/nvim/os/env.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index ae61e54993..eb86cb8ac7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -44,6 +44,16 @@ void env_init(void) uv_mutex_init(&mutex); } +void os_env_var_lock(void) +{ + uv_mutex_lock(&mutex); +} + +void os_env_var_unlock(void) +{ + uv_mutex_unlock(&mutex); +} + /// Like getenv(), but returns NULL if the variable is empty. /// @see os_env_exists const char *os_getenv(const char *name) -- cgit From 19b6237087ebcf45427ceb6943d23ce33b39567f Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Fri, 9 Nov 2018 23:57:00 +0900 Subject: jobstart now supports env/clear_env to modify the environment of the launched job. --- src/nvim/os/env.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index eb86cb8ac7..15153e9bd7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -184,7 +184,7 @@ int os_unsetenv(const char *name) return r == 0 ? 0 : -1; } -char *os_getenvname_at_index(size_t index) +char **os_getfullenv(void) { #ifdef _WIN32 wchar_t *env = GetEnvironmentStringsW(); @@ -224,13 +224,20 @@ char *os_getenvname_at_index(size_t index) # else extern char **environ; # endif - // Check if index is inside the environ array and is not the last element. + return environ; +} + +char *os_getenvname_at_index(size_t index) +{ + char **env = os_getfullenv(); + // check if index is inside the environ array for (size_t i = 0; i <= index; i++) { - if (environ[i] == NULL) { + if (env[i] == NULL) { return NULL; } } - char *str = environ[index]; + char *str = env[index]; + assert(str != NULL); size_t namesize = 0; while (str[namesize] != '=' && str[namesize] != NUL) { namesize++; -- cgit From 6dc10057876d1bf75c5cf1ea45cb4312160f13f0 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sun, 2 Jun 2019 14:36:17 -0400 Subject: Add os_getfullenv_size/os_copyfullenv --- src/nvim/os/env.c | 152 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 29 deletions(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 15153e9bd7..ac442ee2e8 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -184,39 +184,139 @@ int os_unsetenv(const char *name) return r == 0 ? 0 : -1; } -char **os_getfullenv(void) +/// Returns number of variables in the current environment variables block +size_t os_get_fullenv_size(void) { + size_t len = 0; #ifdef _WIN32 - wchar_t *env = GetEnvironmentStringsW(); - if (!env) { + wchar_t *envstrings = GetEnvironmentStringsW(); + wchar_t *p = envstrings; + size_t l; + if (!envstrings) { + return len; + } + // GetEnvironmentStringsW() result has this format: + // var1=value1\0var2=value2\0...varN=valueN\0\0 + while ((l = wcslen(p)) != 0) { + p += l + 1; + len++; + } + + FreeEnvironmentStringsW(envstrings); +#else +# if defined(HAVE__NSGETENVIRON) + char **environ = *_NSGetEnviron(); +# else + extern char **environ; +# endif + + while (environ[len] != NULL) { + len++; + } + +#endif + return len; +} + +void os_free_fullenv(char **env) +{ + if (!env) { return; } + for (char **it = env; *it; it++) { + XFREE_CLEAR(*it); + } + xfree(env); +} + +/// Copies the current environment variables into the given array, `env`. Each +/// array element is of the form "NAME=VALUE". +/// Result must be freed by the caller. +/// +/// @param[out] env array to populate with environment variables +/// @param env_size size of `env`, @see os_fullenv_size +void os_copy_fullenv(char **env, size_t env_size) +{ +#ifdef _WIN32 + wchar_t *envstrings = GetEnvironmentStringsW(); + if (!envstrings) { + return; + } + wchar_t *p = envstrings; + size_t i = 0; + size_t l; + // GetEnvironmentStringsW() result has this format: + // var1=value1\0var2=value2\0...varN=valueN\0\0 + while ((l = wcslen(p)) != 0 && i < env_size) { + char *utf8_str; + int conversion_result = utf16_to_utf8(p, -1, &utf8_str); + if (conversion_result != 0) { + EMSG2("utf16_to_utf8 failed: %d", conversion_result); + break; + } + p += l + 1; + + env[i] = utf8_str; + i++; + } + + FreeEnvironmentStringsW(envstrings); +#else +# if defined(HAVE__NSGETENVIRON) + char **environ = *_NSGetEnviron(); +# else + extern char **environ; +# endif + + size_t i = 0; + while (environ[i] != NULL && i < env_size) { + env[i] = xstrdup(environ[i]); + i++; + } +#endif +} + +/// Copy value of the environment variable at `index` in the current +/// environment variables block. +/// Result must be freed by the caller. +/// +/// @param index nth item in environment variables block +/// @return [allocated] environment variable's value, or NULL +char *os_getenvname_at_index(size_t index) +{ +#ifdef _WIN32 + wchar_t *envstrings = GetEnvironmentStringsW(); + if (!envstrings) { return NULL; } + wchar_t *p = envstrings; char *name = NULL; - size_t current_index = 0; + size_t i = 0; + size_t l; // GetEnvironmentStringsW() result has this format: // var1=value1\0var2=value2\0...varN=valueN\0\0 - for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) { - if (index == current_index) { + while ((l = wcslen(p)) != 0 && i <= index) { + if (i == index) { char *utf8_str; - int conversion_result = utf16_to_utf8(it, -1, &utf8_str); + int conversion_result = utf16_to_utf8(p, -1, &utf8_str); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %d", conversion_result); break; } - size_t namesize = 0; - while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) { - namesize++; - } - name = (char *)vim_strnsave((char_u *)utf8_str, namesize); + + const char * const end = strchr(utf8_str, '='); + assert(end != NULL); + ptrdiff_t len = end - utf8_str; + assert(len > 0); + name = xstrndup(utf8_str, (size_t)len); xfree(utf8_str); break; } - if (*it == L'\0') { - current_index++; - } + + // Advance past the name and NUL + p += l + 1; + i++; } - FreeEnvironmentStringsW(env); + FreeEnvironmentStringsW(envstrings); return name; #else # if defined(HAVE__NSGETENVIRON) @@ -224,26 +324,20 @@ char **os_getfullenv(void) # else extern char **environ; # endif - return environ; -} -char *os_getenvname_at_index(size_t index) -{ - char **env = os_getfullenv(); // check if index is inside the environ array for (size_t i = 0; i <= index; i++) { - if (env[i] == NULL) { + if (environ[i] == NULL) { return NULL; } } - char *str = env[index]; + char *str = environ[index]; assert(str != NULL); - size_t namesize = 0; - while (str[namesize] != '=' && str[namesize] != NUL) { - namesize++; - } - char *name = (char *)vim_strnsave((char_u *)str, namesize); - return name; + const char * const end = strchr(str, '='); + assert(end != NULL); + ptrdiff_t len = end - str; + assert(len > 0); + return xstrndup(str, (size_t)len); #endif } -- cgit From 39963c6a04afc417a821b2255a5caea4b7955d7d Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 4 Dec 2019 08:01:05 -0500 Subject: os_getenvname_at_index: Handle Windows env vars whose name starts with = --- src/nvim/os/env.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/os/env.c') diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index ac442ee2e8..360609c50d 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -302,7 +302,10 @@ char *os_getenvname_at_index(size_t index) break; } - const char * const end = strchr(utf8_str, '='); + // Some Windows env vars start with =, so skip over that to find the + // separator between name/value + const char * const end = strchr(utf8_str + (utf8_str[0] == '=' ? 1 : 0), + '='); assert(end != NULL); ptrdiff_t len = end - utf8_str; assert(len > 0); -- cgit