From 2480b6158a3a21da564bdb565c4db827df176a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Thu, 1 Aug 2019 09:48:38 +0200 Subject: [PATCH] unix,win: add ability to retrieve all env variables Fixes: https://github.com/libuv/libuv/issues/2400 PR-URL: https://github.com/libuv/libuv/pull/2404 Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig Reviewed-By: Santiago Gimeno --- docs/src/misc.rst | 28 ++++++++++++++++++ include/uv.h | 8 +++++ src/unix/core.c | 60 ++++++++++++++++++++++++++++++++++++++ src/uv-common.c | 11 +++++++ src/win/util.c | 69 ++++++++++++++++++++++++++++++++++++++++++++ test/test-env-vars.c | 37 +++++++++++++++++++++++- 6 files changed, 212 insertions(+), 1 deletion(-) diff --git a/docs/src/misc.rst b/docs/src/misc.rst index ef70e14b..3ecfce48 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -180,6 +180,17 @@ Data types char machine[256]; } uv_utsname_t; +.. c:type:: uv_env_item_t + + Data type for environment variable storage. + + :: + + typedef struct uv_env_item_s { + char* name; + char* value; + } uv_env_item_t; + API --- @@ -523,6 +534,23 @@ API .. versionadded:: 1.8.0 +.. c:function:: int uv_os_environ(uv_env_item_t** envitems, int* count) + + Retrieves all environment variables. This function will allocate memory + which must be freed by calling :c:func:`uv_os_free_environ`. + + .. warning:: + This function is not thread safe. + + .. versionadded:: 1.31.0 + +.. c:function:: void uv_os_free_environ(uv_env_item_t* envitems, int count); + + Frees the memory allocated for the environment variables by + :c:func:`uv_os_environ`. + + .. versionadded:: 1.31.0 + .. c:function:: int uv_os_getenv(const char* name, char* buffer, size_t* size) Retrieves the environment variable specified by `name`, copies its value diff --git a/include/uv.h b/include/uv.h index 9157d69a..f71767b6 100644 --- a/include/uv.h +++ b/include/uv.h @@ -231,6 +231,7 @@ typedef struct uv_fs_s uv_fs_t; typedef struct uv_work_s uv_work_t; /* None of the above. */ +typedef struct uv_env_item_s uv_env_item_t; typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; @@ -1162,6 +1163,13 @@ UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, int count); +struct uv_env_item_s { + char* name; + char* value; +}; + +UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count); +UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count); UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size); UV_EXTERN int uv_os_setenv(const char* name, const char* value); UV_EXTERN int uv_os_unsetenv(const char* name); diff --git a/src/unix/core.c b/src/unix/core.c index 202c75bb..f4b94e30 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -50,11 +50,15 @@ #endif #ifdef __APPLE__ +# include # include /* _NSGetExecutablePath */ # include # if defined(O_CLOEXEC) # define UV__O_CLOEXEC O_CLOEXEC # endif +# define environ (*_NSGetEnviron()) +#else +extern char** environ; #endif #if defined(__DragonFly__) || \ @@ -1284,6 +1288,62 @@ int uv_translate_sys_error(int sys_errno) { } +int uv_os_environ(uv_env_item_t** envitems, int* count) { + int i, j, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + for (i = 0; environ[i] != NULL; i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + + if (envitems == NULL) + return UV_ENOMEM; + + for (j = 0, cnt = 0; j < i; j++) { + char* buf; + char* ptr; + + if (environ[j] == NULL) + break; + + buf = uv__strdup(environ[j]); + if (buf == NULL) + goto fail; + + ptr = strchr(buf, '='); + if (ptr == NULL) { + uv__free(buf); + continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + } + + *count = cnt; + return 0; + +fail: + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + int uv_os_getenv(const char* name, char* buffer, size_t* size) { char* var; size_t len; diff --git a/src/uv-common.c b/src/uv-common.c index 066eb31d..d1a5e2fb 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -786,3 +786,14 @@ void uv_loop_delete(uv_loop_t* loop) { if (loop != default_loop) uv__free(loop); } + + +void uv_os_free_environ(uv_env_item_t* envitems, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(envitems[i].name); + } + + uv__free(envitems); +} diff --git a/src/win/util.c b/src/win/util.c index a8f45233..359a16ae 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -1397,6 +1397,75 @@ int uv_os_get_passwd(uv_passwd_t* pwd) { } +int uv_os_environ(uv_env_item_t** envitems, int* count) { + wchar_t* env; + wchar_t* penv; + int i, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + env = GetEnvironmentStringsW(); + if (env == NULL) + return 0; + + for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + if (envitems == NULL) { + FreeEnvironmentStringsW(env); + return UV_ENOMEM; + } + + penv = env; + cnt = 0; + + while (*penv != L'\0' && cnt < i) { + char* buf; + char* ptr; + + if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0) + goto fail; + + ptr = strchr(buf, '='); + if (ptr == NULL) { + uv__free(buf); + goto do_continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + + do_continue: + penv += wcslen(penv) + 1; + } + + FreeEnvironmentStringsW(env); + + *count = cnt; + return 0; + +fail: + FreeEnvironmentStringsW(env); + + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + int uv_os_getenv(const char* name, char* buffer, size_t* size) { wchar_t var[MAX_ENV_VAR_LENGTH]; wchar_t* name_w; diff --git a/test/test-env-vars.c b/test/test-env-vars.c index 641050e6..d7abb424 100644 --- a/test/test-env-vars.c +++ b/test/test-env-vars.c @@ -27,9 +27,11 @@ TEST_IMPL(env_vars) { const char* name = "UV_TEST_FOO"; + const char* name2 = "UV_TEST_FOO2"; char buf[BUF_SIZE]; size_t size; - int r; + int i, r, envcount, found; + uv_env_item_t* envitems; /* Reject invalid inputs when setting an environment variable */ r = uv_os_setenv(NULL, "foo"); @@ -86,5 +88,38 @@ TEST_IMPL(env_vars) { r = uv_os_unsetenv(name); ASSERT(r == 0); + /* Check getting all env variables. */ + r = uv_os_setenv(name, "123456789"); + ASSERT(r == 0); + r = uv_os_setenv(name2, ""); + ASSERT(r == 0); + + r = uv_os_environ(&envitems, &envcount); + ASSERT(r == 0); + ASSERT(envcount > 0); + + found = 0; + + for (i = 0; i < envcount; i++) { + /* printf("Env: %s = %s\n", envitems[i].name, envitems[i].value); */ + if (strcmp(envitems[i].name, name) == 0) { + found++; + ASSERT(strcmp(envitems[i].value, "123456789") == 0); + } else if (strcmp(envitems[i].name, name2) == 0) { + found++; + ASSERT(strlen(envitems[i].value) == 0); + } + } + + ASSERT(found == 2); + + uv_os_free_environ(envitems, envcount); + + r = uv_os_unsetenv(name); + ASSERT(r == 0); + + r = uv_os_unsetenv(name2); + ASSERT(r == 0); + return 0; }