[glib] gutils: stop g_get_home_dir() from reading passwd
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] gutils: stop g_get_home_dir() from reading passwd
- Date: Wed, 20 Feb 2013 11:10:15 +0000 (UTC)
commit cfafad5aefeeab1a4ee208cefa15e01d31932611
Author: Ryan Lortie <desrt desrt ca>
Date: Mon Feb 4 14:41:25 2013 +0100
gutils: stop g_get_home_dir() from reading passwd
In the case that the "HOME" environment variable is set (as it is under
normal circumstances), we don't really need to be opening /etc/passwd.
For historical reasons (ie: how we used to ignore $HOME) and due to the
grouping of many unrelated things together (reading username, hostname,
home directory, tmpdir, etc.) into one function we were still opening
/etc/passwd in g_get_home_dir(), even if $HOME was set.
Since earlier commits removed code from it, all that remains in
g_get_any_init_do() is the logic for dealing with $HOME and reading the
password database.
We now split the logic to deal with $HOME into g_get_home_dir(). With
only the password database functionality remaining, g_get_any_init_do()
is renamed to g_get_user_database_entry() and modified not to set global
variables but rather return a struct. If g_get_home_dir() cannot find
$HOME, it falls back to calling g_get_user_database_entry() and using
the home directory from there.
Use of the 'g_utils_global' lock is further reduced by using
g_once_init_enter() to protect the critical sections in each of
g_get_user_database_entry() and g_get_home_dir().
Finally, the g_get_user_name() and g_get_real_name() functions are
modified to use the new regime.
https://bugzilla.gnome.org/show_bug.cgi?id=693204
glib/gutils.c | 399 ++++++++++++++++++++++++++++++---------------------------
1 files changed, 213 insertions(+), 186 deletions(-)
---
diff --git a/glib/gutils.c b/glib/gutils.c
index 15f6f16..76f6c34 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -577,9 +577,12 @@ g_find_program_in_path (const gchar *program)
G_LOCK_DEFINE_STATIC (g_utils_global);
-static gchar *g_user_name = NULL;
-static gchar *g_real_name = NULL;
-static gchar *g_home_dir = NULL;
+typedef struct
+{
+ gchar *user_name;
+ gchar *real_name;
+ gchar *home_dir;
+} UserDatabaseEntry;
static gchar *g_user_data_dir = NULL;
static gchar **g_system_data_dirs = NULL;
@@ -643,214 +646,163 @@ get_windows_directory_root (void)
#endif
/* HOLDS: g_utils_global_lock */
-static void
-g_get_any_init_do (void)
+static UserDatabaseEntry *
+g_get_user_database_entry (void)
{
- /* We first check HOME and use it if it is set */
- g_home_dir = g_strdup (g_getenv ("HOME"));
+ static UserDatabaseEntry *entry;
-#ifdef G_OS_WIN32
- /* Only believe HOME if it is an absolute path and exists.
- *
- * We only do this check on Windows for a couple of reasons.
- * Historically, we only did it there because we used to ignore $HOME
- * on UNIX. There are concerns about enabling it now on UNIX because
- * of things like autofs. In short, if the user has a bogus value in
- * $HOME then they get what they pay for...
- */
- if (g_home_dir)
+ if (g_once_init_enter (&entry))
{
- if (!(g_path_is_absolute (g_home_dir) &&
- g_file_test (g_home_dir, G_FILE_TEST_IS_DIR)))
- {
- g_free (g_home_dir);
- g_home_dir = NULL;
- }
- }
+ static UserDatabaseEntry e;
- /* In case HOME is Unix-style (it happens), convert it to
- * Windows style.
- */
- if (g_home_dir)
- {
- gchar *p;
- while ((p = strchr (g_home_dir, '/')) != NULL)
- *p = '\\';
- }
-
- if (!g_home_dir)
- {
- /* USERPROFILE is probably the closest equivalent to $HOME? */
- if (g_getenv ("USERPROFILE") != NULL)
- g_home_dir = g_strdup (g_getenv ("USERPROFILE"));
- }
-
- if (!g_home_dir)
- g_home_dir = get_special_folder (CSIDL_PROFILE);
-
- if (!g_home_dir)
- g_home_dir = get_windows_directory_root ();
-#endif /* G_OS_WIN32 */
-
#ifdef HAVE_PWD_H
- {
- struct passwd *pw = NULL;
- gpointer buffer = NULL;
- gint error;
- gchar *logname;
+ {
+ struct passwd *pw = NULL;
+ gpointer buffer = NULL;
+ gint error;
+ gchar *logname;
# if defined (HAVE_POSIX_GETPWUID_R) || defined (HAVE_NONPOSIX_GETPWUID_R)
- struct passwd pwd;
-# ifdef _SC_GETPW_R_SIZE_MAX
- /* This reurns the maximum length */
- glong bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
-
- if (bufsize < 0)
- bufsize = 64;
+ struct passwd pwd;
+# ifdef _SC_GETPW_R_SIZE_MAX
+ /* This reurns the maximum length */
+ glong bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
+
+ if (bufsize < 0)
+ bufsize = 64;
# else /* _SC_GETPW_R_SIZE_MAX */
- glong bufsize = 64;
+ glong bufsize = 64;
# endif /* _SC_GETPW_R_SIZE_MAX */
- logname = (gchar *) g_getenv ("LOGNAME");
-
- do
- {
- g_free (buffer);
- /* we allocate 6 extra bytes to work around a bug in
- * Mac OS < 10.3. See #156446
- */
- buffer = g_malloc (bufsize + 6);
- errno = 0;
-
+ logname = (gchar *) g_getenv ("LOGNAME");
+
+ do
+ {
+ g_free (buffer);
+ /* we allocate 6 extra bytes to work around a bug in
+ * Mac OS < 10.3. See #156446
+ */
+ buffer = g_malloc (bufsize + 6);
+ errno = 0;
+
# ifdef HAVE_POSIX_GETPWUID_R
- if (logname) {
- error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
- if (!pw || (pw->pw_uid != getuid ())) {
- /* LOGNAME is lying, fall back to looking up the uid */
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
- }
- } else {
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
- }
- error = error < 0 ? errno : error;
+ if (logname) {
+ error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
+ if (!pw || (pw->pw_uid != getuid ())) {
+ /* LOGNAME is lying, fall back to looking up the uid */
+ error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+ }
+ } else {
+ error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+ }
+ error = error < 0 ? errno : error;
# else /* HAVE_NONPOSIX_GETPWUID_R */
- /* HPUX 11 falls into the HAVE_POSIX_GETPWUID_R case */
+ /* HPUX 11 falls into the HAVE_POSIX_GETPWUID_R case */
# if defined(_AIX) || defined(__hpux)
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- pw = error == 0 ? &pwd : NULL;
+ error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+ pw = error == 0 ? &pwd : NULL;
# else /* !_AIX */
- if (logname) {
- pw = getpwnam_r (logname, &pwd, buffer, bufsize);
- if (!pw || (pw->pw_uid != getuid ())) {
- /* LOGNAME is lying, fall back to looking up the uid */
- pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- }
- } else {
- pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- }
- error = pw ? 0 : errno;
-# endif /* !_AIX */
+ if (logname) {
+ pw = getpwnam_r (logname, &pwd, buffer, bufsize);
+ if (!pw || (pw->pw_uid != getuid ())) {
+ /* LOGNAME is lying, fall back to looking up the uid */
+ pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+ }
+ } else {
+ pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+ }
+ error = pw ? 0 : errno;
+# endif /* !_AIX */
# endif /* HAVE_NONPOSIX_GETPWUID_R */
-
- if (!pw)
- {
- /* we bail out prematurely if the user id can't be found
- * (should be pretty rare case actually), or if the buffer
- * should be sufficiently big and lookups are still not
- * successful.
- */
- if (error == 0 || error == ENOENT)
- {
- g_warning ("getpwuid_r(): failed due to unknown user id (%lu)",
- (gulong) getuid ());
- break;
- }
- if (bufsize > 32 * 1024)
- {
- g_warning ("getpwuid_r(): failed due to: %s.",
- g_strerror (error));
- break;
- }
-
- bufsize *= 2;
- }
- }
- while (!pw);
+
+ if (!pw)
+ {
+ /* we bail out prematurely if the user id can't be found
+ * (should be pretty rare case actually), or if the buffer
+ * should be sufficiently big and lookups are still not
+ * successful.
+ */
+ if (error == 0 || error == ENOENT)
+ {
+ g_warning ("getpwuid_r(): failed due to unknown user id (%lu)",
+ (gulong) getuid ());
+ break;
+ }
+ if (bufsize > 32 * 1024)
+ {
+ g_warning ("getpwuid_r(): failed due to: %s.",
+ g_strerror (error));
+ break;
+ }
+
+ bufsize *= 2;
+ }
+ }
+ while (!pw);
# endif /* HAVE_POSIX_GETPWUID_R || HAVE_NONPOSIX_GETPWUID_R */
-
- if (!pw)
- {
- setpwent ();
- pw = getpwuid (getuid ());
- endpwent ();
- }
- if (pw)
- {
- g_user_name = g_strdup (pw->pw_name);
-
- if (pw->pw_gecos && *pw->pw_gecos != '\0')
- {
- gchar **gecos_fields;
- gchar **name_parts;
-
- /* split the gecos field and substitute '&' */
- gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
- name_parts = g_strsplit (gecos_fields[0], "&", 0);
- pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
- g_real_name = g_strjoinv (pw->pw_name, name_parts);
- g_strfreev (gecos_fields);
- g_strfreev (name_parts);
- }
-
- if (!g_home_dir)
- g_home_dir = g_strdup (pw->pw_dir);
+
+ if (!pw)
+ {
+ setpwent ();
+ pw = getpwuid (getuid ());
+ endpwent ();
+ }
+ if (pw)
+ {
+ e.user_name = g_strdup (pw->pw_name);
+
+ if (pw->pw_gecos && *pw->pw_gecos != '\0')
+ {
+ gchar **gecos_fields;
+ gchar **name_parts;
+
+ /* split the gecos field and substitute '&' */
+ gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
+ name_parts = g_strsplit (gecos_fields[0], "&", 0);
+ pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
+ e.real_name = g_strjoinv (pw->pw_name, name_parts);
+ g_strfreev (gecos_fields);
+ g_strfreev (name_parts);
+ }
+
+ if (!e.home_dir)
+ e.home_dir = g_strdup (pw->pw_dir);
+ }
+ g_free (buffer);
}
- g_free (buffer);
- }
-
+
#else /* !HAVE_PWD_H */
-
+
#ifdef G_OS_WIN32
- {
- guint len = UNLEN+1;
- wchar_t buffer[UNLEN+1];
-
- if (GetUserNameW (buffer, (LPDWORD) &len))
{
- g_user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
- g_real_name = g_strdup (g_user_name);
+ guint len = UNLEN+1;
+ wchar_t buffer[UNLEN+1];
+
+ if (GetUserNameW (buffer, (LPDWORD) &len))
+ {
+ e.user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+ e.real_name = g_strdup (e.user_name);
+ }
}
- }
#endif /* G_OS_WIN32 */
#endif /* !HAVE_PWD_H */
#ifdef __EMX__
- /* change '\\' in %HOME% to '/' */
- g_strdelimit (g_home_dir, "\\",'/');
+ /* change '\\' in %HOME% to '/' */
+ g_strdelimit (e.home_dir, "\\",'/');
#endif
- if (!g_user_name)
- g_user_name = g_strdup ("somebody");
- if (!g_real_name)
- g_real_name = g_strdup ("Unknown");
-}
+ if (!e.user_name)
+ e.user_name = g_strdup ("somebody");
+ if (!e.real_name)
+ e.real_name = g_strdup ("Unknown");
-static inline void
-g_get_any_init (void)
-{
- if (!g_user_name)
- g_get_any_init_do ();
-}
+ g_once_init_leave (&entry, &e);
+ }
-static inline void
-g_get_any_init_locked (void)
-{
- G_LOCK (g_utils_global);
- g_get_any_init ();
- G_UNLOCK (g_utils_global);
+ return entry;
}
-
/**
* g_get_user_name:
*
@@ -864,8 +816,11 @@ g_get_any_init_locked (void)
const gchar *
g_get_user_name (void)
{
- g_get_any_init_locked ();
- return g_user_name;
+ UserDatabaseEntry *entry;
+
+ entry = g_get_user_database_entry ();
+
+ return entry->user_name;
}
/**
@@ -882,8 +837,11 @@ g_get_user_name (void)
const gchar *
g_get_real_name (void)
{
- g_get_any_init_locked ();
- return g_real_name;
+ UserDatabaseEntry *entry;
+
+ entry = g_get_user_database_entry ();
+
+ return entry->real_name;
}
/**
@@ -920,8 +878,77 @@ g_get_real_name (void)
const gchar *
g_get_home_dir (void)
{
- g_get_any_init_locked ();
- return g_home_dir;
+ static gchar *home_dir;
+
+ if (g_once_init_enter (&home_dir))
+ {
+ gchar *tmp;
+
+ /* We first check HOME and use it if it is set */
+ tmp = g_strdup (g_getenv ("HOME"));
+
+#ifdef G_OS_WIN32
+ /* Only believe HOME if it is an absolute path and exists.
+ *
+ * We only do this check on Windows for a couple of reasons.
+ * Historically, we only did it there because we used to ignore $HOME
+ * on UNIX. There are concerns about enabling it now on UNIX because
+ * of things like autofs. In short, if the user has a bogus value in
+ * $HOME then they get what they pay for...
+ */
+ if (tmp)
+ {
+ if (!(g_path_is_absolute (tmp) &&
+ g_file_test (tmp, G_FILE_TEST_IS_DIR)))
+ {
+ g_free (tmp);
+ tmp = NULL;
+ }
+ }
+
+ /* In case HOME is Unix-style (it happens), convert it to
+ * Windows style.
+ */
+ if (tmp)
+ {
+ gchar *p;
+ while ((p = strchr (tmp, '/')) != NULL)
+ *p = '\\';
+ }
+
+ if (!tmp)
+ {
+ /* USERPROFILE is probably the closest equivalent to $HOME? */
+ if (g_getenv ("USERPROFILE") != NULL)
+ tmp = g_strdup (g_getenv ("USERPROFILE"));
+ }
+
+ if (!tmp)
+ tmp = get_special_folder (CSIDL_PROFILE);
+
+ if (!tmp)
+ tmp = get_windows_directory_root ();
+#endif /* G_OS_WIN32 */
+
+ if (!tmp)
+ {
+ /* If we didn't get it from any of those methods, we will have
+ * to read the user database entry.
+ */
+ UserDatabaseEntry *entry;
+
+ entry = g_get_user_database_entry ();
+
+ /* Strictly speaking, we should copy this, but we know that
+ * neither will ever be freed, so don't bother...
+ */
+ tmp = entry->home_dir;
+ }
+
+ g_once_init_leave (&home_dir, tmp);
+ }
+
+ return home_dir;
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]