unix,win: add uv_get_passwd()

This commit adds the uv_get_passwd() function, which returns a
subset of the current effective user's password file entry.

Refs: https://github.com/libuv/libuv/issues/11
Fixes: https://github.com/libuv/libuv/issues/731
PR-URL: https://github.com/libuv/libuv/pull/742
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
cjihrig 2016-03-01 15:41:01 -05:00
parent 27aa81fe5a
commit 217f81b6a1
12 changed files with 414 additions and 86 deletions

View File

@ -161,6 +161,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-get-currentexe.c \
test/test-get-loadavg.c \
test/test-get-memory.c \
test/test-get-passwd.c \
test/test-getaddrinfo.c \
test/test-getnameinfo.c \
test/test-getsockname.c \

View File

@ -101,6 +101,7 @@ test/test-fs.c
test/test-get-currentexe.c
test/test-get-loadavg.c
test/test-get-memory.c
test/test-get-passwd.c
test/test-getaddrinfo.c
test/test-getsockname.c
test/test-homedir.c

View File

@ -122,6 +122,20 @@ Data types
} netmask;
} uv_interface_address_t;
.. c:type:: uv_passwd_t
Data type for password file information.
::
typedef struct uv_passwd_s {
char* username;
long uid;
long gid;
char* shell;
char* homedir;
} uv_passwd_t;
API
---
@ -291,6 +305,24 @@ API
.. versionadded:: 1.9.0
.. c:function:: int uv_os_get_passwd(uv_passwd_t* pwd)
Gets a subset of the password file entry for the current effective uid (not
the real uid). The populated data includes the username, euid, gid, shell,
and home directory. On non-Windows systems, all data comes from
:man:`getpwuid_r(3)`. On Windows, uid and gid are set to -1 and have no
meaning, and shell is `NULL`. After successfully calling this function, the
memory allocated to `pwd` needs to be freed with
:c:func:`uv_os_free_passwd`.
.. versionadded:: 1.9.0
.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd)
Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`.
.. versionadded:: 1.9.0
.. uint64_t uv_get_free_memory(void)
.. c:function:: uint64_t uv_get_total_memory(void)

View File

@ -230,6 +230,7 @@ typedef struct uv_work_s uv_work_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;
typedef struct uv_passwd_s uv_passwd_t;
typedef enum {
UV_LOOP_BLOCK_SIGNAL
@ -1001,6 +1002,14 @@ struct uv_interface_address_s {
} netmask;
};
struct uv_passwd_s {
char* username;
long uid;
long gid;
char* shell;
char* homedir;
};
typedef enum {
UV_DIRENT_UNKNOWN,
UV_DIRENT_FILE,
@ -1051,6 +1060,8 @@ UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd);
UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd);
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);

View File

@ -1032,17 +1032,10 @@ int uv__dup2_cloexec(int oldfd, int newfd) {
int uv_os_homedir(char* buffer, size_t* size) {
struct passwd pw;
struct passwd* result;
uv_passwd_t pwd;
char* buf;
uid_t uid;
size_t bufsize;
size_t len;
long initsize;
int r;
#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
int (*getpwuid_r)(uid_t, struct passwd*, char*, size_t, struct passwd**);
#endif
if (buffer == NULL || size == NULL || *size == 0)
return -EINVAL;
@ -1064,59 +1057,24 @@ int uv_os_homedir(char* buffer, size_t* size) {
return 0;
}
#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
getpwuid_r = dlsym(RTLD_DEFAULT, "getpwuid_r");
if (getpwuid_r == NULL)
return -ENOSYS;
#endif
/* HOME is not set, so call getpwuid() */
initsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (initsize <= 0)
bufsize = 4096;
else
bufsize = (size_t) initsize;
uid = getuid();
buf = NULL;
for (;;) {
uv__free(buf);
buf = uv__malloc(bufsize);
if (buf == NULL)
return -ENOMEM;
r = getpwuid_r(uid, &pw, buf, bufsize, &result);
if (r != ERANGE)
break;
bufsize *= 2;
}
/* HOME is not set, so call uv__getpwuid_r() */
r = uv__getpwuid_r(&pwd);
if (r != 0) {
uv__free(buf);
return -r;
return r;
}
if (result == NULL) {
uv__free(buf);
return -ENOENT;
}
len = strlen(pw.pw_dir);
len = strlen(pwd.homedir);
if (len >= *size) {
*size = len + 1;
uv__free(buf);
uv_os_free_passwd(&pwd);
return -ENOBUFS;
}
memcpy(buffer, pw.pw_dir, len + 1);
memcpy(buffer, pwd.homedir, len + 1);
*size = len;
uv__free(buf);
uv_os_free_passwd(&pwd);
return 0;
}
@ -1171,3 +1129,113 @@ return_buffer:
return 0;
}
int uv__getpwuid_r(uv_passwd_t* pwd) {
struct passwd pw;
struct passwd* result;
char* buf;
uid_t uid;
size_t bufsize;
size_t name_size;
size_t homedir_size;
size_t shell_size;
long initsize;
int r;
#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
int (*getpwuid_r)(uid_t, struct passwd*, char*, size_t, struct passwd**);
getpwuid_r = dlsym(RTLD_DEFAULT, "getpwuid_r");
if (getpwuid_r == NULL)
return -ENOSYS;
#endif
if (pwd == NULL)
return -EINVAL;
initsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (initsize <= 0)
bufsize = 4096;
else
bufsize = (size_t) initsize;
uid = geteuid();
buf = NULL;
for (;;) {
uv__free(buf);
buf = uv__malloc(bufsize);
if (buf == NULL)
return -ENOMEM;
r = getpwuid_r(uid, &pw, buf, bufsize, &result);
if (r != ERANGE)
break;
bufsize *= 2;
}
if (r != 0) {
uv__free(buf);
return -r;
}
if (result == NULL) {
uv__free(buf);
return -ENOENT;
}
/* Allocate memory for the username, shell, and home directory */
name_size = strlen(pw.pw_name) + 1;
homedir_size = strlen(pw.pw_dir) + 1;
shell_size = strlen(pw.pw_shell) + 1;
pwd->username = uv__malloc(name_size + homedir_size + shell_size);
if (pwd->username == NULL) {
uv__free(buf);
return -ENOMEM;
}
/* Copy the username */
memcpy(pwd->username, pw.pw_name, name_size);
/* Copy the home directory */
pwd->homedir = pwd->username + name_size;
memcpy(pwd->homedir, pw.pw_dir, homedir_size);
/* Copy the shell */
pwd->shell = pwd->homedir + homedir_size;
memcpy(pwd->shell, pw.pw_shell, shell_size);
/* Copy the uid and gid */
pwd->uid = pw.pw_uid;
pwd->gid = pw.pw_gid;
uv__free(buf);
return 0;
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
/*
The memory for name, shell, and homedir are allocated in a single
uv__malloc() call. The base of the pointer is stored in pwd->username, so
that is the field that needs to be freed.
*/
uv__free(pwd->username);
pwd->username = NULL;
pwd->shell = NULL;
pwd->homedir = NULL;
}
int uv_os_get_passwd(uv_passwd_t* pwd) {
return uv__getpwuid_r(pwd);
}

View File

@ -257,6 +257,7 @@ void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
uv_handle_type uv__handle_type(int fd);
FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd);
#if defined(__APPLE__)

View File

@ -328,6 +328,8 @@ uint64_t uv__hrtime(double scale);
int uv_parent_pid();
int uv_current_pid();
__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
int uv__getpwuid_r(uv_passwd_t* pwd);
int uv__convert_utf16_to_utf8(const WCHAR* utf16, char** utf8);
/*

View File

@ -1152,7 +1152,7 @@ int uv_getrusage(uv_rusage_t *uv_rusage) {
int uv_os_homedir(char* buffer, size_t* size) {
HANDLE token;
uv_passwd_t pwd;
wchar_t path[MAX_PATH];
DWORD bufsize;
size_t len;
@ -1166,6 +1166,7 @@ int uv_os_homedir(char* buffer, size_t* size) {
if (len == 0) {
r = GetLastError();
/* Don't return an error if USERPROFILE was not found */
if (r != ERROR_ENVVAR_NOT_FOUND)
return uv_translate_sys_error(r);
@ -1173,51 +1174,52 @@ int uv_os_homedir(char* buffer, size_t* size) {
/* This should not be possible */
return UV_EIO;
} else {
goto convert_buffer;
/* Check how much space we need */
bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
if (bufsize == 0) {
return uv_translate_sys_error(GetLastError());
} else if (bufsize > *size) {
*size = bufsize;
return UV_ENOBUFS;
}
/* Convert to UTF-8 */
bufsize = WideCharToMultiByte(CP_UTF8,
0,
path,
-1,
buffer,
*size,
NULL,
NULL);
if (bufsize == 0)
return uv_translate_sys_error(GetLastError());
*size = bufsize - 1;
return 0;
}
/* USERPROFILE is not set, so call GetUserProfileDirectoryW() */
if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
return uv_translate_sys_error(GetLastError());
/* USERPROFILE is not set, so call uv__getpwuid_r() */
r = uv__getpwuid_r(&pwd);
bufsize = MAX_PATH;
if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
r = GetLastError();
CloseHandle(token);
/* This should not be possible */
if (r == ERROR_INSUFFICIENT_BUFFER)
return UV_EIO;
return uv_translate_sys_error(r);
if (r != 0) {
return r;
}
CloseHandle(token);
len = strlen(pwd.homedir);
convert_buffer:
/* Check how much space we need */
bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL);
if (bufsize == 0) {
return uv_translate_sys_error(GetLastError());
} else if (bufsize > *size) {
*size = bufsize;
if (len >= *size) {
*size = len + 1;
uv_os_free_passwd(&pwd);
return UV_ENOBUFS;
}
/* Convert to UTF-8 */
bufsize = WideCharToMultiByte(CP_UTF8,
0,
path,
-1,
buffer,
*size,
NULL,
NULL);
if (bufsize == 0)
return uv_translate_sys_error(GetLastError());
memcpy(buffer, pwd.homedir, len + 1);
*size = len;
uv_os_free_passwd(&pwd);
*size = bufsize - 1;
return 0;
}
@ -1273,3 +1275,118 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
*size = bufsize - 1;
return 0;
}
void uv_os_free_passwd(uv_passwd_t* pwd) {
if (pwd == NULL)
return;
uv__free(pwd->username);
uv__free(pwd->homedir);
pwd->username = NULL;
pwd->homedir = NULL;
}
int uv__convert_utf16_to_utf8(const WCHAR* utf16, char** utf8) {
DWORD bufsize;
if (utf16 == NULL)
return UV_EINVAL;
/* Check how much space we need */
bufsize = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL);
if (bufsize == 0)
return uv_translate_sys_error(GetLastError());
/* Allocate the destination buffer */
*utf8 = uv__malloc(bufsize);
if (*utf8 == NULL)
return UV_ENOMEM;
/* Convert to UTF-8 */
bufsize = WideCharToMultiByte(CP_UTF8,
0,
utf16,
-1,
*utf8,
bufsize,
NULL,
NULL);
if (bufsize == 0) {
uv__free(*utf8);
return uv_translate_sys_error(GetLastError());
}
return 0;
}
int uv__getpwuid_r(uv_passwd_t* pwd) {
HANDLE token;
wchar_t username[UNLEN + 1];
wchar_t path[MAX_PATH];
DWORD bufsize;
int r;
if (pwd == NULL)
return UV_EINVAL;
/* Get the home directory using GetUserProfileDirectoryW() */
if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
return uv_translate_sys_error(GetLastError());
bufsize = sizeof(path);
if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
r = GetLastError();
CloseHandle(token);
/* This should not be possible */
if (r == ERROR_INSUFFICIENT_BUFFER)
return UV_ENOMEM;
return uv_translate_sys_error(r);
}
CloseHandle(token);
/* Get the username using GetUserNameW() */
bufsize = sizeof(username);
if (!GetUserNameW(username, &bufsize)) {
r = GetLastError();
/* This should not be possible */
if (r == ERROR_INSUFFICIENT_BUFFER)
return UV_ENOMEM;
return uv_translate_sys_error(r);
}
pwd->homedir = NULL;
r = uv__convert_utf16_to_utf8(path, &pwd->homedir);
if (r != 0)
return r;
pwd->username = NULL;
r = uv__convert_utf16_to_utf8(username, &pwd->username);
if (r != 0) {
uv__free(pwd->homedir);
return r;
}
pwd->shell = NULL;
pwd->uid = -1;
pwd->gid = -1;
return 0;
}
int uv_os_get_passwd(uv_passwd_t* pwd) {
return uv__getpwuid_r(pwd);
}

80
test/test-get-passwd.c Normal file
View File

@ -0,0 +1,80 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
#include <string.h>
TEST_IMPL(get_passwd) {
uv_passwd_t pwd;
size_t len;
int r;
/* Test the normal case */
r = uv_os_get_passwd(&pwd);
ASSERT(r == 0);
len = strlen(pwd.username);
ASSERT(len > 0);
#ifdef _WIN32
ASSERT(pwd.shell == NULL);
#else
len = strlen(pwd.shell);
ASSERT(len > 0);
#endif
len = strlen(pwd.homedir);
ASSERT(len > 0);
#ifdef _WIN32
ASSERT(pwd.homedir[len - 1] != '\\');
#else
ASSERT(pwd.homedir[len - 1] != '/');
#endif
#ifdef _WIN32
ASSERT(pwd.uid == -1);
ASSERT(pwd.gid == -1);
#else
ASSERT(pwd.uid >= 0);
ASSERT(pwd.gid >= 0);
#endif
/* Test uv_os_free_passwd() */
uv_os_free_passwd(&pwd);
ASSERT(pwd.username == NULL);
ASSERT(pwd.shell == NULL);
ASSERT(pwd.homedir == NULL);
/* Test a double free */
uv_os_free_passwd(&pwd);
ASSERT(pwd.username == NULL);
ASSERT(pwd.shell == NULL);
ASSERT(pwd.homedir == NULL);
/* Test invalid input */
r = uv_os_get_passwd(NULL);
ASSERT(r == UV_EINVAL);
return 0;
}

View File

@ -197,6 +197,7 @@ TEST_DECLARE (get_currentexe)
TEST_DECLARE (process_title)
TEST_DECLARE (cwd_and_chdir)
TEST_DECLARE (get_memory)
TEST_DECLARE (get_passwd)
TEST_DECLARE (handle_fileno)
TEST_DECLARE (homedir)
TEST_DECLARE (tmpdir)
@ -585,6 +586,8 @@ TASK_LIST_START
TEST_ENTRY (get_memory)
TEST_ENTRY (get_passwd)
TEST_ENTRY (get_loadavg)
TEST_ENTRY (handle_fileno)

View File

@ -32,6 +32,7 @@ TEST_IMPL(platform_output) {
uv_rusage_t rusage;
uv_cpu_info_t* cpus;
uv_interface_address_t* interfaces;
uv_passwd_t pwd;
int count;
int i;
int err;
@ -122,5 +123,15 @@ TEST_IMPL(platform_output) {
}
uv_free_interface_addresses(interfaces, count);
err = uv_os_get_passwd(&pwd);
ASSERT(err == 0);
printf("uv_os_get_passwd:\n");
printf(" euid: %d\n", pwd.uid);
printf(" gid: %d\n", pwd.gid);
printf(" username: %s\n", pwd.username);
printf(" shell: %s\n", pwd.shell);
printf(" home directory: %s\n", pwd.homedir);
return 0;
}

1
uv.gyp
View File

@ -304,6 +304,7 @@
'test/test-fs-event.c',
'test/test-get-currentexe.c',
'test/test-get-memory.c',
'test/test-get-passwd.c',
'test/test-getaddrinfo.c',
'test/test-getnameinfo.c',
'test/test-getsockname.c',