mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
glib-unix: Add g_unix_get_passwd_entry() function
This is a convenience wrapper around getpwnam_r() which handles all the memory allocation faff. Signed-off-by: Philip Withnall <withnall@endlessm.com> Helps: #1687
This commit is contained in:
parent
ae49ff0903
commit
94a800fb9d
@ -2455,6 +2455,9 @@ g_unix_fd_add
|
|||||||
g_unix_fd_add_full
|
g_unix_fd_add_full
|
||||||
g_unix_fd_source_new
|
g_unix_fd_source_new
|
||||||
|
|
||||||
|
<SUBSECTION>
|
||||||
|
g_unix_get_passwd_entry
|
||||||
|
|
||||||
<SUBSECTION Private>
|
<SUBSECTION Private>
|
||||||
g_unix_error_quark
|
g_unix_error_quark
|
||||||
</SECTION>
|
</SECTION>
|
||||||
|
121
glib/glib-unix.c
121
glib/glib-unix.c
@ -30,6 +30,8 @@
|
|||||||
#include "gmain-internal.h"
|
#include "gmain-internal.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:gunix
|
* SECTION:gunix
|
||||||
@ -421,3 +423,122 @@ g_unix_fd_add (gint fd,
|
|||||||
{
|
{
|
||||||
return g_unix_fd_add_full (G_PRIORITY_DEFAULT, fd, condition, function, user_data, NULL);
|
return g_unix_fd_add_full (G_PRIORITY_DEFAULT, fd, condition, function, user_data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_unix_get_passwd_entry:
|
||||||
|
* @user_name: the username to get the passwd file entry for
|
||||||
|
* @error: return location for a #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Get the `passwd` file entry for the given @user_name using `getpwnam_r()`.
|
||||||
|
* This can fail if the given @user_name doesn’t exist.
|
||||||
|
*
|
||||||
|
* The returned `struct passwd` has been allocated using g_malloc() and should
|
||||||
|
* be freed using g_free(). The strings referenced by the returned struct are
|
||||||
|
* included in the same allocation, so are valid until the `struct passwd` is
|
||||||
|
* freed.
|
||||||
|
*
|
||||||
|
* This function is safe to call from multiple threads concurrently.
|
||||||
|
*
|
||||||
|
* You will need to include `pwd.h` to get the definition of `struct passwd`.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): passwd entry, or %NULL on error; free the returned
|
||||||
|
* value with g_free()
|
||||||
|
* Since: 2.64
|
||||||
|
*/
|
||||||
|
struct passwd *
|
||||||
|
g_unix_get_passwd_entry (const gchar *user_name,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
struct passwd *passwd_file_entry;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
struct passwd pwd;
|
||||||
|
char string_buffer[];
|
||||||
|
} *buffer = NULL;
|
||||||
|
gsize string_buffer_size = 0;
|
||||||
|
GError *local_error = NULL;
|
||||||
|
int errsv = 0;
|
||||||
|
|
||||||
|
g_return_val_if_fail (user_name != NULL, NULL);
|
||||||
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||||
|
|
||||||
|
#ifdef _SC_GETPW_R_SIZE_MAX
|
||||||
|
{
|
||||||
|
/* Get the recommended buffer size */
|
||||||
|
glong string_buffer_size_long = sysconf (_SC_GETPW_R_SIZE_MAX);
|
||||||
|
if (string_buffer_size_long > 0)
|
||||||
|
string_buffer_size = string_buffer_size_long;
|
||||||
|
}
|
||||||
|
#endif /* _SC_GETPW_R_SIZE_MAX */
|
||||||
|
|
||||||
|
/* Default starting size. */
|
||||||
|
if (string_buffer_size == 0)
|
||||||
|
string_buffer_size = 64;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
g_free (buffer);
|
||||||
|
/* Allocate space for the `struct passwd`, and then a buffer for all its
|
||||||
|
* strings (whose size is @string_buffer_size, which increases in this
|
||||||
|
* loop until it’s big enough). Add 6 extra bytes to work around a bug in
|
||||||
|
* macOS < 10.3. See #156446.
|
||||||
|
*/
|
||||||
|
buffer = g_malloc0 (sizeof (buffer) + string_buffer_size + 6);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
retval = getpwnam_r (user_name, &buffer->pwd, buffer->string_buffer,
|
||||||
|
string_buffer_size, &passwd_file_entry);
|
||||||
|
errsv = errno;
|
||||||
|
|
||||||
|
/* Bail out if: the lookup was successful, or if the user id can't be
|
||||||
|
* found (should be pretty rare case actually), or if the buffer should be
|
||||||
|
* big enough and yet lookups are still not successful.
|
||||||
|
*/
|
||||||
|
if (passwd_file_entry != NULL)
|
||||||
|
{
|
||||||
|
/* Success. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (retval == 0 ||
|
||||||
|
errsv == ENOENT || errsv == ESRCH ||
|
||||||
|
errsv == EBADF || errsv == EPERM)
|
||||||
|
{
|
||||||
|
/* Username not found. */
|
||||||
|
g_unix_set_error_from_errno (&local_error, errsv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (errsv == ERANGE)
|
||||||
|
{
|
||||||
|
/* Can’t allocate enough string buffer space. */
|
||||||
|
if (string_buffer_size > 32 * 1024)
|
||||||
|
{
|
||||||
|
g_unix_set_error_from_errno (&local_error, errsv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
string_buffer_size *= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_unix_set_error_from_errno (&local_error, errsv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (passwd_file_entry == NULL);
|
||||||
|
|
||||||
|
g_assert (passwd_file_entry == NULL ||
|
||||||
|
(gpointer) passwd_file_entry == (gpointer) buffer);
|
||||||
|
|
||||||
|
/* Success or error. */
|
||||||
|
if (local_error != NULL)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&buffer, g_free);
|
||||||
|
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||||
|
errno = errsv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct passwd *) g_steal_pointer (&buffer);
|
||||||
|
}
|
||||||
|
@ -114,6 +114,10 @@ guint g_unix_fd_add (gint fd,
|
|||||||
GUnixFDSourceFunc function,
|
GUnixFDSourceFunc function,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_2_64
|
||||||
|
struct passwd *g_unix_get_passwd_entry (const gchar *user_name,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_UNIX_H__ */
|
#endif /* __G_UNIX_H__ */
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "glib-unix.h"
|
#include "glib-unix.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_pipe (void)
|
test_pipe (void)
|
||||||
@ -295,6 +296,40 @@ test_callback_after_signal (void)
|
|||||||
g_main_context_unref (context);
|
g_main_context_unref (context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_get_passwd_entry_root (void)
|
||||||
|
{
|
||||||
|
struct passwd *pwd;
|
||||||
|
GError *local_error = NULL;
|
||||||
|
|
||||||
|
g_test_summary ("Tests that g_unix_get_passwd_entry() works for a "
|
||||||
|
"known-existing username.");
|
||||||
|
|
||||||
|
pwd = g_unix_get_passwd_entry ("root", &local_error);
|
||||||
|
g_assert_no_error (local_error);
|
||||||
|
|
||||||
|
g_assert_cmpstr (pwd->pw_name, ==, "root");
|
||||||
|
g_assert_cmpuint (pwd->pw_uid, ==, 0);
|
||||||
|
|
||||||
|
g_free (pwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_get_passwd_entry_nonexistent (void)
|
||||||
|
{
|
||||||
|
struct passwd *pwd;
|
||||||
|
GError *local_error = NULL;
|
||||||
|
|
||||||
|
g_test_summary ("Tests that g_unix_get_passwd_entry() returns an error for a "
|
||||||
|
"nonexistent username.");
|
||||||
|
|
||||||
|
pwd = g_unix_get_passwd_entry ("thisusernamedoesntexist", &local_error);
|
||||||
|
g_assert_error (local_error, G_UNIX_ERROR, 0);
|
||||||
|
g_assert_null (pwd);
|
||||||
|
|
||||||
|
g_clear_error (&local_error);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc,
|
main (int argc,
|
||||||
char *argv[])
|
char *argv[])
|
||||||
@ -310,6 +345,8 @@ main (int argc,
|
|||||||
g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
|
g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
|
||||||
g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
|
g_test_add_func ("/glib-unix/sighup_nested", test_sighup_nested);
|
||||||
g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
|
g_test_add_func ("/glib-unix/callback_after_signal", test_callback_after_signal);
|
||||||
|
g_test_add_func ("/glib-unix/get-passwd-entry/root", test_get_passwd_entry_root);
|
||||||
|
g_test_add_func ("/glib-unix/get-passwd-entry/nonexistent", test_get_passwd_entry_nonexistent);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user