/* -*- mode: C; c-file-style: "gnu" -*- */
/* dbus-sysdeps.c Wrappers around system/libc features (internal to D-BUS implementation)
*
- * Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
*
* Licensed under the Academic Free License version 1.2
*
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
+#include <grp.h>
#ifdef HAVE_WRITEV
#include <sys/uio.h>
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
+#ifdef HAVE_BACKTRACE
+#include <execinfo.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))
+#endif
+
/**
* @addtogroup DBusInternalsUtils
* @{
dbus_bool_t
_dbus_setenv (const char *varname, const char *value)
{
+#ifdef HAVE_SETENV
return (setenv (varname, value, TRUE) == 0);
+#else
+ DBusString str;
+ char *putenv_value;
+
+ if (!_dbus_string_init (&str))
+ return FALSE;
+
+ if (!_dbus_string_append (&str, varname) ||
+ !_dbus_string_append (&str, "=") ||
+ !_dbus_string_append (&str, value) ||
+ !_dbus_string_steal_data (&str, &putenv_value))
+ {
+ _dbus_string_free (&str);
+ return FALSE;
+ }
+
+ _dbus_string_free (&str);
+
+ return (putenv (putenv_value) == 0);
+#endif
}
/**
* the data it reads to the DBusString buffer. It appends
* up to the given count, and returns the same value
* and same errno as read(). The only exception is that
- * _dbus_read() handles EINTR for you.
+ * _dbus_read() handles EINTR for you. _dbus_read() can
+ * return ENOMEM, even though regular UNIX read doesn't.
*
* @param fd the file descriptor to read from
* @param buffer the buffer to append data to
return -1;
}
- _dbus_string_get_data_len (buffer, &data, start, count);
+ data = _dbus_string_get_data_len (buffer, start, count);
again:
const char *data;
int bytes_written;
- _dbus_string_get_const_data_len (buffer, &data, start, len);
+ data = _dbus_string_get_const_data_len (buffer, start, len);
again:
const char *data2;
int bytes_written;
- _dbus_string_get_const_data_len (buffer1, &data1, start1, len1);
+ data1 = _dbus_string_get_const_data_len (buffer1, start1, len1);
if (buffer2 != NULL)
- _dbus_string_get_const_data_len (buffer2, &data2, start2, len2);
+ data2 = _dbus_string_get_const_data_len (buffer2, start2, len2);
else
{
data2 = NULL;
* nonblocking.
*
* @param path the path to UNIX domain socket
- * @param result return location for error code
+ * @param error return location for error code
* @returns connection file descriptor or -1 on error
*/
int
_dbus_connect_unix_socket (const char *path,
- DBusResultCode *result)
+ DBusError *error)
{
int fd;
struct sockaddr_un addr;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
- fd = socket (AF_LOCAL, SOCK_STREAM, 0);
+ fd = socket (PF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
-
- _dbus_verbose ("Failed to create socket: %s\n",
- _dbus_strerror (errno));
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to create socket: %s",
+ _dbus_strerror (errno));
return -1;
}
_DBUS_ZERO (addr);
- addr.sun_family = AF_LOCAL;
+ addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH);
addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0';
if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
-
- _dbus_verbose ("Failed to connect to socket %s: %s\n",
- path, _dbus_strerror (errno));
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to connect to socket %s: %s",
+ path, _dbus_strerror (errno));
close (fd);
fd = -1;
return -1;
}
- if (!_dbus_set_fd_nonblocking (fd, result))
+ if (!_dbus_set_fd_nonblocking (fd, error))
{
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
close (fd);
fd = -1;
* set to be nonblocking.
*
* @param path the socket name
- * @param result return location for errors
+ * @param error return location for errors
* @returns the listening file descriptor or -1 on error
*/
int
_dbus_listen_unix_socket (const char *path,
- DBusResultCode *result)
+ DBusError *error)
{
int listen_fd;
struct sockaddr_un addr;
- listen_fd = socket (AF_LOCAL, SOCK_STREAM, 0);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ listen_fd = socket (PF_UNIX, SOCK_STREAM, 0);
if (listen_fd < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to create socket \"%s\": %s\n",
- path, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to create socket \"%s\": %s",
+ path, _dbus_strerror (errno));
return -1;
}
_DBUS_ZERO (addr);
- addr.sun_family = AF_LOCAL;
+ addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH);
addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH] = '\0';
if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to bind socket \"%s\": %s\n",
- path, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to bind socket \"%s\": %s",
+ path, _dbus_strerror (errno));
close (listen_fd);
return -1;
}
if (listen (listen_fd, 30 /* backlog */) < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to listen on socket \"%s\": %s\n",
- path, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to listen on socket \"%s\": %s",
+ path, _dbus_strerror (errno));
close (listen_fd);
return -1;
}
- if (!_dbus_set_fd_nonblocking (listen_fd, result))
+ if (!_dbus_set_fd_nonblocking (listen_fd, error))
{
+ _DBUS_ASSERT_ERROR_IS_SET (error);
close (listen_fd);
return -1;
}
*
* @param host the host name to connect to
* @param port the prot to connect to
- * @param result return location for error code
+ * @param error return location for error code
* @returns connection file descriptor or -1 on error
*/
int
_dbus_connect_tcp_socket (const char *host,
dbus_uint32_t port,
- DBusResultCode *result)
+ DBusError *error)
{
int fd;
struct sockaddr_in addr;
struct hostent *he;
struct in_addr *haddr;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
fd = socket (AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
-
- _dbus_verbose ("Failed to create socket: %s\n",
- _dbus_strerror (errno));
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to create socket: %s",
+ _dbus_strerror (errno));
return -1;
}
he = gethostbyname (host);
if (he == NULL)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to lookup hostname: %s\n",
- host);
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to lookup hostname: %s",
+ host);
return -1;
}
if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
-
- _dbus_verbose ("Failed to connect to socket %s: %s:%d\n",
- host, port, _dbus_strerror (errno));
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to connect to socket %s: %s:%d",
+ host, _dbus_strerror (errno), port);
close (fd);
fd = -1;
return -1;
}
- if (!_dbus_set_fd_nonblocking (fd, result))
+ if (!_dbus_set_fd_nonblocking (fd, error))
{
close (fd);
fd = -1;
*
* @param host the host name to listen on
* @param port the prot to listen on
- * @param result return location for errors
+ * @param error return location for errors
* @returns the listening file descriptor or -1 on error
*/
int
_dbus_listen_tcp_socket (const char *host,
dbus_uint32_t port,
- DBusResultCode *result)
+ DBusError *error)
{
int listen_fd;
struct sockaddr_in addr;
struct hostent *he;
struct in_addr *haddr;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
listen_fd = socket (AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to create socket \"%s:%d\": %s\n",
- host, port, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to create socket \"%s:%d\": %s",
+ host, port, _dbus_strerror (errno));
return -1;
}
he = gethostbyname (host);
if (he == NULL)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to lookup hostname: %s\n",
- host);
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "Failed to lookup hostname: %s",
+ host);
return -1;
}
if (bind (listen_fd, (struct sockaddr*) &addr, sizeof (struct sockaddr)))
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to bind socket \"%s:%d\": %s\n",
- host, port, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to bind socket \"%s:%d\": %s",
+ host, port, _dbus_strerror (errno));
close (listen_fd);
return -1;
}
if (listen (listen_fd, 30 /* backlog */) < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to listen on socket \"%s:%d\": %s\n",
- host, port, _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to listen on socket \"%s:%d\": %s",
+ host, port, _dbus_strerror (errno));
close (listen_fd);
return -1;
}
- if (!_dbus_set_fd_nonblocking (listen_fd, result))
+ if (!_dbus_set_fd_nonblocking (listen_fd, error))
{
close (listen_fd);
return -1;
return listen_fd;
}
-/* try to read a single byte and return #TRUE if we read it
- * and it's equal to nul.
- */
-static dbus_bool_t
-read_credentials_byte (int client_fd,
- DBusResultCode *result)
-{
- char buf[1];
- int bytes_read;
-
- again:
- bytes_read = read (client_fd, buf, 1);
- if (bytes_read < 0)
- {
- if (errno == EINTR)
- goto again;
- else
- {
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to read credentials byte: %s\n",
- _dbus_strerror (errno));
- return FALSE;
- }
- }
- else if (bytes_read == 0)
- {
- dbus_set_result (result, DBUS_RESULT_IO_ERROR);
- _dbus_verbose ("EOF reading credentials byte\n");
- return FALSE;
- }
- else
- {
- _dbus_assert (bytes_read == 1);
-
- if (buf[0] != '\0')
- {
- dbus_set_result (result, DBUS_RESULT_FAILED);
- _dbus_verbose ("Credentials byte was not nul\n");
- return FALSE;
- }
-
- _dbus_verbose ("read credentials byte\n");
-
- return TRUE;
- }
-}
-
static dbus_bool_t
write_credentials_byte (int server_fd,
- DBusResultCode *result)
+ DBusError *error)
{
int bytes_written;
char buf[1] = { '\0' };
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
again:
if (bytes_written < 0)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
- _dbus_verbose ("Failed to write credentials byte: %s\n",
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to write credentials byte: %s",
_dbus_strerror (errno));
return FALSE;
}
else if (bytes_written == 0)
{
- dbus_set_result (result, DBUS_RESULT_IO_ERROR);
- _dbus_verbose ("wrote zero bytes writing credentials byte\n");
+ dbus_set_error (error, DBUS_ERROR_IO_ERROR,
+ "wrote zero bytes writing credentials byte");
return FALSE;
}
else
*
* @param client_fd the client file descriptor
* @param credentials struct to fill with credentials of client
- * @param result location to store result code
+ * @param error location to store error code
* @returns #TRUE on success
*/
dbus_bool_t
_dbus_read_credentials_unix_socket (int client_fd,
DBusCredentials *credentials,
- DBusResultCode *result)
+ DBusError *error)
{
+ struct msghdr msg;
+ struct iovec iov;
+ char buf;
+
+#ifdef HAVE_CMSGCRED
+ char cmsgmem[CMSG_SPACE (sizeof (struct cmsgcred))];
+ struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
+#endif
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* The POSIX spec certainly doesn't promise this, but
+ * we need these assertions to fail as soon as we're wrong about
+ * it so we can do the porting fixups
+ */
+ _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;
-
- if (read_credentials_byte (client_fd, result))
+
+#if defined(LOCAL_CREDS) && defined(HAVE_CMSGCRED)
+ /* Set the socket to receive credentials on the next message */
+ {
+ int on = 1;
+ if (setsockopt (client_fd, 0, LOCAL_CREDS, &on, sizeof (on)) < 0)
+ {
+ _dbus_verbose ("Unable to set LOCAL_CREDS socket option\n");
+ return FALSE;
+ }
+ }
+#endif
+
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+
+ memset (&msg, 0, sizeof (msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+#ifdef HAVE_CMSGCRED
+ memset (cmsgmem, 0, sizeof (cmsgmem));
+ msg.msg_control = cmsgmem;
+ msg.msg_controllen = sizeof (cmsgmem);
+#endif
+
+ again:
+ if (recvmsg (client_fd, &msg, 0) < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to read credentials byte: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if (buf != '\0')
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Credentials byte was not nul");
+ return FALSE;
+ }
+
+#ifdef HAVE_CMSGCRED
+ if (cmsg->cmsg_len < sizeof (cmsgmem) || cmsg->cmsg_type != SCM_CREDS)
{
+ dbus_set_error (error, DBUS_ERROR_FAILED);
+ _dbus_verbose ("Message from recvmsg() was not SCM_CREDS\n");
+ return FALSE;
+ }
+#endif
+
+ _dbus_verbose ("read credentials byte\n");
+
+ {
#ifdef SO_PEERCRED
- struct ucred cr;
- int cr_len = sizeof (cr);
+ struct ucred cr;
+ int cr_len = sizeof (cr);
- if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
- cr_len == sizeof (cr))
- {
- credentials->pid = cr.pid;
- credentials->uid = cr.uid;
- credentials->gid = cr.gid;
- _dbus_verbose ("Got credentials pid %d uid %d gid %d\n",
- credentials->pid,
- credentials->uid,
- credentials->gid);
- }
- else
- {
- _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
- cr_len, (int) sizeof (cr), _dbus_strerror (errno));
- }
-#else /* !SO_PEERCRED */
- _dbus_verbose ("Socket credentials not supported on this OS\n");
+ if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
+ cr_len == sizeof (cr))
+ {
+ credentials->pid = cr.pid;
+ credentials->uid = cr.uid;
+ credentials->gid = cr.gid;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n",
+ 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 */
+ _dbus_verbose ("Socket credentials not supported on this OS\n");
#endif
+ }
- return TRUE;
- }
- else
- return FALSE;
+ _dbus_verbose ("Credentials: pid %d uid %d gid %d\n",
+ credentials->pid,
+ credentials->uid,
+ credentials->gid);
+
+ return TRUE;
}
/**
* use sendmsg()/recvmsg() to transmit credentials.
*
* @param server_fd file descriptor for connection to server
- * @param result return location for error code
+ * @param error return location for error code
* @returns #TRUE if the byte was sent
*/
dbus_bool_t
_dbus_send_credentials_unix_socket (int server_fd,
- DBusResultCode *result)
+ DBusError *error)
{
- if (write_credentials_byte (server_fd, result))
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (write_credentials_byte (server_fd, error))
return TRUE;
else
return FALSE;
_dbus_accept (int listen_fd)
{
int client_fd;
+ struct sockaddr addr;
+ socklen_t addrlen;
+
+ addrlen = sizeof (addr);
retry:
- client_fd = accept (listen_fd, NULL, NULL);
+ client_fd = accept (listen_fd, &addr, &addrlen);
if (client_fd < 0)
{
if (!_dbus_string_lengthen (str, MAX_LONG_LEN))
return FALSE;
- _dbus_string_get_data_len (str, &buf, orig_len, MAX_LONG_LEN);
+ buf = _dbus_string_get_data_len (str, orig_len, MAX_LONG_LEN);
snprintf (buf, MAX_LONG_LEN, "%ld", value);
if (!_dbus_string_lengthen (str, MAX_ULONG_LEN))
return FALSE;
- _dbus_string_get_data_len (str, &buf, orig_len, MAX_ULONG_LEN);
+ buf = _dbus_string_get_data_len (str, orig_len, MAX_ULONG_LEN);
snprintf (buf, MAX_ULONG_LEN, "%lu", value);
if (!_dbus_string_lengthen (str, MAX_DOUBLE_LEN))
return FALSE;
- _dbus_string_get_data_len (str, &buf, orig_len, MAX_DOUBLE_LEN);
+ buf = _dbus_string_get_data_len (str, orig_len, MAX_DOUBLE_LEN);
snprintf (buf, MAX_LONG_LEN, "%g", value);
const char *p;
char *end;
- _dbus_string_get_const_data_len (str, &p, start,
- _dbus_string_get_length (str) - start);
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
end = NULL;
errno = 0;
if (value_return)
*value_return = v;
if (end_return)
- *end_return = (end - p);
+ *end_return = start + (end - p);
+
+ 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
+ * initialized if the function returns #FALSE.
+ *
+ * @param str the string
+ * @param start the byte index of the start of the integer
+ * @param value_return return location of the integer value or #NULL
+ * @param end_return return location of the end of the integer, or #NULL
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_string_parse_uint (const DBusString *str,
+ int start,
+ unsigned long *value_return,
+ int *end_return)
+{
+ unsigned long v;
+ const char *p;
+ char *end;
+
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
+
+ end = NULL;
+ errno = 0;
+ v = strtoul (p, &end, 0);
+ if (end == NULL || end == p || errno != 0)
+ return FALSE;
+
+ if (value_return)
+ *value_return = v;
+ if (end_return)
+ *end_return = start + (end - p);
return TRUE;
}
_dbus_warn ("_dbus_string_parse_double() needs to be made locale-independent\n");
- _dbus_string_get_const_data_len (str, &p, start,
- _dbus_string_get_length (str) - start);
+ p = _dbus_string_get_const_data_len (str, start,
+ _dbus_string_get_length (str) - start);
end = NULL;
errno = 0;
if (value_return)
*value_return = v;
if (end_return)
- *end_return = (end - p);
+ *end_return = start + (end - p);
return TRUE;
}
* @{
*/
+static dbus_bool_t
+store_user_info (struct passwd *p,
+ DBusCredentials *credentials,
+ DBusString *homedir,
+ DBusString *username_out)
+{
+ int old_homedir_len;
+
+ 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;
+ }
+ }
+
+ if (username_out &&
+ !_dbus_string_append (username_out, p->pw_name))
+ {
+ if (homedir)
+ _dbus_string_set_length (homedir, old_homedir_len);
+ _dbus_verbose ("No memory to get username\n");
+ 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 the credentials corresponding to the given username.
+ * 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 credentials credentials to fill in
- * @returns #TRUE if the username existed and we got some credentials
+ * @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
*/
-dbus_bool_t
-_dbus_credentials_from_username (const DBusString *username,
- DBusCredentials *credentials)
+static dbus_bool_t
+get_user_info (const DBusString *username,
+ int uid,
+ DBusCredentials *credentials,
+ DBusString *homedir,
+ DBusString *username_out)
{
const char *username_c_str;
+
+ /* exactly one of username/uid provided */
+ _dbus_assert (username != NULL || uid >= 0);
+ _dbus_assert (username == NULL || uid < 0);
+
+ if (credentials)
+ {
+ credentials->pid = -1;
+ credentials->uid = -1;
+ credentials->gid = -1;
+ }
- credentials->pid = -1;
- credentials->uid = -1;
- credentials->gid = -1;
+ if (username != NULL)
+ username_c_str = _dbus_string_get_const_data (username);
+ else
+ username_c_str = NULL;
- _dbus_string_get_const_data (username, &username_c_str);
+ /* For now assuming that the getpwnam() and getpwuid() flavors
+ * are always symmetrical, if not we have to add more configure
+ * checks
+ */
-#ifdef HAVE_GETPWNAM_R
+#if defined (HAVE_POSIX_GETPWNAME_R) || defined (HAVE_NONPOSIX_GETPWNAME_R)
{
struct passwd *p;
int result;
struct passwd p_str;
p = NULL;
- result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
- &p);
-
+#ifdef HAVE_POSIX_GETPWNAME_R
+ if (uid >= 0)
+ result = getpwuid_r (uid, &p_str, buf, sizeof (buf),
+ &p);
+ else
+ result = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf),
+ &p);
+#else
+ if (uid >= 0)
+ p = getpwuid_r (uid, &p_str, buf, sizeof (buf));
+ else
+ p = getpwnam_r (username_c_str, &p_str, buf, sizeof (buf));
+ result = 0;
+#endif /* !HAVE_POSIX_GETPWNAME_R */
if (result == 0 && p == &p_str)
{
- credentials->uid = p->pw_uid;
- credentials->gid = p->pw_gid;
-
- _dbus_verbose ("Username %s has uid %d gid %d\n",
- username_c_str, credentials->uid, credentials->gid);
- return TRUE;
+ return store_user_info (p, credentials, homedir,
+ username_out);
}
else
{
/* I guess we're screwed on thread safety here */
struct passwd *p;
- p = getpwnam (username_c_str);
+ if (uid >= 0)
+ p = getpwuid (uid);
+ else
+ p = getpwnam (username_c_str);
if (p != NULL)
{
- credentials->uid = p->pw_uid;
- credentials->gid = p->pw_gid;
-
- _dbus_verbose ("Username %s has uid %d gid %d\n",
- username_c_str, credentials->uid, credentials->gid);
- return TRUE;
+ return store_user_info (p, credentials, homedir,
+ username_out);
}
else
{
return FALSE;
}
}
-#endif
+#endif /* ! HAVE_GETPWNAM_R */
}
/**
- * Gets credentials from a UID string. (Parses a string to a UID
- * and converts to a DBusCredentials.)
+ * Gets the credentials corresponding to the given username.
*
- * @param uid_str the UID in string form
+ * @param username the username
* @param credentials credentials to fill in
- * @returns #TRUE if successfully filled in some credentials
+ * @returns #TRUE if the username existed and we got some credentials
*/
dbus_bool_t
-_dbus_credentials_from_uid_string (const DBusString *uid_str,
- DBusCredentials *credentials)
+_dbus_credentials_from_username (const DBusString *username,
+ 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;
+ return get_user_info (username, -1, credentials, NULL, NULL);
}
/**
- * Gets the credentials of the current process.
+ * Gets the credentials corresponding to the given user ID.
*
- * @param credentials credentials to fill in.
+ * @param user_id the user ID
+ * @param credentials credentials to fill in
+ * @returns #TRUE if the username existed and we got some credentials
*/
-void
-_dbus_credentials_from_current_process (DBusCredentials *credentials)
+dbus_bool_t
+_dbus_credentials_from_user_id (unsigned long user_id,
+ DBusCredentials *credentials)
{
- credentials->pid = getpid ();
- credentials->uid = getuid ();
- credentials->gid = getgid ();
+ 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)
+{
+ UserInfo *u = data;
+
+ _dbus_string_free (&u->name);
+ _dbus_string_free (&u->dir);
}
/**
- * Checks whether the provided_credentials are allowed to log in
- * as the expected_credentials.
+ * Gets information about the user running this process.
*
- * @param expected_credentials credentials we're trying to log in as
- * @param provided_credentials credentials we have
- * @returns #TRUE if we can log in
+ * @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
*/
dbus_bool_t
-_dbus_credentials_match (const DBusCredentials *expected_credentials,
- const DBusCredentials *provided_credentials)
+_dbus_user_info_from_current_process (const DBusString **username,
+ const DBusString **homedir,
+ const DBusCredentials **credentials)
+{
+ 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;
+}
+
+/**
+ * Gets the home directory for the given user.
+ *
+ * @param username the username
+ * @param homedir string to append home directory to
+ * @returns #TRUE if user existed and we appended their homedir
+ */
+dbus_bool_t
+_dbus_homedir_from_username (const DBusString *username,
+ DBusString *homedir)
+{
+ return get_user_info (username, -1, NULL, homedir, NULL);
+}
+
+/**
+ * Gets credentials from a UID string. (Parses a string to a UID
+ * and converts to a DBusCredentials.)
+ *
+ * @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.
+ */
+void
+_dbus_credentials_from_current_process (DBusCredentials *credentials)
+{
+ /* The POSIX spec certainly doesn't promise this, but
+ * we need these assertions to fail as soon as we're wrong about
+ * it so we can do the porting fixups
+ */
+ _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 = getpid ();
+ credentials->uid = getuid ();
+ credentials->gid = getgid ();
+}
+
+/**
+ * Checks whether the provided_credentials are allowed to log in
+ * as the expected_credentials.
+ *
+ * @param expected_credentials credentials we're trying to log in as
+ * @param provided_credentials credentials we have
+ * @returns #TRUE if we can log in
+ */
+dbus_bool_t
+_dbus_credentials_match (const DBusCredentials *expected_credentials,
+ const DBusCredentials *provided_credentials)
{
if (provided_credentials->uid < 0)
return FALSE;
}
/**
- * Appends the uid of the current process to the given string.
+ * Gets group ID from group name.
*
- * @param str the string to append to
- * @returns #TRUE on success
+ * @param group_name name of the group
+ * @param gid location to store group ID
+ * @returns #TRUE if group was known
*/
dbus_bool_t
-_dbus_string_append_our_uid (DBusString *str)
+_dbus_get_group_id (const DBusString *group_name,
+ unsigned long *gid)
{
- return _dbus_string_append_int (str, getuid ());
+ 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 */
}
+/**
+ * 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
+ */
+dbus_bool_t
+_dbus_get_groups (unsigned long uid,
+ unsigned long **group_ids,
+ int *n_group_ids)
+{
+ 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;
+}
-static DBusMutex *atomic_lock = NULL;
/**
- * Initializes the global mutex for the fallback implementation
- * of atomic integers.
+ * Appends the uid of the current process to the given string.
*
- * @returns the mutex
+ * @param str the string to append to
+ * @returns #TRUE on success
*/
-DBusMutex *
-_dbus_atomic_init_lock (void)
+dbus_bool_t
+_dbus_string_append_our_uid (DBusString *str)
{
- atomic_lock = dbus_mutex_new ();
- return atomic_lock;
+ return _dbus_string_append_int (str, getuid ());
}
+
+_DBUS_DEFINE_GLOBAL_LOCK (atomic);
+
/**
* Atomically increments an integer
*
{
dbus_atomic_t res;
- dbus_mutex_lock (atomic_lock);
+ _DBUS_LOCK (atomic);
*atomic += 1;
res = *atomic;
- dbus_mutex_unlock (atomic_lock);
+ _DBUS_UNLOCK (atomic);
return res;
}
{
dbus_atomic_t res;
- dbus_mutex_lock (atomic_lock);
+ _DBUS_LOCK (atomic);
*atomic -= 1;
res = *atomic;
- dbus_mutex_unlock (atomic_lock);
+ _DBUS_UNLOCK (atomic);
return res;
}
#else /* ! HAVE_POLL */
fd_set read_set, write_set, err_set;
- int max_fd;
+ int max_fd = 0;
int i;
struct timeval tv;
int ready;
/**
* Appends the contents of the given file to the string,
- * returning result code. At the moment, won't open a file
+ * returning error code. At the moment, won't open a file
* more than a megabyte in size.
*
* @param str the string to append to
* @param filename filename to load
- * @returns result
+ * @param error place to set an error
+ * @returns #FALSE if error was set
*/
-DBusResultCode
+dbus_bool_t
_dbus_file_get_contents (DBusString *str,
- const DBusString *filename)
+ const DBusString *filename,
+ DBusError *error)
{
int fd;
struct stat sb;
int total;
const char *filename_c;
- _dbus_string_get_const_data (filename, &filename_c);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
/* O_BINARY useful on Cygwin */
fd = open (filename_c, O_RDONLY | O_BINARY);
if (fd < 0)
- return _dbus_result_from_errno (errno);
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
+ return FALSE;
+ }
if (fstat (fd, &sb) < 0)
{
- DBusResultCode result;
-
- result = _dbus_result_from_errno (errno); /* prior to close() */
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
_dbus_verbose ("fstat() failed: %s",
_dbus_strerror (errno));
close (fd);
- return result;
+ return FALSE;
}
if (sb.st_size > _DBUS_ONE_MEGABYTE)
{
- _dbus_verbose ("File size %lu is too large.\n",
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "File size %lu is too large.\n",
(unsigned long) sb.st_size);
close (fd);
- return DBUS_RESULT_FAILED;
+ return FALSE;
}
total = 0;
sb.st_size - total);
if (bytes_read <= 0)
{
- DBusResultCode result;
-
- result = _dbus_result_from_errno (errno); /* prior to close() */
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
_dbus_verbose ("read() failed: %s",
_dbus_strerror (errno));
close (fd);
_dbus_string_set_length (str, orig_len);
- return result;
+ return FALSE;
}
else
total += bytes_read;
}
close (fd);
- return DBUS_RESULT_SUCCESS;
+ return TRUE;
}
else if (sb.st_size != 0)
{
_dbus_verbose ("Can only open regular files at the moment.\n");
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Not a regular file");
close (fd);
- return DBUS_RESULT_FAILED;
+ return FALSE;
}
else
{
close (fd);
- return DBUS_RESULT_SUCCESS;
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+append_unique_chars (DBusString *str)
+{
+ static const char letters[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ int i;
+ int len;
+
+#define N_UNIQUE_CHARS 8
+
+ if (!_dbus_generate_random_bytes (str, N_UNIQUE_CHARS))
+ return FALSE;
+
+ len = _dbus_string_get_length (str);
+ i = len - N_UNIQUE_CHARS;
+ while (i < len)
+ {
+ _dbus_string_set_byte (str, i,
+ letters[_dbus_string_get_byte (str, i) %
+ (sizeof (letters) - 1)]);
+
+ ++i;
}
+
+ _dbus_assert (_dbus_string_validate_ascii (str, len - N_UNIQUE_CHARS,
+ N_UNIQUE_CHARS));
+
+ return TRUE;
}
/**
- * Writes a string out to a file.
+ * Writes a string out to a file. If the file exists,
+ * it will be atomically overwritten by the new data.
*
* @param str the string to write out
* @param filename the file to save string to
- * @returns result code
+ * @param error error to be filled in on failure
+ * @returns #FALSE on failure
*/
-DBusResultCode
+dbus_bool_t
_dbus_string_save_to_file (const DBusString *str,
- const DBusString *filename)
+ const DBusString *filename,
+ DBusError *error)
{
int fd;
int bytes_to_write;
const char *filename_c;
+ DBusString tmp_filename;
+ const char *tmp_filename_c;
int total;
+ dbus_bool_t need_unlink;
+ dbus_bool_t retval;
- _dbus_string_get_const_data (filename, &filename_c);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
- fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+ fd = -1;
+ retval = FALSE;
+ need_unlink = FALSE;
+
+ if (!_dbus_string_init (&tmp_filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy (filename, 0, &tmp_filename, 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&tmp_filename, "."))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ if (!append_unique_chars (&tmp_filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return FALSE;
+ }
+
+ filename_c = _dbus_string_get_const_data (filename);
+ tmp_filename_c = _dbus_string_get_const_data (&tmp_filename);
+
+ fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
0600);
if (fd < 0)
- return _dbus_result_from_errno (errno);
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not create %s: %s", tmp_filename_c,
+ _dbus_strerror (errno));
+ goto out;
+ }
+ need_unlink = TRUE;
+
total = 0;
bytes_to_write = _dbus_string_get_length (str);
if (bytes_written <= 0)
{
- DBusResultCode result;
-
- result = _dbus_result_from_errno (errno); /* prior to close() */
-
- _dbus_verbose ("write() failed: %s",
- _dbus_strerror (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not write to %s: %s", tmp_filename_c,
+ _dbus_strerror (errno));
- close (fd);
- return result;
+ goto out;
}
total += bytes_written;
}
- close (fd);
- return DBUS_RESULT_SUCCESS;
+ if (close (fd) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not close file %s: %s",
+ tmp_filename_c, _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ fd = -1;
+
+ if (rename (tmp_filename_c, filename_c) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not rename %s to %s: %s",
+ tmp_filename_c, filename_c,
+ _dbus_strerror (errno));
+
+ goto out;
+ }
+
+ need_unlink = FALSE;
+
+ retval = TRUE;
+
+ out:
+ /* close first, then unlink, to prevent ".nfs34234235" garbage
+ * files
+ */
+
+ if (fd >= 0)
+ close (fd);
+
+ if (need_unlink && unlink (tmp_filename_c) < 0)
+ _dbus_verbose ("Failed to unlink temp file %s: %s\n",
+ tmp_filename_c, _dbus_strerror (errno));
+
+ _dbus_string_free (&tmp_filename);
+
+ if (!retval)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ return retval;
+}
+
+/** Creates the given file, failing if the file already exists.
+ *
+ * @param filename the filename
+ * @param error error location
+ * @returns #TRUE if we created the file and it didn't exist
+ */
+dbus_bool_t
+_dbus_create_file_exclusively (const DBusString *filename,
+ DBusError *error)
+{
+ int fd;
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT,
+ 0600);
+ if (fd < 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not create file %s: %s\n",
+ filename_c,
+ _dbus_errno_to_string (errno));
+ return FALSE;
+ }
+
+ if (close (fd) < 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not close file %s: %s\n",
+ filename_c,
+ _dbus_errno_to_string (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Deletes the given file.
+ *
+ * @param filename the filename
+ * @param error error location
+ *
+ * @returns #TRUE if unlink() succeeded
+ */
+dbus_bool_t
+_dbus_delete_file (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (unlink (filename_c) < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to delete file %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/**
+ * Creates a directory; succeeds if the directory
+ * is created or already existed.
+ *
+ * @param filename directory filename
+ * @param error initialized error object
+ * @returns #TRUE on success
+ */
+dbus_bool_t
+_dbus_create_directory (const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_c;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
+
+ if (mkdir (filename_c, 0700) < 0)
+ {
+ if (errno == EEXIST)
+ return TRUE;
+
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to create directory %s: %s\n",
+ filename_c, _dbus_strerror (errno));
+ return FALSE;
+ }
+ else
+ return TRUE;
}
/**
* Open a directory to iterate over.
*
* @param filename the directory name
- * @param result return location for error code if #NULL returned
+ * @param error exception return object or #NULL
* @returns new iterator, or #NULL on error
*/
DBusDirIter*
_dbus_directory_open (const DBusString *filename,
- DBusResultCode *result)
+ DBusError *error)
{
DIR *d;
DBusDirIter *iter;
const char *filename_c;
- _dbus_string_get_const_data (filename, &filename_c);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ filename_c = _dbus_string_get_const_data (filename);
d = opendir (filename_c);
if (d == NULL)
{
- dbus_set_result (result, _dbus_result_from_errno (errno));
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
return NULL;
}
-
iter = dbus_new0 (DBusDirIter, 1);
if (iter == NULL)
{
closedir (d);
- dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Could not allocate memory for directory iterator");
return NULL;
}
}
/**
- * Get next file in the directory. Will not return "." or ".."
- * on UNIX. If an error occurs, the contents of "filename"
- * are undefined. #DBUS_RESULT_SUCCESS is always returned
- * in result if no error occurs.
+ * 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.
*
* @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 result return location for error, or #DBUS_RESULT_SUCCESS
+ * @param error return location for error
* @returns #TRUE if filename was filled in with a new filename
*/
dbus_bool_t
_dbus_directory_get_next_file (DBusDirIter *iter,
DBusString *filename,
- DBusResultCode *result)
+ DBusError *error)
{
- /* we always have to put something in result, since return
- * value means whether there's a filename and doesn't
- * reliably indicate whether an error was set.
- */
struct dirent *ent;
-
- dbus_set_result (result, DBUS_RESULT_SUCCESS);
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
again:
errno = 0;
ent = readdir (iter->d);
if (ent == NULL)
{
- dbus_set_result (result,
- _dbus_result_from_errno (errno));
+ if (errno != 0)
+ dbus_set_error (error,
+ _dbus_error_from_errno (errno),
+ "%s", _dbus_strerror (errno));
return FALSE;
}
else if (ent->d_name[0] == '.' &&
_dbus_string_set_length (filename, 0);
if (!_dbus_string_append (filename, ent->d_name))
{
- dbus_set_result (result, DBUS_RESULT_NO_MEMORY);
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "No memory to read directory entry");
return FALSE;
}
else
int i;
/* 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);
while (i < n_bytes)
{
double r;
- int b;
+ unsigned int b;
r = rand ();
b = (r / (double) RAND_MAX) * 255.0;
if (_dbus_read (fd, str, n_bytes) != n_bytes)
goto failed;
+ _dbus_verbose ("Read %d bytes from /dev/urandom\n",
+ n_bytes);
+
close (fd);
return TRUE;
/**
* A wrapper around strerror()
*
+ * @todo get rid of this function, it's the same as
+ * _dbus_strerror().
+ *
* @param errnum the errno
* @returns an error message (never #NULL)
*/
return msg;
}
+/**
+ * A wrapper around strerror() because some platforms
+ * may be lame and not have strerror().
+ *
+ * @param error_number errno.
+ * @returns error description.
+ */
+const char*
+_dbus_strerror (int error_number)
+{
+ const char *msg;
+
+ msg = strerror (error_number);
+ if (msg == NULL)
+ msg = "unknown";
+
+ return msg;
+}
+
/* Avoids a danger in threaded situations (calling close()
* on a file descriptor twice, and another thread has
* re-opened it since the first close)
make_pipe (int p[2],
DBusError *error)
{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
if (pipe (p) < 0)
{
dbus_set_error (error,
DBusError *error)
{
size_t bytes = 0;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
while (TRUE)
{
int pid = -1, grandchild_pid;
int child_err_report_pipe[2] = { -1, -1 };
int status;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (!make_pipe (child_err_report_pipe, error))
return FALSE;
fcntl (fd, F_SETFD, val);
}
+/**
+ * Converts a UNIX errno into a #DBusError name.
+ *
+ * @todo should cover more errnos, specifically those
+ * from open().
+ *
+ * @param error_number the errno.
+ * @returns an error name
+ */
+const char*
+_dbus_error_from_errno (int error_number)
+{
+ switch (error_number)
+ {
+ case 0:
+ return DBUS_ERROR_FAILED;
+
+#ifdef EPROTONOSUPPORT
+ case EPROTONOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT:
+ return DBUS_ERROR_NOT_SUPPORTED;
+#endif
+#ifdef ENFILE
+ case ENFILE:
+ return DBUS_ERROR_LIMITS_EXCEEDED; /* kernel out of memory */
+#endif
+#ifdef EMFILE
+ case EMFILE:
+ return DBUS_ERROR_LIMITS_EXCEEDED;
+#endif
+#ifdef EACCES
+ case EACCES:
+ return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef EPERM
+ case EPERM:
+ return DBUS_ERROR_ACCESS_DENIED;
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS:
+ return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef ENOMEM
+ case ENOMEM:
+ return DBUS_ERROR_NO_MEMORY;
+#endif
+#ifdef EINVAL
+ case EINVAL:
+ return DBUS_ERROR_FAILED;
+#endif
+#ifdef EBADF
+ case EBADF:
+ return DBUS_ERROR_FAILED;
+#endif
+#ifdef EFAULT
+ case EFAULT:
+ return DBUS_ERROR_FAILED;
+#endif
+#ifdef ENOTSOCK
+ case ENOTSOCK:
+ return DBUS_ERROR_FAILED;
+#endif
+#ifdef EISCONN
+ case EISCONN:
+ return DBUS_ERROR_FAILED;
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED:
+ return DBUS_ERROR_NO_SERVER;
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+ return DBUS_ERROR_TIMEOUT;
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+ return DBUS_ERROR_NO_NETWORK;
+#endif
+#ifdef EADDRINUSE
+ case EADDRINUSE:
+ return DBUS_ERROR_ADDRESS_IN_USE;
+#endif
+#ifdef EEXIST
+ case EEXIST:
+ return DBUS_ERROR_FILE_NOT_FOUND;
+#endif
+#ifdef ENOENT
+ case ENOENT:
+ return DBUS_ERROR_FILE_NOT_FOUND;
+#endif
+ }
+
+ return DBUS_ERROR_FAILED;
+}
+
+/**
+ * Exit the process, returning the given value.
+ *
+ * @param code the exit code
+ */
+void
+_dbus_exit (int code)
+{
+ _exit (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 error error return
+ * @returns #FALSE on failure (if error is set)
+ */
+dbus_bool_t
+_dbus_full_duplex_pipe (int *fd1,
+ int *fd2,
+ 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 (!_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
+ * @param error error object
+ * @returns #FALSE if error set
+ */
+dbus_bool_t
+_dbus_close (int fd,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ again:
+ if (close (fd) < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Could not close fd %d", fd);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Sets a file descriptor to be nonblocking.
+ *
+ * @param fd the file descriptor.
+ * @param error address of error location.
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_set_fd_nonblocking (int fd,
+ DBusError *error)
+{
+ int val;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ val = fcntl (fd, F_GETFL, 0);
+ if (val < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to get flags from file descriptor %d: %s",
+ fd, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to get flags for fd %d: %s\n", fd,
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0)
+ {
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set nonblocking flag of file descriptor %d: %s",
+ fd, _dbus_strerror (errno));
+ _dbus_verbose ("Failed to set fd %d nonblocking: %s\n",
+ fd, _dbus_strerror (errno));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * On GNU libc systems, print a crude backtrace to the verbose log.
+ * On other systems, print "no backtrace support"
+ *
+ */
+void
+_dbus_print_backtrace (void)
+{
+#if defined (HAVE_BACKTRACE) && defined (DBUS_ENABLE_VERBOSE_MODE)
+ void *bt[500];
+ int bt_size;
+ int i;
+ char **syms;
+
+ bt_size = backtrace (bt, 500);
+
+ syms = backtrace_symbols (bt, bt_size);
+
+ i = 0;
+ while (i < bt_size)
+ {
+ _dbus_verbose (" %s\n", syms[i]);
+ ++i;
+ }
+
+ free (syms);
+#else
+ _dbus_verbose (" D-BUS not compiled with backtrace support\n");
+#endif
+}
+
/** @} end of sysdeps */