[glib/wip/gutils-splitup: 6/6] gutils: stop g_get_home_dir() from reading passwd



commit d54e80358e8dc495bed4ab4ec3005ad5ae04af2a
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.

 glib/gutils.c |  391 ++++++++++++++++++++++++++++++---------------------------
 1 files changed, 205 insertions(+), 186 deletions(-)
---
diff --git a/glib/gutils.c b/glib/gutils.c
index 70c4cab..f2d420e 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"));
-
-#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_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;
-	}
-    }
-
-  /* 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 = '\\';
-    }
+  static UserDatabaseEntry *entry;
 
-  if (!g_home_dir)
+  if (g_once_init_enter (&entry))
     {
-      /* USERPROFILE is probably the closest equivalent to $HOME? */
-      if (g_getenv ("USERPROFILE") != NULL)
-	g_home_dir = g_strdup (g_getenv ("USERPROFILE"));
-    }
+      static UserDatabaseEntry e;
 
-  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,7 @@ g_get_any_init_locked (void)
 const gchar *
 g_get_user_name (void)
 {
-  g_get_any_init_locked ();
-  return g_user_name;
+  return g_get_user_database_entry ()->user_name;
 }
 
 /**
@@ -882,8 +833,7 @@ g_get_user_name (void)
 const gchar *
 g_get_real_name (void)
 {
-  g_get_any_init_locked ();
-  return g_real_name;
+  return g_get_user_database_entry ()->real_name;
 }
 
 /**
@@ -920,8 +870,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]