diff --git a/include/uv.h b/include/uv.h index 045371aa..e6724def 100644 --- a/include/uv.h +++ b/include/uv.h @@ -245,6 +245,7 @@ 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 struct uv_group_s uv_group_t; typedef struct uv_utsname_s uv_utsname_t; typedef struct uv_statfs_s uv_statfs_t; @@ -1139,6 +1140,12 @@ struct uv_passwd_s { char* homedir; }; +struct uv_group_s { + char* groupname; + unsigned long gid; + char** members; +}; + struct uv_utsname_s { char sysname[256]; char release[256]; @@ -1219,6 +1226,9 @@ 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_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid); +UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid); +UV_EXTERN void uv_os_free_group(uv_group_t* grp); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); diff --git a/src/unix/core.c b/src/unix/core.c index bb9226af..706dcccb 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -41,12 +41,12 @@ #include /* writev */ #include /* getrusage */ #include +#include #include #include #ifdef __sun # include -# include # include #endif @@ -1097,8 +1097,8 @@ int uv_os_homedir(char* buffer, size_t* size) { if (r != UV_ENOENT) return r; - /* HOME is not set, so call uv__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* HOME is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1171,11 +1171,10 @@ return_buffer: } -int uv__getpwuid_r(uv_passwd_t* pwd) { +static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) { struct passwd pw; struct passwd* result; char* buf; - uid_t uid; size_t bufsize; size_t name_size; size_t homedir_size; @@ -1185,8 +1184,6 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (pwd == NULL) return UV_EINVAL; - uid = geteuid(); - /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it * is frequently 1024 or 4096, so we can just use that directly. The pwent * will not usually be large. */ @@ -1245,24 +1242,93 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { } -void uv_os_free_passwd(uv_passwd_t* pwd) { - if (pwd == NULL) - return; +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { + struct group gp; + struct group* result; + char* buf; + char* gr_mem; + size_t bufsize; + size_t name_size; + long members; + size_t mem_size; + int r; - /* - 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; + if (grp == NULL) + return UV_EINVAL; + + /* Calling sysconf(_SC_GETGR_R_SIZE_MAX) would get the suggested size, but it + * is frequently 1024 or 4096, so we can just use that directly. The pwent + * will not usually be large. */ + for (bufsize = 2000;; bufsize *= 2) { + buf = uv__malloc(bufsize); + + if (buf == NULL) + return UV_ENOMEM; + + do + r = getgrgid_r(gid, &gp, buf, bufsize, &result); + while (r == EINTR); + + if (r != 0 || result == NULL) + uv__free(buf); + + if (r != ERANGE) + break; + } + + if (r != 0) + return UV__ERR(r); + + if (result == NULL) + return UV_ENOENT; + + /* Allocate memory for the groupname and members. */ + name_size = strlen(gp.gr_name) + 1; + members = 0; + mem_size = sizeof(char*); + for (r = 0; gp.gr_mem[r] != NULL; r++) { + mem_size += strlen(gp.gr_mem[r]) + 1 + sizeof(char*); + members++; + } + + gr_mem = uv__malloc(name_size + mem_size); + if (gr_mem == NULL) { + uv__free(buf); + return UV_ENOMEM; + } + + /* Copy the members */ + grp->members = (char**) gr_mem; + grp->members[members] = NULL; + gr_mem = (char*) &grp->members[members + 1]; + for (r = 0; r < members; r++) { + grp->members[r] = gr_mem; + strcpy(gr_mem, gp.gr_mem[r]); + gr_mem += strlen(gr_mem) + 1; + } + assert(gr_mem == (char*)grp->members + mem_size); + + /* Copy the groupname */ + grp->groupname = gr_mem; + memcpy(grp->groupname, gp.gr_name, name_size); + gr_mem += name_size; + + /* Copy the gid */ + grp->gid = gp.gr_gid; + + uv__free(buf); + + return 0; } int uv_os_get_passwd(uv_passwd_t* pwd) { - return uv__getpwuid_r(pwd); + return uv__getpwuid_r(pwd, geteuid()); +} + + +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return uv__getpwuid_r(pwd, uid); } diff --git a/src/unix/internal.h b/src/unix/internal.h index e83526db..5497ee36 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -312,7 +312,6 @@ size_t uv__thread_stack_size(void); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); FILE* uv__open_file(const char* path); -int uv__getpwuid_r(uv_passwd_t* pwd); int uv__search_path(const char* prog, char* buf, size_t* buflen); void uv__wait_children(uv_loop_t* loop); diff --git a/src/uv-common.c b/src/uv-common.c index c87bd2e8..1031be4a 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -128,6 +128,39 @@ int uv_replace_allocator(uv_malloc_func malloc_func, return 0; } + +void uv_os_free_passwd(uv_passwd_t* pwd) { + if (pwd == NULL) + return; + + /* On unix, 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); +#ifdef _WIN32 + uv__free(pwd->homedir); +#endif + pwd->username = NULL; + pwd->shell = NULL; + pwd->homedir = NULL; +} + + +void uv_os_free_group(uv_group_t *grp) { + if (grp == NULL) + return; + + /* The memory for is allocated in a single uv__malloc() call. The base of the + * pointer is stored in grp->members, so that is the only field that needs to + * be freed. + */ + uv__free(grp->members); + grp->members = NULL; + grp->groupname = NULL; +} + + #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t); size_t uv_handle_size(uv_handle_type type) { diff --git a/src/win/internal.h b/src/win/internal.h index 8a87743b..1b524b5c 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -269,7 +269,6 @@ void uv__util_init(void); uint64_t uv__hrtime(unsigned int scale); __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, int utf16len, char** utf8); int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); diff --git a/src/win/util.c b/src/win/util.c index 094a414c..647d6e66 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -999,8 +999,8 @@ int uv_os_homedir(char* buffer, size_t* size) { if (r != UV_ENOENT) return r; - /* USERPROFILE is not set, so call uv__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* USERPROFILE is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1087,17 +1087,6 @@ int uv_os_tmpdir(char* buffer, size_t* size) { } -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; -} - - /* * Converts a UTF-16 string into a UTF-8 one. The resulting string is * null-terminated. @@ -1194,7 +1183,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { } -int uv__getpwuid_r(uv_passwd_t* pwd) { +static int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; wchar_t *path; @@ -1272,6 +1261,16 @@ int uv_os_get_passwd(uv_passwd_t* pwd) { } +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return UV_ENOTSUP; +} + + +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { + return UV_ENOTSUP; +} + + int uv_os_environ(uv_env_item_t** envitems, int* count) { wchar_t* env; wchar_t* penv; diff --git a/test/test-get-passwd.c b/test/test-get-passwd.c index d2c7431f..d046e40c 100644 --- a/test/test-get-passwd.c +++ b/test/test-get-passwd.c @@ -39,32 +39,32 @@ TEST_IMPL(get_passwd) { /* Test the normal case */ r = uv_os_get_passwd(&pwd); - ASSERT(r == 0); + ASSERT_EQ(r, 0); len = strlen(pwd.username); - ASSERT(len > 0); + ASSERT_GT(len, 0); #ifdef _WIN32 ASSERT_NULL(pwd.shell); #else len = strlen(pwd.shell); # ifndef __PASE__ - ASSERT(len > 0); + ASSERT_GT(len, 0); # endif #endif len = strlen(pwd.homedir); - ASSERT(len > 0); + ASSERT_GT(len, 0); #ifdef _WIN32 if (len == 3 && pwd.homedir[1] == ':') - ASSERT(pwd.homedir[2] == '\\'); + ASSERT_EQ(pwd.homedir[2], '\\'); else - ASSERT(pwd.homedir[len - 1] != '\\'); + ASSERT_NE(pwd.homedir[len - 1], '\\'); #else if (len == 1) - ASSERT(pwd.homedir[0] == '/'); + ASSERT_EQ(pwd.homedir[0], '/'); else - ASSERT(pwd.homedir[len - 1] != '/'); + ASSERT_NE(pwd.homedir[len - 1], '/'); #endif #ifdef _WIN32 @@ -95,7 +95,110 @@ TEST_IMPL(get_passwd) { /* Test invalid input */ r = uv_os_get_passwd(NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); + + return 0; +} + + +TEST_IMPL(get_passwd2) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + + uv_passwd_t pwd; + uv_passwd_t pwd2; + size_t len; + int r; + + /* Test the normal case */ + r = uv_os_get_passwd(&pwd); + ASSERT_EQ(r, 0); + + r = uv_os_get_passwd2(&pwd2, pwd.uid); + +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); + +#else + ASSERT_EQ(r, 0); + ASSERT_EQ(pwd.uid, pwd2.uid); + ASSERT_STR_EQ(pwd.username, pwd2.username); + ASSERT_STR_EQ(pwd.shell, pwd2.shell); + ASSERT_STR_EQ(pwd.homedir, pwd2.homedir); + uv_os_free_passwd(&pwd2); + + r = uv_os_get_passwd2(&pwd2, 0); + ASSERT_EQ(r, 0); + + len = strlen(pwd2.username); + ASSERT_GT(len, 0); + ASSERT_STR_EQ(pwd2.username, "root"); + + len = strlen(pwd2.homedir); + ASSERT_GT(len, 0); + + len = strlen(pwd2.shell); +# ifndef __PASE__ + ASSERT_GT(len, 0); +# endif + + uv_os_free_passwd(&pwd2); +#endif + + uv_os_free_passwd(&pwd); + + /* Test invalid input */ + r = uv_os_get_passwd2(NULL, pwd.uid); +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); +#else + ASSERT_EQ(r, UV_EINVAL); +#endif + + return 0; +} + + +TEST_IMPL(get_group) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + + uv_passwd_t pwd; + uv_group_t grp; + size_t len; + int r; + + r = uv_os_get_passwd(&pwd); + ASSERT_EQ(r, 0); + + r = uv_os_get_group(&grp, pwd.gid); + +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); + +#else + ASSERT_EQ(r, 0); + ASSERT_EQ(pwd.gid, grp.gid); + + len = strlen(grp.groupname); + ASSERT_GT(len, 0); + + uv_os_free_group(&grp); +#endif + + uv_os_free_passwd(&pwd); + + /* Test invalid input */ + r = uv_os_get_group(NULL, pwd.gid); +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); +#else + ASSERT_EQ(r, UV_EINVAL); +#endif return 0; } diff --git a/test/test-list.h b/test/test-list.h index 00eda659..4cd06356 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -281,6 +281,8 @@ TEST_DECLARE (process_title_threadsafe) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) TEST_DECLARE (get_passwd) +TEST_DECLARE (get_passwd2) +TEST_DECLARE (get_group) TEST_DECLARE (handle_fileno) TEST_DECLARE (homedir) TEST_DECLARE (tmpdir) @@ -899,6 +901,8 @@ TASK_LIST_START TEST_ENTRY (get_memory) TEST_ENTRY (get_passwd) + TEST_ENTRY (get_passwd2) + TEST_ENTRY (get_group) TEST_ENTRY (get_loadavg) diff --git a/test/test-platform-output.c b/test/test-platform-output.c index 4c2d77c9..5839f52d 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -35,8 +35,10 @@ TEST_IMPL(platform_output) { uv_cpu_info_t* cpus; uv_interface_address_t* interfaces; uv_passwd_t pwd; + uv_group_t grp; uv_utsname_t uname; unsigned par; + char* const* member; int count; int i; int err; @@ -147,15 +149,38 @@ TEST_IMPL(platform_output) { uv_free_interface_addresses(interfaces, count); err = uv_os_get_passwd(&pwd); - ASSERT(err == 0); + ASSERT_EQ(err, 0); + + err = uv_os_get_group(&grp, pwd.gid); +#if defined(_WIN32) + ASSERT_EQ(err, UV_ENOTSUP); + ASSERT_EQ(pwd.uid, (unsigned long) -1); + ASSERT_EQ(pwd.gid, (unsigned long) -1); + (void) member; + grp.groupname = "ENOTSUP"; +#else + ASSERT_EQ(err, 0); + ASSERT_EQ(pwd.gid, grp.gid); +#endif printf("uv_os_get_passwd:\n"); printf(" euid: %ld\n", pwd.uid); - printf(" gid: %ld\n", pwd.gid); + printf(" gid: %ld (%s)\n", pwd.gid, grp.groupname); +#if !defined(_WIN32) + printf(" members: ["); + for (member = grp.members; *member != NULL; member++) { + printf(" %s", *member); + } + printf(" ]\n"); +#endif printf(" username: %s\n", pwd.username); - printf(" shell: %s\n", pwd.shell); + if (pwd.shell != NULL) /* Not set on Windows */ + printf(" shell: %s\n", pwd.shell); printf(" home directory: %s\n", pwd.homedir); uv_os_free_passwd(&pwd); +#if !defined(_WIN32) + uv_os_free_group(&grp); +#endif pid = uv_os_getpid(); ASSERT(pid > 0);