* configure.in: Add test/name-test/Makefile to the generated
[platform/upstream/dbus.git] / dbus / dbus-sysdeps.c
index bb47187..030d080 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2002, 2003  Red Hat, Inc.
  * Copyright (C) 2003 CodeFactory AB
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.1
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,8 @@
 #include "dbus-internals.h"
 #include "dbus-sysdeps.h"
 #include "dbus-threads.h"
-#include "dbus-test.h"
+#include "dbus-protocol.h"
+#include "dbus-string.h"
 #include <sys/types.h>
 #include <stdlib.h>
 #include <string.h>
 #ifdef HAVE_BACKTRACE
 #include <execinfo.h>
 #endif
-
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-#ifndef SUN_LEN
-/* This system is not POSIX.1g.         */
-#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)  \
-       + strlen ((ptr)->sun_path))
+#ifndef HAVE_SOCKLEN_T
+#define socklen_t int
 #endif
 
 /**
  * @addtogroup DBusInternalsUtils
  * @{
  */
+#ifndef DBUS_DISABLE_ASSERT
 /**
  * Aborts the program with SIGABRT (dumping core).
  */
 void
 _dbus_abort (void)
 {
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+  const char *s;
+  s = _dbus_getenv ("DBUS_PRINT_BACKTRACE");
+  if (s && *s)
+    _dbus_print_backtrace ();
+#endif
   abort ();
   _exit (1); /* in case someone manages to ignore SIGABRT */
 }
+#endif
 
 /**
  * Wrapper for setenv(). If the value is #NULL, unsets
@@ -373,19 +382,29 @@ _dbus_write_two (int               fd,
  * Creates a socket and connects it to the UNIX domain socket at the
  * given path.  The connection fd is returned, and is set up as
  * nonblocking.
+ * 
+ * Uses abstract sockets instead of filesystem-linked sockets if
+ * requested (it's possible only on Linux; see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
  *
  * @param path the path to UNIX domain socket
+ * @param abstract #TRUE to use abstract namespace
  * @param error return location for error code
  * @returns connection file descriptor or -1 on error
  */
 int
 _dbus_connect_unix_socket (const char     *path,
+                           dbus_bool_t     abstract,
                            DBusError      *error)
 {
   int fd;
+  size_t path_len;
   struct sockaddr_un addr;  
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _dbus_verbose ("connecting to unix socket %s abstract=%d\n",
+                 path, abstract);
   
   fd = socket (PF_UNIX, SOCK_STREAM, 0);
   
@@ -401,9 +420,45 @@ _dbus_connect_unix_socket (const char     *path,
 
   _DBUS_ZERO (addr);
   addr.sun_family = AF_UNIX;
-  strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
+  path_len = strlen (path);
+
+  if (abstract)
+    {
+#ifdef HAVE_ABSTRACT_SOCKETS
+      addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (fd);
+          return -1;
+       }
+       
+      strncpy (&addr.sun_path[1], path, path_len);
+      /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* HAVE_ABSTRACT_SOCKETS */
+      dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+                      "Operating system does not support abstract socket namespace\n");
+      close (fd);
+      return -1;
+#endif /* ! HAVE_ABSTRACT_SOCKETS */
+    }
+  else
+    {
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Socket name too long\n");
+          close (fd);
+          return -1;
+       }
+
+      strncpy (addr.sun_path, path, path_len);
+    }
   
-  if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
+  if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
     {      
       dbus_set_error (error,
                       _dbus_error_from_errno (errno),
@@ -434,24 +489,29 @@ _dbus_connect_unix_socket (const char     *path,
  * then listens on the socket. The socket is
  * set to be nonblocking.
  *
- * @todo we'd like to be able to use the abstract namespace on linux
- * (see "man 7 unix"). The question is whether to silently move all
- * paths into that namespace if we can (I think that's best) or to
- * require it to be specified explicitly in the dbus address.  Also,
- * need to sort out how to check for abstract namespace support.
+ * Uses abstract sockets instead of filesystem-linked
+ * sockets if requested (it's possible only on Linux;
+ * see "man 7 unix" on Linux).
+ * On non-Linux abstract socket usage always fails.
  *
  * @param path the socket name
+ * @param abstract #TRUE to use abstract namespace
  * @param error return location for errors
  * @returns the listening file descriptor or -1 on error
  */
 int
 _dbus_listen_unix_socket (const char     *path,
+                          dbus_bool_t     abstract,
                           DBusError      *error)
 {
   int listen_fd;
   struct sockaddr_un addr;
+  size_t path_len;
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  _dbus_verbose ("listening on unix socket %s abstract=%d\n",
+                 path, abstract);
   
   listen_fd = socket (PF_UNIX, SOCK_STREAM, 0);
   
@@ -463,29 +523,68 @@ _dbus_listen_unix_socket (const char     *path,
       return -1;
     }
 
-  /* FIXME discussed security implications of this with Nalin,
-   * and we couldn't think of where it would kick our ass, but
-   * it still seems a bit sucky. It also has non-security suckage;
-   * really we'd prefer to exit if the socket is already in use.
-   * But there doesn't seem to be a good way to do this.
-   *
-   * Just to be extra careful, I threw in the stat() - clearly
-   * the stat() can't *fix* any security issue, but it probably
-   * makes it harder to exploit.
-   */
-  {
-    struct stat sb;
-
-    if (stat (path, &sb) == 0 &&
-        S_ISSOCK (sb.st_mode))
-      unlink (path);
-  }
-  
   _DBUS_ZERO (addr);
   addr.sun_family = AF_UNIX;
-  strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
+  path_len = strlen (path);
+  
+  if (abstract)
+    {
+#ifdef HAVE_ABSTRACT_SOCKETS
+      /* remember that abstract names aren't nul-terminated so we rely
+       * on sun_path being filled in with zeroes above.
+       */
+      addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
+      path_len++; /* Account for the extra nul byte added to the start of sun_path */
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (listen_fd);
+          return -1;
+       }
+      
+      strncpy (&addr.sun_path[1], path, path_len);
+      /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
+#else /* HAVE_ABSTRACT_SOCKETS */
+      dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
+                      "Operating system does not support abstract socket namespace\n");
+      close (listen_fd);
+      return -1;
+#endif /* ! HAVE_ABSTRACT_SOCKETS */
+    }
+  else
+    {
+      /* FIXME discussed security implications of this with Nalin,
+       * and we couldn't think of where it would kick our ass, but
+       * it still seems a bit sucky. It also has non-security suckage;
+       * really we'd prefer to exit if the socket is already in use.
+       * But there doesn't seem to be a good way to do this.
+       *
+       * Just to be extra careful, I threw in the stat() - clearly
+       * the stat() can't *fix* any security issue, but it at least
+       * avoids inadvertent/accidental data loss.
+       */
+      {
+        struct stat sb;
+
+        if (stat (path, &sb) == 0 &&
+            S_ISSOCK (sb.st_mode))
+          unlink (path);
+      }
+
+      if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
+        {
+          dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
+                      "Abstract socket name too long\n");
+          close (listen_fd);
+          return -1;
+       }
+       
+      strncpy (addr.sun_path, path, path_len);
+    }
   
-  if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0)
+  if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0)
     {
       dbus_set_error (error, _dbus_error_from_errno (errno),
                       "Failed to bind socket \"%s\": %s",
@@ -509,11 +608,11 @@ _dbus_listen_unix_socket (const char     *path,
       close (listen_fd);
       return -1;
     }
-
+  
   /* Try opening up the permissions, but if we can't, just go ahead
    * and continue, maybe it will be good enough.
    */
-  if (chmod (path, 0777) < 0)
+  if (!abstract && chmod (path, 0777) < 0)
     _dbus_warn ("Could not set mode 0777 on socket %s\n",
                 path);
   
@@ -564,6 +663,7 @@ _dbus_connect_tcp_socket (const char     *host,
                       _dbus_error_from_errno (errno),
                       "Failed to lookup hostname: %s",
                       host);
+      close (fd);
       return -1;
     }
   
@@ -630,9 +730,6 @@ _dbus_listen_tcp_socket (const char     *host,
       return -1;
     }
 
-  if (host == NULL)
-    host = "localhost";
-  
   he = gethostbyname (host);
   if (he == NULL) 
     {
@@ -640,6 +737,7 @@ _dbus_listen_tcp_socket (const char     *host,
                       _dbus_error_from_errno (errno),
                       "Failed to lookup hostname: %s",
                       host);
+      close (listen_fd);
       return -1;
     }
   
@@ -683,12 +781,40 @@ write_credentials_byte (int             server_fd,
 {
   int bytes_written;
   char buf[1] = { '\0' };
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  struct {
+         struct cmsghdr hdr;
+         struct cmsgcred cred;
+  } cmsg;
+  struct iovec iov;
+  struct msghdr msg;
+#endif
+
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  iov.iov_base = buf;
+  iov.iov_len = 1;
+
+  memset (&msg, 0, sizeof (msg));
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
+  memset (&cmsg, 0, sizeof (cmsg));
+  cmsg.hdr.cmsg_len = sizeof (cmsg);
+  cmsg.hdr.cmsg_level = SOL_SOCKET;
+  cmsg.hdr.cmsg_type = SCM_CREDS;
+#endif
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   
  again:
 
+#if defined(HAVE_CMSGCRED) && !defined(LOCAL_CREDS)
+  bytes_written = sendmsg (server_fd, &msg, 0);
+#else
   bytes_written = write (server_fd, buf, 1);
+#endif
 
   if (bytes_written < 0 && errno == EINTR)
     goto again;
@@ -742,8 +868,10 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   char buf;
 
 #ifdef HAVE_CMSGCRED 
-  char cmsgmem[CMSG_SPACE (sizeof (struct cmsgcred))];
-  struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
+  struct {
+         struct cmsghdr hdr;
+         struct cmsgcred cred;
+  } cmsg;
 #endif
 
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -755,10 +883,8 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   _dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid));
   _dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid));
   _dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid));
-  
-  credentials->pid = -1;
-  credentials->uid = -1;
-  credentials->gid = -1;
+
+  _dbus_credentials_clear (credentials);
 
 #if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
   /* Set the socket to receive credentials on the next message */
@@ -780,9 +906,9 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
   msg.msg_iovlen = 1;
 
 #ifdef HAVE_CMSGCRED
-  memset (cmsgmem, 0, sizeof (cmsgmem));
-  msg.msg_control = cmsgmem;
-  msg.msg_controllen = sizeof (cmsgmem);
+  memset (&cmsg, 0, sizeof (cmsg));
+  msg.msg_control = &cmsg;
+  msg.msg_controllen = sizeof (cmsg);
 #endif
 
  again:
@@ -805,10 +931,10 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
     }
 
 #ifdef HAVE_CMSGCRED
-  if (cmsg->cmsg_len < sizeof (cmsgmem) || cmsg->cmsg_type != SCM_CREDS)
+  if (cmsg.hdr.cmsg_len < sizeof (cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS)
     {
-      dbus_set_error (error, DBUS_ERROR_FAILED);
-      _dbus_verbose ("Message from recvmsg() was not SCM_CREDS\n");
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Message from recvmsg() was not SCM_CREDS");
       return FALSE;
     }
 #endif
@@ -833,19 +959,44 @@ _dbus_read_credentials_unix_socket  (int              client_fd,
                       cr_len, (int) sizeof (cr), _dbus_strerror (errno));
       }
 #elif defined(HAVE_CMSGCRED)
-    struct cmsgcred *cred;
-
-    cred = (struct cmsgcred *) CMSG_DATA (cmsg);
-
-    credentials->pid = cred->cmcred_pid;
-    credentials->uid = cred->cmcred_euid;
-    credentials->gid = cred->cmcred_groups[0];
-#else /* !SO_PEERCRED && !HAVE_CMSGCRED */
+    credentials->pid = cmsg.cred.cmcred_pid;
+    credentials->uid = cmsg.cred.cmcred_euid;
+    credentials->gid = cmsg.cred.cmcred_groups[0];
+#elif defined(HAVE_GETPEEREID)
+    uid_t euid;
+    gid_t egid;
+    if (getpeereid (client_fd, &euid, &egid) == 0)
+      {
+        credentials->uid = euid;
+        credentials->gid = egid;
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
+      }
+#elif defined(HAVE_GETPEERUCRED)
+    ucred_t * ucred = NULL;
+    if (getpeerucred (client_fd, &ucred) == 0)
+      {
+        credentials->pid = ucred_getpid (ucred);
+        credentials->uid = ucred_geteuid (ucred);
+        credentials->gid = ucred_getegid (ucred);
+      }
+    else
+      {
+        _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
+      }
+    if (ucred != NULL)
+      ucred_free (ucred);
+#else /* !SO_PEERCRED && !HAVE_CMSGCRED && !HAVE_GETPEEREID && !HAVE_GETPEERUCRED */
     _dbus_verbose ("Socket credentials not supported on this OS\n");
 #endif
   }
 
-  _dbus_verbose ("Credentials: pid %d  uid %d  gid %d\n",
+  _dbus_verbose ("Credentials:"
+                 "  pid "DBUS_PID_FORMAT
+                 "  uid "DBUS_UID_FORMAT
+                 "  gid "DBUS_GID_FORMAT"\n",
                 credentials->pid,
                 credentials->uid,
                 credentials->gid);
@@ -993,6 +1144,7 @@ _dbus_string_append_uint (DBusString    *str,
   return TRUE;
 }
 
+#ifdef DBUS_BUILD_TESTS
 /**
  * Appends a double to a DBusString.
  * 
@@ -1029,6 +1181,7 @@ _dbus_string_append_double (DBusString *str,
   
   return TRUE;
 }
+#endif /* DBUS_BUILD_TESTS */
 
 /**
  * Parses an integer contained in a DBusString. Either return parameter
@@ -1070,6 +1223,43 @@ _dbus_string_parse_int (const DBusString *str,
 }
 
 /**
+* Checks to make sure the given directory is 
+* private to the user 
+*
+* @param dir the name of the directory
+* @param error error return
+* @returns #FALSE on failure
+**/
+dbus_bool_t
+_dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error)
+{
+  const char *directory;
+  struct stat sb;
+       
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+    
+  directory = _dbus_string_get_const_data (dir);
+       
+  if (stat (directory, &sb) < 0)
+    {
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "%s", _dbus_strerror (errno));
+   
+      return FALSE;
+    }
+    
+  if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) ||
+      (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                     "%s directory is not private to the user", directory);
+      return FALSE;
+    }
+    
+  return TRUE;
+}
+
+/**
  * Parses an unsigned integer contained in a DBusString. Either return
  * parameter may be #NULL if you aren't interested in it. The integer
  * is parsed and stored in value_return. Return parameters are not
@@ -1108,6 +1298,7 @@ _dbus_string_parse_uint (const DBusString *str,
   return TRUE;
 }
 
+#ifdef DBUS_BUILD_TESTS
 static dbus_bool_t
 ascii_isspace (char c)
 {
@@ -1118,13 +1309,17 @@ ascii_isspace (char c)
          c == '\t' ||
          c == '\v');
 }
+#endif /* DBUS_BUILD_TESTS */
 
+#ifdef DBUS_BUILD_TESTS
 static dbus_bool_t
 ascii_isdigit (char c)
 {
   return c >= '0' && c <= '9';
 }
+#endif /* DBUS_BUILD_TESTS */
 
+#ifdef DBUS_BUILD_TESTS
 static dbus_bool_t
 ascii_isxdigit (char c)
 {
@@ -1132,8 +1327,9 @@ ascii_isxdigit (char c)
          (c >= 'a' && c <= 'f') ||
          (c >= 'A' && c <= 'F'));
 }
+#endif /* DBUS_BUILD_TESTS */
 
-
+#ifdef DBUS_BUILD_TESTS
 /* Calls strtod in a locale-independent fashion, by looking at
  * the locale data and patching the decimal comma to a point.
  *
@@ -1262,8 +1458,9 @@ ascii_strtod (const char *nptr,
   
   return val;
 }
+#endif /* DBUS_BUILD_TESTS */
 
-
+#ifdef DBUS_BUILD_TESTS
 /**
  * Parses a floating point number contained in a DBusString. Either
  * return parameter may be #NULL if you aren't interested in it. The
@@ -1302,6 +1499,7 @@ _dbus_string_parse_double (const DBusString *str,
 
   return TRUE;
 }
+#endif /* DBUS_BUILD_TESTS */
 
 /** @} */ /* DBusString group */
 
@@ -1309,93 +1507,59 @@ _dbus_string_parse_double (const DBusString *str,
  * @addtogroup DBusInternalsUtils
  * @{
  */
-
 static dbus_bool_t
-store_user_info (struct passwd    *p,
-                 DBusCredentials  *credentials,
-                 DBusString       *homedir,
-                 DBusString       *username_out)
+fill_user_info_from_passwd (struct passwd *p,
+                            DBusUserInfo  *info,
+                            DBusError     *error)
 {
-  int old_homedir_len;
+  _dbus_assert (p->pw_name != NULL);
+  _dbus_assert (p->pw_dir != NULL);
   
-  if (credentials != NULL)
-    {
-      credentials->uid = p->pw_uid;
-      credentials->gid = p->pw_gid;
-    }
-
-  old_homedir_len = 0;
-  if (homedir != NULL)
-    {
-      old_homedir_len = _dbus_string_get_length (homedir);
-      
-      if (!_dbus_string_append (homedir, p->pw_dir))
-        {
-          _dbus_verbose ("No memory to get homedir\n");
-          return FALSE;
-        }
-    }
+  info->uid = p->pw_uid;
+  info->primary_gid = p->pw_gid;
+  info->username = _dbus_strdup (p->pw_name);
+  info->homedir = _dbus_strdup (p->pw_dir);
   
-  if (username_out &&
-      !_dbus_string_append (username_out, p->pw_name))
+  if (info->username == NULL ||
+      info->homedir == NULL)
     {
-      if (homedir)
-        _dbus_string_set_length (homedir, old_homedir_len);
-      _dbus_verbose ("No memory to get username\n");
+      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
       return FALSE;
     }
-      
-  _dbus_verbose ("Username %s has uid %d gid %d homedir %s\n",
-                 p->pw_name, (int) p->pw_uid, (int) p->pw_gid,
-                 p->pw_dir);
 
   return TRUE;
 }
-  
-/**
- * Gets user info using either username or uid. Only
- * one of these may be passed in, either username
- * must be #NULL or uid must be < 0.
- *
- * @param username the username
- * @param uid the user ID
- * @param credentials to fill in or #NULL
- * @param homedir string to append homedir to or #NULL
- * @param username_out string to append username to or #NULL
- *
- * @returns #TRUE on success
- */
+
 static dbus_bool_t
-get_user_info (const DBusString *username,
-               int               uid,
-               DBusCredentials  *credentials,
-               DBusString       *homedir,
-               DBusString       *username_out)
+fill_user_info (DBusUserInfo       *info,
+                dbus_uid_t          uid,
+                const DBusString   *username,
+                DBusError          *error)
 {
-  const char *username_c_str;
-      
+  const char *username_c;
+  
   /* exactly one of username/uid provided */
-  _dbus_assert (username != NULL || uid >= 0);
-  _dbus_assert (username == NULL || uid < 0);
+  _dbus_assert (username != NULL || uid != DBUS_UID_UNSET);
+  _dbus_assert (username == NULL || uid == DBUS_UID_UNSET);
 
-  if (credentials)
-    {
-      credentials->pid = -1;
-      credentials->uid = -1;
-      credentials->gid = -1;
-    }
+  info->uid = DBUS_UID_UNSET;
+  info->primary_gid = DBUS_GID_UNSET;
+  info->group_ids = NULL;
+  info->n_group_ids = 0;
+  info->username = NULL;
+  info->homedir = NULL;
   
   if (username != NULL)
-    username_c_str = _dbus_string_get_const_data (username);
+    username_c = _dbus_string_get_const_data (username);
   else
-    username_c_str = NULL;
+    username_c = NULL;
 
   /* For now assuming that the getpwnam() and getpwuid() flavors
    * are always symmetrical, if not we have to add more configure
    * checks
    */
   
-#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
+#if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R)
   {
     struct passwd *p;
     int result;
@@ -1403,28 +1567,31 @@ get_user_info (const DBusString *username,
     struct passwd p_str;
 
     p = NULL;
-#ifdef HAVE_POSIX_GETPWNAME_R
-    if (uid >= 0)
+#ifdef HAVE_POSIX_GETPWNAM_R
+    if (uid != DBUS_UID_UNSET)
       result = getpwuid_r (uid, &p_str, buf, sizeof (buf),
                            &p);
     else
-      result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
+      result = getpwnam_r (username_c, &p_str, buf, sizeof (buf),
                            &p);
 #else
-    if (uid >= 0)
+    if (uid != DBUS_UID_UNSET)
       p = getpwuid_r (uid, &p_str, buf, sizeof (buf));
     else
-      p = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf));
+      p = getpwnam_r (username_c, &p_str, buf, sizeof (buf));
     result = 0;
-#endif /* !HAVE_POSIX_GETPWNAME_R */
+#endif /* !HAVE_POSIX_GETPWNAM_R */
     if (result == 0 && p == &p_str)
       {
-        return store_user_info (p, credentials, homedir,
-                                username_out);
+        if (!fill_user_info_from_passwd (p, info, error))
+          return FALSE;
       }
     else
       {
-        _dbus_verbose ("User %s unknown\n", username_c_str);
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "User \"%s\" unknown or no memory to allocate password entry\n",
+                        username_c ? username_c : "???");
+        _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
         return FALSE;
       }
   }
@@ -1433,203 +1600,177 @@ get_user_info (const DBusString *username,
     /* I guess we're screwed on thread safety here */
     struct passwd *p;
 
-    if (uid >= 0)
+    if (uid != DBUS_UID_UNSET)
       p = getpwuid (uid);
     else
-      p = getpwnam (username_c_str);
+      p = getpwnam (username_c);
 
     if (p != NULL)
       {
-        return store_user_info (p, credentials, homedir,
-                                username_out);
+        if (!fill_user_info_from_passwd (p, info, error))
+          return FALSE;
       }
     else
       {
-        _dbus_verbose ("User %s unknown\n", username_c_str);
+        dbus_set_error (error, _dbus_error_from_errno (errno),
+                        "User \"%s\" unknown or no memory to allocate password entry\n",
+                        username_c ? username_c : "???");
+        _dbus_verbose ("User %s unknown\n", username_c ? username_c : "???");
         return FALSE;
       }
   }
 #endif  /* ! HAVE_GETPWNAM_R */
+
+  /* Fill this in so we can use it to get groups */
+  username_c = info->username;
+  
+#ifdef HAVE_GETGROUPLIST
+  {
+    gid_t *buf;
+    int buf_count;
+    int i;
+    
+    buf_count = 17;
+    buf = dbus_new (gid_t, buf_count);
+    if (buf == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        goto failed;
+      }
+    
+    if (getgrouplist (username_c,
+                      info->primary_gid,
+                      buf, &buf_count) < 0)
+      {
+        gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
+        if (new == NULL)
+          {
+            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+            dbus_free (buf);
+            goto failed;
+          }
+        
+        buf = new;
+
+        errno = 0;
+        if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0)
+          {
+            dbus_set_error (error,
+                            _dbus_error_from_errno (errno),
+                            "Failed to get groups for username \"%s\" primary GID "
+                            DBUS_GID_FORMAT ": %s\n",
+                            username_c, info->primary_gid,
+                            _dbus_strerror (errno));
+            dbus_free (buf);
+            goto failed;
+          }
+      }
+
+    info->group_ids = dbus_new (dbus_gid_t, buf_count);
+    if (info->group_ids == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        dbus_free (buf);
+        goto failed;
+      }
+    
+    for (i = 0; i < buf_count; ++i)
+      info->group_ids[i] = buf[i];
+
+    info->n_group_ids = buf_count;
+    
+    dbus_free (buf);
+  }
+#else  /* HAVE_GETGROUPLIST */
+  {
+    /* We just get the one group ID */
+    info->group_ids = dbus_new (dbus_gid_t, 1);
+    if (info->group_ids == NULL)
+      {
+        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+        goto failed;
+      }
+
+    info->n_group_ids = 1;
+
+    (info->group_ids)[0] = info->primary_gid;
+  }
+#endif /* HAVE_GETGROUPLIST */
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  
+  return TRUE;
+  
+ failed:
+  _DBUS_ASSERT_ERROR_IS_SET (error);
+  return FALSE;
 }
 
 /**
- * Gets the credentials corresponding to the given username.
+ * Gets user info for the given username.
  *
+ * @param info user info object to initialize
  * @param username the username
- * @param credentials credentials to fill in
- * @returns #TRUE if the username existed and we got some credentials
+ * @param error error return
+ * @returns #TRUE on success
  */
 dbus_bool_t
-_dbus_credentials_from_username (const DBusString *username,
-                                 DBusCredentials  *credentials)
+_dbus_user_info_fill (DBusUserInfo     *info,
+                      const DBusString *username,
+                      DBusError        *error)
 {
-  return get_user_info (username, -1, credentials, NULL, NULL);
+  return fill_user_info (info, DBUS_UID_UNSET,
+                         username, error);
 }
 
 /**
- * Gets the credentials corresponding to the given user ID.
+ * Gets user info for the given user ID.
  *
- * @param user_id the user ID
- * @param credentials credentials to fill in
- * @returns #TRUE if the username existed and we got some credentials
+ * @param info user info object to initialize
+ * @param uid the user ID
+ * @param error error return
+ * @returns #TRUE on success
  */
 dbus_bool_t
-_dbus_credentials_from_user_id (unsigned long     user_id,
-                                DBusCredentials  *credentials)
-{
-  return get_user_info (NULL, user_id, credentials, NULL, NULL);
-}
-
-_DBUS_DEFINE_GLOBAL_LOCK (user_info);
-
-typedef struct
-{
-  DBusString name;
-  DBusString dir;
-  DBusCredentials creds;
-} UserInfo;
-
-static void
-shutdown_user_info (void *data)
+_dbus_user_info_fill_uid (DBusUserInfo *info,
+                          dbus_uid_t    uid,
+                          DBusError    *error)
 {
-  UserInfo *u = data;
-
-  _dbus_string_free (&u->name);
-  _dbus_string_free (&u->dir);
+  return fill_user_info (info, uid,
+                         NULL, error);
 }
 
 /**
- * Gets information about the user running this process.
- *
- * @param username return location for username or #NULL
- * @param homedir return location for home directory or #NULL
- * @param credentials return location for credentials or #NULL
- * @returns #TRUE on success
+ * Frees the members of info
+ * (but not info itself)
+ * @param info the user info struct
  */
-dbus_bool_t
-_dbus_user_info_from_current_process (const DBusString      **username,
-                                      const DBusString      **homedir,
-                                      const DBusCredentials **credentials)
+void
+_dbus_user_info_free (DBusUserInfo *info)
 {
-  static UserInfo u;
-  static int initialized_generation = 0;
-  
-  if (!_DBUS_LOCK (user_info))
-    return FALSE;
-
-  if (initialized_generation != _dbus_current_generation)
-    {
-      if (!_dbus_string_init (&u.name))
-        {
-          _DBUS_UNLOCK (user_info);
-          return FALSE;
-        }
-
-      if (!_dbus_string_init (&u.dir))
-        {
-          _dbus_string_free (&u.name);
-          _DBUS_UNLOCK (user_info);
-          return FALSE;
-        }
-      
-      u.creds.uid = -1;
-      u.creds.gid = -1;
-      u.creds.pid = -1;
-
-      if (!get_user_info (NULL, getuid (),
-                          &u.creds, &u.dir, &u.name))
-        goto fail_init;
-      
-      if (!_dbus_register_shutdown_func (shutdown_user_info,
-                                         &u))
-        goto fail_init;
-      
-      initialized_generation = _dbus_current_generation;
-    fail_init:
-      if (initialized_generation != _dbus_current_generation)
-        {
-          _dbus_string_free (&u.name);
-          _dbus_string_free (&u.dir);
-          _DBUS_UNLOCK (user_info);
-          return FALSE;
-        }
-    }
-
-  if (username)
-    *username = &u.name;
-  if (homedir)
-    *homedir = &u.dir;
-  if (credentials)
-    *credentials = &u.creds;
-  
-  _DBUS_UNLOCK (user_info);
-
-  return TRUE;
+  dbus_free (info->group_ids);
+  dbus_free (info->username);
+  dbus_free (info->homedir);
 }
 
 /**
- * Gets the home directory for the given user.
+ * Sets fields in DBusCredentials to DBUS_PID_UNSET,
+ * DBUS_UID_UNSET, DBUS_GID_UNSET.
  *
- * @param username the username
- * @param homedir string to append home directory to
- * @returns #TRUE if user existed and we appended their homedir
+ * @param credentials the credentials object to fill in
  */
-dbus_bool_t
-_dbus_homedir_from_username (const DBusString *username,
-                             DBusString       *homedir)
+void
+_dbus_credentials_clear (DBusCredentials *credentials)
 {
-  return get_user_info (username, -1, NULL, homedir, NULL);
+  credentials->pid = DBUS_PID_UNSET;
+  credentials->uid = DBUS_UID_UNSET;
+  credentials->gid = DBUS_GID_UNSET;
 }
 
 /**
- * Gets credentials from a UID string. (Parses a string to a UID
- * and converts to a DBusCredentials.)
+ * Gets the credentials of the current process.
  *
- * @param uid_str the UID in string form
- * @param credentials credentials to fill in
- * @returns #TRUE if successfully filled in some credentials
- */
-dbus_bool_t
-_dbus_credentials_from_uid_string (const DBusString      *uid_str,
-                                   DBusCredentials       *credentials)
-{
-  int end;
-  long uid;
-
-  credentials->pid = -1;
-  credentials->uid = -1;
-  credentials->gid = -1;
-  
-  if (_dbus_string_get_length (uid_str) == 0)
-    {
-      _dbus_verbose ("UID string was zero length\n");
-      return FALSE;
-    }
-
-  uid = -1;
-  end = 0;
-  if (!_dbus_string_parse_int (uid_str, 0, &uid,
-                               &end))
-    {
-      _dbus_verbose ("could not parse string as a UID\n");
-      return FALSE;
-    }
-  
-  if (end != _dbus_string_get_length (uid_str))
-    {
-      _dbus_verbose ("string contained trailing stuff after UID\n");
-      return FALSE;
-    }
-
-  credentials->uid = uid;
-
-  return TRUE;
-}
-
-/**
- * Gets the credentials of the current process.
- *
- * @param credentials credentials to fill in.
+ * @param credentials credentials to fill in.
  */
 void
 _dbus_credentials_from_current_process (DBusCredentials *credentials)
@@ -1659,9 +1800,9 @@ dbus_bool_t
 _dbus_credentials_match (const DBusCredentials *expected_credentials,
                          const DBusCredentials *provided_credentials)
 {
-  if (provided_credentials->uid < 0)
+  if (provided_credentials->uid == DBUS_UID_UNSET)
     return FALSE;
-  else if (expected_credentials->uid < 0)
+  else if (expected_credentials->uid == DBUS_UID_UNSET)
     return FALSE;
   else if (provided_credentials->uid == 0)
     return TRUE;
@@ -1672,242 +1813,103 @@ _dbus_credentials_match (const DBusCredentials *expected_credentials,
 }
 
 /**
- * Gets group ID from group name.
- *
- * @param group_name name of the group
- * @param gid location to store group ID
- * @returns #TRUE if group was known
+ * Gets our process ID
+ * @returns process ID
  */
-dbus_bool_t
-_dbus_get_group_id (const DBusString *group_name,
-                    unsigned long    *gid)
+unsigned long
+_dbus_getpid (void)
 {
-  const char *group_c_str;
-  
-  group_c_str = _dbus_string_get_const_data (group_name);
-  
-  /* For now assuming that the getgrnam() and getgrgid() flavors
-   * always correspond to the pwnam flavors, if not we have
-   * to add more configure checks.
-   */
-  
-#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
-  {
-    struct group *g;
-    int result;
-    char buf[1024];
-    struct group g_str;
-
-    g = NULL;
-#ifdef HAVE_POSIX_GETPWNAME_R
-
-    result = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf),
-                         &g);
-#else
-    p = getgrnam_r (group_c_str, &g_str, buf, sizeof (buf));
-    result = 0;
-#endif /* !HAVE_POSIX_GETPWNAME_R */
-    if (result == 0 && g == &g_str)
-      {
-        *gid = g->gr_gid;
-        return TRUE;
-      }
-    else
-      {
-        _dbus_verbose ("Group %s unknown\n", group_c_str);
-        return FALSE;
-      }
-  }
-#else /* ! HAVE_GETPWNAM_R */
-  {
-    /* I guess we're screwed on thread safety here */
-    struct group *g;
-
-    g = getgrnam (group_c_str);
-
-    if (g != NULL)
-      {
-        *gid = g->gr_gid;
-        return TRUE;
-      }
-    else
-      {
-        _dbus_verbose ("Group %s unknown\n", group_c_str);
-        return FALSE;
-      }
-  }
-#endif  /* ! HAVE_GETPWNAM_R */
+  return getpid ();
 }
 
-/**
- * Gets all groups for a particular user. Returns #FALSE
- * if no memory, or user isn't known, but always initializes
- * group_ids to a NULL array.
- *
- * @todo failing to distinguish "out of memory" from
- * "unknown user" is kind of bogus and would probably
- * result in a failure in a comprehensive test suite.
- *
- * @param uid the user ID
- * @param group_ids return location for array of group IDs
- * @param n_group_ids return location for length of returned array
- * @returns #TRUE on success
+/** Gets our UID
+ * @returns process UID
  */
-dbus_bool_t
-_dbus_get_groups (unsigned long   uid,
-                  unsigned long **group_ids,
-                  int            *n_group_ids)
+dbus_uid_t
+_dbus_getuid (void)
 {
-  DBusCredentials creds;
-  DBusString username;
-  const char *username_c;
-  dbus_bool_t retval;
-  
-  *group_ids = NULL;
-  *n_group_ids = 0;
-
-  retval = FALSE;
-
-  if (!_dbus_string_init (&username))
-    return FALSE;
-
-  if (!get_user_info (NULL, uid, &creds,
-                      NULL, &username) ||
-      creds.gid < 0)
-    goto out;
-
-  username_c = _dbus_string_get_const_data (&username);
-  
-#ifdef HAVE_GETGROUPLIST
-  {
-    gid_t *buf;
-    int buf_count;
-    int i;
-    
-    buf_count = 17;
-    buf = dbus_new (gid_t, buf_count);
-    if (buf == NULL)
-      goto out;
-    
-    if (getgrouplist (username_c,
-                      creds.gid,
-                      buf, &buf_count) < 0)
-      {
-        gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0]));
-        if (new == NULL)
-          {
-            dbus_free (buf);
-            goto out;
-          }
-        
-        buf = new;
-
-        getgrouplist (username_c, creds.gid, buf, &buf_count);
-      }
-
-    *group_ids = dbus_new (unsigned long, buf_count);
-    if (*group_ids == NULL)
-      {
-        dbus_free (buf);
-        goto out;
-      }
-    
-    for (i = 0; i < buf_count; ++i)
-      (*group_ids)[i] = buf[i];
-
-    *n_group_ids = buf_count;
-    
-    dbus_free (buf);
-  }
-#else  /* HAVE_GETGROUPLIST */
-  {
-    /* We just get the one group ID */
-    *group_ids = dbus_new (unsigned long, 1);
-    if (*group_ids == NULL)
-      goto out;
-
-    *n_group_ids = 1;
-
-    (*group_ids)[0] = creds.gid;
-  }
-#endif /* HAVE_GETGROUPLIST */
-
-    retval = TRUE;
-    
-  out:
-    _dbus_string_free (&username);
-    return retval;
+  return getuid ();
 }
 
-/**
- * Appends the uid of the current process to the given string.
- *
- * @param str the string to append to
- * @returns #TRUE on success
+#ifdef DBUS_BUILD_TESTS
+/** Gets our GID
+ * @returns process GID
  */
-dbus_bool_t
-_dbus_string_append_our_uid (DBusString *str)
+dbus_gid_t
+_dbus_getgid (void)
 {
-  return _dbus_string_append_int (str, getuid ());
+  return getgid ();
 }
+#endif
 
-/**
- * Gets our process ID
- * @returns process ID
- */
-unsigned long
-_dbus_getpid (void)
+_DBUS_DEFINE_GLOBAL_LOCK (atomic);
+
+#ifdef DBUS_USE_ATOMIC_INT_486
+/* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */
+/* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */
+static inline dbus_int32_t
+atomic_exchange_and_add (DBusAtomic            *atomic,
+                         volatile dbus_int32_t  val)
 {
-  return getpid ();
-}
+  register dbus_int32_t result;
 
-_DBUS_DEFINE_GLOBAL_LOCK (atomic);
+  __asm__ __volatile__ ("lock; xaddl %0,%1"
+                        : "=r" (result), "=m" (atomic->value)
+                       : "0" (val), "m" (atomic->value));
+  return result;
+}
+#endif
 
 /**
  * Atomically increments an integer
  *
  * @param atomic pointer to the integer to increment
- * @returns the value after incrementing
+ * @returns the value before incrementing
  *
  * @todo implement arch-specific faster atomic ops
  */
-dbus_atomic_t
-_dbus_atomic_inc (dbus_atomic_t *atomic)
+dbus_int32_t
+_dbus_atomic_inc (DBusAtomic *atomic)
 {
-  dbus_atomic_t res;
-  
+#ifdef DBUS_USE_ATOMIC_INT_486
+  return atomic_exchange_and_add (atomic, 1);
+#else
+  dbus_int32_t res;
   _DBUS_LOCK (atomic);
-  *atomic += 1;
-  res = *atomic;
+  res = atomic->value;
+  atomic->value += 1;
   _DBUS_UNLOCK (atomic);
   return res;
+#endif
 }
 
 /**
  * Atomically decrement an integer
  *
  * @param atomic pointer to the integer to decrement
- * @returns the value after decrementing
+ * @returns the value before decrementing
  *
  * @todo implement arch-specific faster atomic ops
  */
-dbus_atomic_t
-_dbus_atomic_dec (dbus_atomic_t *atomic)
+dbus_int32_t
+_dbus_atomic_dec (DBusAtomic *atomic)
 {
-  dbus_atomic_t res;
+#ifdef DBUS_USE_ATOMIC_INT_486
+  return atomic_exchange_and_add (atomic, -1);
+#else
+  dbus_int32_t res;
   
   _DBUS_LOCK (atomic);
-  *atomic -= 1;
-  res = *atomic;
+  res = atomic->value;
+  atomic->value -= 1;
   _DBUS_UNLOCK (atomic);
   return res;
+#endif
 }
 
 /**
  * Wrapper for poll().
  *
- * @todo need a fallback implementation using select()
- *
  * @param fds the file descriptors to poll
  * @param n_fds number of descriptors in the array
  * @param timeout_milliseconds timeout or -1 for infinite
@@ -1963,40 +1965,41 @@ _dbus_poll (DBusPollFD *fds,
 
   for (i = 0; i < n_fds; i++)
     {
-      DBusPollFD f = fds[i];
+      DBusPollFD *fdp = &fds[i];
 
-      if (f.events & _DBUS_POLLIN)
-       FD_SET (f.fd, &read_set);
+      if (fdp->events & _DBUS_POLLIN)
+       FD_SET (fdp->fd, &read_set);
 
-      if (f.events & _DBUS_POLLOUT)
-       FD_SET (f.fd, &write_set);
+      if (fdp->events & _DBUS_POLLOUT)
+       FD_SET (fdp->fd, &write_set);
 
-      FD_SET (f.fd, &err_set);
+      FD_SET (fdp->fd, &err_set);
 
-      max_fd = MAX (max_fd, f.fd);
+      max_fd = MAX (max_fd, fdp->fd);
     }
     
   tv.tv_sec = timeout_milliseconds / 1000;
   tv.tv_usec = (timeout_milliseconds % 1000) * 1000;
 
-  ready = select (max_fd + 1, &read_set, &write_set, &err_set, &tv);
+  ready = select (max_fd + 1, &read_set, &write_set, &err_set,
+                  timeout_milliseconds < 0 ? NULL : &tv);
 
   if (ready > 0)
     {
       for (i = 0; i < n_fds; i++)
        {
-         DBusPollFD f = fds[i];
+         DBusPollFD *fdp = &fds[i];
 
-         f.revents = 0;
+         fdp->revents = 0;
 
-         if (FD_ISSET (f.fd, &read_set))
-           f.revents |= _DBUS_POLLIN;
+         if (FD_ISSET (fdp->fd, &read_set))
+           fdp->revents |= _DBUS_POLLIN;
 
-         if (FD_ISSET (f.fd, &write_set))
-           f.revents |= _DBUS_POLLOUT;
+         if (FD_ISSET (fdp->fd, &write_set))
+           fdp->revents |= _DBUS_POLLOUT;
 
-         if (FD_ISSET (f.fd, &err_set))
-           f.revents |= _DBUS_POLLERR;
+         if (FD_ISSET (fdp->fd, &err_set))
+           fdp->revents |= _DBUS_POLLERR;
        }
     }
 
@@ -2115,7 +2118,7 @@ _dbus_file_get_contents (DBusString       *str,
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
                       "File size %lu of \"%s\" is too large.",
-                      filename_c, (unsigned long) sb.st_size);
+                      (unsigned long) sb.st_size, filename_c);
       close (fd);
       return FALSE;
     }
@@ -2205,12 +2208,14 @@ _dbus_string_save_to_file (const DBusString *str,
   if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
       return FALSE;
     }
   
   if (!_dbus_string_append (&tmp_filename, "."))
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
       return FALSE;
     }
 
@@ -2218,6 +2223,7 @@ _dbus_string_save_to_file (const DBusString *str,
   if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES))
     {
       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+      _dbus_string_free (&tmp_filename);
       return FALSE;
     }
     
@@ -2446,215 +2452,82 @@ _dbus_concat_dir_and_file (DBusString       *dir,
                             _dbus_string_get_length (dir));
 }
 
-/**
- * Get the directory name from a complete filename
- * @param filename the filename
- * @param dirname string to append directory name to
- * @returns #FALSE if no memory
- */
-dbus_bool_t
-_dbus_string_get_dirname  (const DBusString *filename,
-                           DBusString       *dirname)
+static void
+pseudorandom_generate_random_bytes_buffer (char *buffer,
+                                           int   n_bytes)
 {
-  int sep;
-  
-  _dbus_assert (filename != dirname);
-  _dbus_assert (filename != NULL);
-  _dbus_assert (dirname != NULL);
-
-  /* Ignore any separators on the end */
-  sep = _dbus_string_get_length (filename);
-  if (sep == 0)
-    return _dbus_string_append (dirname, "."); /* empty string passed in */
-    
-  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
-    --sep;
-
-  _dbus_assert (sep >= 0);
+  unsigned long tv_usec;
+  int i;
   
-  if (sep == 0)
-    return _dbus_string_append (dirname, "/");
+  /* fall back to pseudorandom */
+  _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
+                 n_bytes);
   
-  /* Now find the previous separator */
-  _dbus_string_find_byte_backward (filename, sep, '/', &sep);
-  if (sep < 0)
-    return _dbus_string_append (dirname, ".");
+  _dbus_get_current_time (NULL, &tv_usec);
+  srand (tv_usec);
   
-  /* skip multiple separators */
-  while (sep > 0 && _dbus_string_get_byte (filename, sep - 1) == '/')
-    --sep;
+  i = 0;
+  while (i < n_bytes)
+    {
+      double r;
+      unsigned int b;
+          
+      r = rand ();
+      b = (r / (double) RAND_MAX) * 255.0;
 
-  _dbus_assert (sep >= 0);
-  
-  if (sep == 0 &&
-      _dbus_string_get_byte (filename, 0) == '/')
-    return _dbus_string_append (dirname, "/");
-  else
-    return _dbus_string_copy_len (filename, 0, sep - 0,
-                                  dirname, _dbus_string_get_length (dirname));
-}
+      buffer[i] = b;
 
-/**
- * Checks whether the filename is an absolute path
- *
- * @param filename the filename
- * @returns #TRUE if an absolute path
- */
-dbus_bool_t
-_dbus_path_is_absolute (const DBusString *filename)
-{
-  if (_dbus_string_get_length (filename) > 0)
-    return _dbus_string_get_byte (filename, 0) == '/';
-  else
-    return FALSE;
+      ++i;
+    }
 }
 
-struct DBusDirIter
+static dbus_bool_t
+pseudorandom_generate_random_bytes (DBusString *str,
+                                    int         n_bytes)
 {
-  DIR *d;
+  int old_len;
+  char *p;
   
-};
-
-/**
- * Open a directory to iterate over.
- *
- * @param filename the directory name
- * @param error exception return object or #NULL
- * @returns new iterator, or #NULL on error
- */
-DBusDirIter*
-_dbus_directory_open (const DBusString *filename,
-                      DBusError        *error)
-{
-  DIR *d;
-  DBusDirIter *iter;
-  const char *filename_c;
+  old_len = _dbus_string_get_length (str);
 
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
-  filename_c = _dbus_string_get_const_data (filename);
+  if (!_dbus_string_lengthen (str, n_bytes))
+    return FALSE;
 
-  d = opendir (filename_c);
-  if (d == NULL)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to read directory \"%s\": %s",
-                      filename_c,
-                      _dbus_strerror (errno));
-      return NULL;
-    }
-  iter = dbus_new0 (DBusDirIter, 1);
-  if (iter == NULL)
-    {
-      closedir (d);
-      dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
-                      "Could not allocate memory for directory iterator");
-      return NULL;
-    }
+  p = _dbus_string_get_data_len (str, old_len, n_bytes);
 
-  iter->d = d;
+  pseudorandom_generate_random_bytes_buffer (p, n_bytes);
 
-  return iter;
+  return TRUE;
 }
 
 /**
- * Get next file in the directory. Will not return "." or ".."  on
- * UNIX. If an error occurs, the contents of "filename" are
- * undefined. The error is never set if the function succeeds.
+ * Fills n_bytes of the given buffer with random bytes.
  *
- * @todo for thread safety, I think we have to use
- * readdir_r(). (GLib has the same issue, should file a bug.)
- *
- * @param iter the iterator
- * @param filename string to be set to the next file in the dir
- * @param error return location for error
- * @returns #TRUE if filename was filled in with a new filename
+ * @param buffer an allocated buffer
+ * @param n_bytes the number of bytes in buffer to write to
  */
-dbus_bool_t
-_dbus_directory_get_next_file (DBusDirIter      *iter,
-                               DBusString       *filename,
-                               DBusError        *error)
+void
+_dbus_generate_random_bytes_buffer (char *buffer,
+                                    int   n_bytes)
 {
-  struct dirent *ent;
+  DBusString str;
 
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
- again:
-  errno = 0;
-  ent = readdir (iter->d);
-  if (ent == NULL)
-    {
-      if (errno != 0)
-        dbus_set_error (error,
-                        _dbus_error_from_errno (errno),
-                        "%s", _dbus_strerror (errno));
-      return FALSE;
-    }
-  else if (ent->d_name[0] == '.' &&
-           (ent->d_name[1] == '\0' ||
-            (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
-    goto again;
-  else
+  if (!_dbus_string_init (&str))
     {
-      _dbus_string_set_length (filename, 0);
-      if (!_dbus_string_append (filename, ent->d_name))
-        {
-          dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
-                          "No memory to read directory entry");
-          return FALSE;
-        }
-      else
-        return TRUE;
+      pseudorandom_generate_random_bytes_buffer (buffer, n_bytes);
+      return;
     }
-}
 
-/**
- * Closes a directory iteration.
- */
-void
-_dbus_directory_close (DBusDirIter *iter)
-{
-  closedir (iter->d);
-  dbus_free (iter);
-}
-
-static dbus_bool_t
-pseudorandom_generate_random_bytes (DBusString *str,
-                                    int         n_bytes)
-{
-  int old_len;
-  unsigned long tv_usec;
-  int i;
-  
-  old_len = _dbus_string_get_length (str);
-
-  /* fall back to pseudorandom */
-  _dbus_verbose ("Falling back to pseudorandom for %d bytes\n",
-                 n_bytes);
-  
-  _dbus_get_current_time (NULL, &tv_usec);
-  srand (tv_usec);
-  
-  i = 0;
-  while (i < n_bytes)
+  if (!_dbus_generate_random_bytes (&str, n_bytes))
     {
-      double r;
-      unsigned int b;
-          
-      r = rand ();
-      b = (r / (double) RAND_MAX) * 255.0;
-          
-      if (!_dbus_string_append_byte (str, b))
-        goto failed;
-          
-      ++i;
+      _dbus_string_free (&str);
+      pseudorandom_generate_random_bytes_buffer (buffer, n_bytes);
+      return;
     }
 
-  return TRUE;
+  _dbus_string_copy_to_buffer (&str, buffer, n_bytes);
 
- failed:
-  _dbus_string_set_length (str, old_len);
-  return FALSE;
+  _dbus_string_free (&str);
 }
 
 /**
@@ -2898,98 +2771,6 @@ _dbus_exit (int code)
 }
 
 /**
- * stat() wrapper.
- *
- * @param filename the filename to stat
- * @param statbuf the stat info to fill in
- * @param error return location for error
- * @returns #FALSE if error was set
- */
-dbus_bool_t
-_dbus_stat (const DBusString *filename,
-            DBusStat         *statbuf,
-            DBusError        *error)
-{
-  const char *filename_c;
-  struct stat sb;
-
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
-  filename_c = _dbus_string_get_const_data (filename);
-
-  if (stat (filename_c, &sb) < 0)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "%s", _dbus_strerror (errno));
-      return FALSE;
-    }
-
-  statbuf->mode = sb.st_mode;
-  statbuf->nlink = sb.st_nlink;
-  statbuf->uid = sb.st_uid;
-  statbuf->gid = sb.st_gid;
-  statbuf->size = sb.st_size;
-  statbuf->atime = sb.st_atime;
-  statbuf->mtime = sb.st_mtime;
-  statbuf->ctime = sb.st_ctime;
-
-  return TRUE;
-}
-
-/**
- * Creates a full-duplex pipe (as in socketpair()).
- * Sets both ends of the pipe nonblocking.
- *
- * @param fd1 return location for one end
- * @param fd2 return location for the other end
- * @param blocking #TRUE if pipe should be blocking
- * @param error error return
- * @returns #FALSE on failure (if error is set)
- */
-dbus_bool_t
-_dbus_full_duplex_pipe (int        *fd1,
-                        int        *fd2,
-                        dbus_bool_t blocking,
-                        DBusError  *error)
-{
-#ifdef HAVE_SOCKETPAIR
-  int fds[2];
-
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
-  
-  if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Could not create full-duplex pipe");
-      return FALSE;
-    }
-
-  if (!blocking &&
-      (!_dbus_set_fd_nonblocking (fds[0], NULL) ||
-       !_dbus_set_fd_nonblocking (fds[1], NULL)))
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Could not set full-duplex pipe nonblocking");
-      
-      close (fds[0]);
-      close (fds[1]);
-      
-      return FALSE;
-    }
-  
-  *fd1 = fds[0];
-  *fd2 = fds[1];
-  
-  return TRUE;  
-#else
-  _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n");
-  dbus_set_error (error, DBUS_ERROR_FAILED,
-                  "_dbus_full_duplex_pipe() not implemented on this OS");
-  return FALSE;
-#endif
-}
-
-/**
  * Closes a file descriptor.
  *
  * @param fd the file descriptor
@@ -3056,6 +2837,7 @@ _dbus_set_fd_nonblocking (int             fd,
   return TRUE;
 }
 
+#if !defined (DBUS_DISABLE_ASSERT) || defined(DBUS_BUILD_TESTS)
 /**
  * On GNU libc systems, print a crude backtrace to the verbose log.
  * On other systems, print "no backtrace support"
@@ -3086,295 +2868,110 @@ _dbus_print_backtrace (void)
   _dbus_verbose ("  D-BUS not compiled with backtrace support\n");
 #endif
 }
+#endif /* asserts or tests enabled */
 
-/**
- * Does the chdir, fork, setsid, etc. to become a daemon process.
- *
- * @param pidfile #NULL, or pidfile to create
- * @param error return location for errors
- * @returns #FALSE on failure
- */
-dbus_bool_t
-_dbus_become_daemon (const DBusString *pidfile,
-                     DBusError        *error)
-{
-  const char *s;
-  pid_t child_pid;
-
-  if (chdir ("/") < 0)
-    {
-      dbus_set_error (error, DBUS_ERROR_FAILED,
-                      "Could not chdir() to root directory");
-      return FALSE;
-    }
-
-  switch ((child_pid = fork ()))
-    {
-    case -1:
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to fork daemon: %s", _dbus_strerror (errno));
-      return FALSE;
-      break;
-
-    case 0:
-      s = _dbus_getenv ("DBUS_DEBUG_OUTPUT");
-      if (s == NULL || *s == '\0')
-        {
-          int dev_null_fd;
-
-          /* silently ignore failures here, if someone
-           * doesn't have /dev/null we may as well try
-           * to continue anyhow
-           */
-
-          dev_null_fd = open ("/dev/null", O_RDWR);
-          if (dev_null_fd >= 0)
-            {
-              dup2 (dev_null_fd, 0);
-              dup2 (dev_null_fd, 1);
-              dup2 (dev_null_fd, 2);
-            }
-        }
-
-      /* Get a predictable umask */
-      umask (022);
-      break;
-
-    default:
-      if (pidfile)
-        {
-          if (!_dbus_write_pid_file (pidfile,
-                                     child_pid,
-                                     error))
-            {
-              kill (child_pid, SIGTERM);
-              return FALSE;
-            }
-        }
-      _exit (0);
-      break;
-    }
-
-  if (setsid () == -1)
-    _dbus_assert_not_reached ("setsid() failed");
-  
-  return TRUE;
-}
 
 /**
- * Creates a file containing the process ID.
+ * Gets a UID from a UID string.
  *
- * @param filename the filename to write to
- * @param pid our process ID
- * @param error return location for errors
- * @returns #FALSE on failure
+ * @param uid_str the UID in string form
+ * @param uid UID to fill in
+ * @returns #TRUE if successfully filled in UID
  */
 dbus_bool_t
-_dbus_write_pid_file (const DBusString *filename,
-                      unsigned long     pid,
-                     DBusError        *error)
+_dbus_parse_uid (const DBusString      *uid_str,
+                 dbus_uid_t            *uid)
 {
-  const char *cfilename;
-  int fd;
-  FILE *f;
-
-  cfilename = _dbus_string_get_const_data (filename);
-  
-  fd = open (cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644);
+  int end;
+  long val;
   
-  if (fd < 0)
+  if (_dbus_string_get_length (uid_str) == 0)
     {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to open \"%s\": %s", cfilename,
-                      _dbus_strerror (errno));
+      _dbus_verbose ("UID string was zero length\n");
       return FALSE;
     }
 
-  if ((f = fdopen (fd, "w")) == NULL)
+  val = -1;
+  end = 0;
+  if (!_dbus_string_parse_int (uid_str, 0, &val,
+                               &end))
     {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to fdopen fd %d: %s", fd, _dbus_strerror (errno));
-      close (fd);
+      _dbus_verbose ("could not parse string as a UID\n");
       return FALSE;
     }
   
-  if (fprintf (f, "%lu\n", pid) < 0)
+  if (end != _dbus_string_get_length (uid_str))
     {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to write to \"%s\": %s", cfilename,
-                      _dbus_strerror (errno));
+      _dbus_verbose ("string contained trailing stuff after UID\n");
       return FALSE;
     }
 
-  if (fclose (f) == EOF)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to close \"%s\": %s", cfilename,
-                      _dbus_strerror (errno));
-      return FALSE;
-    }
-  
+  *uid = val;
+
   return TRUE;
 }
 
 /**
- * Changes the user and group the bus is running as.
+ * Creates a full-duplex pipe (as in socketpair()).
+ * Sets both ends of the pipe nonblocking.
  *
- * @param uid the new user ID
- * @param gid the new group ID
- * @param error return location for errors
- * @returns #FALSE on failure
+ * @todo libdbus only uses this for the debug-pipe server, so in
+ * principle it could be in dbus-sysdeps-util.c, except that
+ * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the
+ * debug-pipe server is used.
+ * 
+ * @param fd1 return location for one end
+ * @param fd2 return location for the other end
+ * @param blocking #TRUE if pipe should be blocking
+ * @param error error return
+ * @returns #FALSE on failure (if error is set)
  */
 dbus_bool_t
-_dbus_change_identity  (unsigned long  uid,
-                        unsigned long  gid,
-                        DBusError     *error)
+_dbus_full_duplex_pipe (int        *fd1,
+                        int        *fd2,
+                        dbus_bool_t blocking,
+                        DBusError  *error)
 {
-  /* Set GID first, or the setuid may remove our permission
-   * to change the GID
-   */
-  if (setgid (gid) < 0)
-    {
-      dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to set GID to %lu: %s", gid,
-                      _dbus_strerror (errno));
-      return FALSE;
-    }
+#ifdef HAVE_SOCKETPAIR
+  int fds[2];
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
   
-  if (setuid (uid) < 0)
+  if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0)
     {
       dbus_set_error (error, _dbus_error_from_errno (errno),
-                      "Failed to set UID to %lu: %s", uid,
-                      _dbus_strerror (errno));
+                      "Could not create full-duplex pipe");
       return FALSE;
     }
-  
-  return TRUE;
-}
 
-/** Installs a UNIX signal handler
- *
- * @param sig the signal to handle
- * @param handler the handler
- */
-void
-_dbus_set_signal_handler (int               sig,
-                          DBusSignalHandler handler)
-{
-  struct sigaction act;
-  sigset_t empty_mask;
-  
-  sigemptyset (&empty_mask);
-  act.sa_handler = handler;
-  act.sa_mask    = empty_mask;
-  act.sa_flags   = 0;
-  sigaction (sig,  &act, 0);
-}
-
-
-#ifdef DBUS_BUILD_TESTS
-#include <stdlib.h>
-static void
-check_dirname (const char *filename,
-               const char *dirname)
-{
-  DBusString f, d;
-  
-  _dbus_string_init_const (&f, filename);
-
-  if (!_dbus_string_init (&d))
-    _dbus_assert_not_reached ("no memory");
-
-  if (!_dbus_string_get_dirname (&f, &d))
-    _dbus_assert_not_reached ("no memory");
-
-  if (!_dbus_string_equal_c_str (&d, dirname))
-    {
-      _dbus_warn ("For filename \"%s\" got dirname \"%s\" and expected \"%s\"\n",
-                  filename,
-                  _dbus_string_get_const_data (&d),
-                  dirname);
-      exit (1);
-    }
-
-  _dbus_string_free (&d);
-}
-
-static void
-check_path_absolute (const char *path,
-                     dbus_bool_t expected)
-{
-  DBusString p;
-
-  _dbus_string_init_const (&p, path);
-
-  if (_dbus_path_is_absolute (&p) != expected)
+  if (!blocking &&
+      (!_dbus_set_fd_nonblocking (fds[0], NULL) ||
+       !_dbus_set_fd_nonblocking (fds[1], NULL)))
     {
-      _dbus_warn ("For path \"%s\" expected absolute = %d got %d\n",
-                  path, expected, _dbus_path_is_absolute (&p));
-      exit (1);
+      dbus_set_error (error, _dbus_error_from_errno (errno),
+                      "Could not set full-duplex pipe nonblocking");
+      
+      close (fds[0]);
+      close (fds[1]);
+      
+      return FALSE;
     }
-}
-
-/**
- * Unit test for dbus-sysdeps.c.
- * 
- * @returns #TRUE on success.
- */
-dbus_bool_t
-_dbus_sysdeps_test (void)
-{
-  DBusString str;
-  double val;
-  int pos;
   
-  check_dirname ("foo", ".");
-  check_dirname ("foo/bar", "foo");
-  check_dirname ("foo//bar", "foo");
-  check_dirname ("foo///bar", "foo");
-  check_dirname ("foo/bar/", "foo");
-  check_dirname ("foo//bar/", "foo");
-  check_dirname ("foo///bar/", "foo");
-  check_dirname ("foo/bar//", "foo");
-  check_dirname ("foo//bar////", "foo");
-  check_dirname ("foo///bar///////", "foo");
-  check_dirname ("/foo", "/");
-  check_dirname ("////foo", "/");
-  check_dirname ("/foo/bar", "/foo");
-  check_dirname ("/foo//bar", "/foo");
-  check_dirname ("/foo///bar", "/foo");
-  check_dirname ("/", "/");
-  check_dirname ("///", "/");
-  check_dirname ("", ".");  
-
-
-  _dbus_string_init_const (&str, "3.5");
-  if (!_dbus_string_parse_double (&str,
-                                 0, &val, &pos))
-    {
-      _dbus_warn ("Failed to parse double");
-      exit (1);
-    }
-  if (val != 3.5)
-    {
-      _dbus_warn ("Failed to parse 3.5 correctly, got: %f", val);
-      exit (1);
-    }
-  if (pos != 3)
-    {
-      _dbus_warn ("_dbus_string_parse_double of \"3.5\" returned wrong position %d", pos);
-      exit (1);
-    }
+  *fd1 = fds[0];
+  *fd2 = fds[1];
 
-  check_path_absolute ("/", TRUE);
-  check_path_absolute ("/foo", TRUE);
-  check_path_absolute ("", FALSE);
-  check_path_absolute ("foo", FALSE);
-  check_path_absolute ("foo/bar", FALSE);
+  _dbus_verbose ("full-duplex pipe %d <-> %d\n",
+                 *fd1, *fd2);
   
-  return TRUE;
+  return TRUE;  
+#else
+  _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n");
+  dbus_set_error (error, DBUS_ERROR_FAILED,
+                  "_dbus_full_duplex_pipe() not implemented on this OS");
+  return FALSE;
+#endif
 }
-#endif /* DBUS_BUILD_TESTS */
 
 /** @} end of sysdeps */
 
+/* tests in dbus-sysdeps-util.c */