X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=dbus%2Fdbus-sysdeps.c;h=f551c4bfff33da3c0cb942fdb57e5b56106aeddd;hb=fa05de9230d62e7c427b5313796fc6ccd4d0ff60;hp=bcfa15fc90da82e355c4b67588c591db4c080a7a;hpb=92f7d50b3b420670732a55bf15f7b343b2ce9fe6;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index bcfa15f..f551c4b 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -1,7 +1,7 @@ /* -*- 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 * @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef HAVE_WRITEV #include @@ -50,6 +51,10 @@ #ifdef HAVE_POLL #include #endif +#ifdef HAVE_BACKTRACE +#include +#endif + #ifndef O_BINARY #define O_BINARY 0 @@ -91,7 +96,7 @@ _dbus_setenv (const char *varname, const char *value) DBusString str; char *putenv_value; - if (!_dbus_string_init (&str, _DBUS_INT_MAX)) + if (!_dbus_string_init (&str)) return FALSE; if (!_dbus_string_append (&str, varname) || @@ -126,7 +131,8 @@ _dbus_getenv (const char *varname) * 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 @@ -152,7 +158,7 @@ _dbus_read (int fd, return -1; } - _dbus_string_get_data_len (buffer, &data, start, count); + data = _dbus_string_get_data_len (buffer, start, count); again: @@ -202,7 +208,7 @@ _dbus_write (int fd, 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: @@ -261,10 +267,10 @@ _dbus_write_two (int fd, 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; @@ -313,25 +319,26 @@ _dbus_write_two (int fd, * 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 (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; } @@ -343,11 +350,10 @@ _dbus_connect_unix_socket (const char *path, 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; @@ -355,8 +361,10 @@ _dbus_connect_unix_socket (const char *path, 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; @@ -372,23 +380,25 @@ _dbus_connect_unix_socket (const char *path, * 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; + _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; } @@ -399,24 +409,25 @@ _dbus_listen_unix_socket (const char *path, 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; } @@ -431,28 +442,29 @@ _dbus_listen_unix_socket (const char *path, * * @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; } @@ -463,10 +475,10 @@ _dbus_connect_tcp_socket (const char *host, 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; } @@ -479,11 +491,10 @@ _dbus_connect_tcp_socket (const char *host, 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; @@ -491,7 +502,7 @@ _dbus_connect_tcp_socket (const char *host, return -1; } - if (!_dbus_set_fd_nonblocking (fd, result)) + if (!_dbus_set_fd_nonblocking (fd, error)) { close (fd); fd = -1; @@ -509,26 +520,28 @@ _dbus_connect_tcp_socket (const char *host, * * @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; } @@ -538,10 +551,10 @@ _dbus_listen_tcp_socket (const char *host, 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; } @@ -554,23 +567,23 @@ _dbus_listen_tcp_socket (const char *host, 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; @@ -579,59 +592,14 @@ _dbus_listen_tcp_socket (const char *host, 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: @@ -642,15 +610,15 @@ write_credentials_byte (int server_fd, 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 @@ -676,48 +644,128 @@ write_credentials_byte (int server_fd, * * @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; } /** @@ -734,14 +782,16 @@ _dbus_read_credentials_unix_socket (int client_fd, * 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; @@ -758,9 +808,13 @@ int _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) { @@ -800,7 +854,7 @@ _dbus_string_append_int (DBusString *str, 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); @@ -838,7 +892,7 @@ _dbus_string_append_uint (DBusString *str, 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); @@ -875,7 +929,7 @@ _dbus_string_append_double (DBusString *str, 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); @@ -913,8 +967,8 @@ _dbus_string_parse_int (const DBusString *str, 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; @@ -931,6 +985,45 @@ _dbus_string_parse_int (const DBusString *str, } /** + * 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; +} + +/** * Parses a floating point number 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 @@ -958,8 +1051,8 @@ _dbus_string_parse_double (const DBusString *str, _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; @@ -1045,17 +1138,20 @@ get_user_info (const DBusString *username, DBusString *username_out) { const char *username_c_str; - - credentials->pid = -1; - credentials->uid = -1; - credentials->gid = -1; - + /* 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; + } if (username != NULL) - _dbus_string_get_const_data (username, &username_c_str); + username_c_str = _dbus_string_get_const_data (username); else username_c_str = NULL; @@ -1135,17 +1231,36 @@ _dbus_credentials_from_username (const DBusString *username, return get_user_info (username, -1, credentials, NULL, NULL); } -static DBusMutex *user_info_lock = NULL; /** - * Initializes the global mutex for the process's user information. + * Gets the credentials corresponding to the given user ID. * - * @returns the mutex + * @param user_id the user ID + * @param credentials credentials to fill in + * @returns #TRUE if the username existed and we got some credentials */ -DBusMutex * -_dbus_user_info_init_lock (void) +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) { - user_info_lock = dbus_mutex_new (); - return user_info_lock; + UserInfo *u = data; + + _dbus_string_free (&u->name); + _dbus_string_free (&u->dir); } /** @@ -1161,53 +1276,58 @@ _dbus_user_info_from_current_process (const DBusString **username, const DBusString **homedir, const DBusCredentials **credentials) { - static DBusString name; - static DBusString dir; - static DBusCredentials creds; - static dbus_bool_t initialized = FALSE; + static UserInfo u; + static int initialized_generation = 0; - if (!dbus_mutex_lock (user_info_lock)) + if (!_DBUS_LOCK (user_info)) return FALSE; - if (!initialized) + if (initialized_generation != _dbus_current_generation) { - if (!_dbus_string_init (&name, _DBUS_INT_MAX)) + if (!_dbus_string_init (&u.name)) { - dbus_mutex_unlock (user_info_lock); + _DBUS_UNLOCK (user_info); return FALSE; } - if (!_dbus_string_init (&dir, _DBUS_INT_MAX)) + if (!_dbus_string_init (&u.dir)) { - _dbus_string_free (&name); - dbus_mutex_unlock (user_info_lock); + _dbus_string_free (&u.name); + _DBUS_UNLOCK (user_info); return FALSE; } - creds.uid = -1; - creds.gid = -1; - creds.pid = -1; + u.creds.uid = -1; + u.creds.gid = -1; + u.creds.pid = -1; if (!get_user_info (NULL, getuid (), - &creds, &dir, &name)) + &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 (&name); - _dbus_string_free (&dir); - dbus_mutex_unlock (user_info_lock); + _dbus_string_free (&u.name); + _dbus_string_free (&u.dir); + _DBUS_UNLOCK (user_info); return FALSE; } - - initialized = TRUE; } if (username) - *username = &name; + *username = &u.name; if (homedir) - *homedir = &dir; + *homedir = &u.dir; if (credentials) - *credentials = &creds; + *credentials = &u.creds; - dbus_mutex_unlock (user_info_lock); + _DBUS_UNLOCK (user_info); return TRUE; } @@ -1279,6 +1399,14 @@ _dbus_credentials_from_uid_string (const DBusString *uid_str, 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 (); @@ -1309,32 +1437,188 @@ _dbus_credentials_match (const DBusCredentials *expected_credentials, } /** - * 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 * @@ -1348,10 +1632,10 @@ _dbus_atomic_inc (dbus_atomic_t *atomic) { 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; } @@ -1368,10 +1652,10 @@ _dbus_atomic_dec (dbus_atomic_t *atomic) { 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; } @@ -1424,7 +1708,7 @@ _dbus_poll (DBusPollFD *fds, #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; @@ -1534,16 +1818,18 @@ _dbus_get_current_time (long *tv_sec, /** * 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; @@ -1551,33 +1837,39 @@ _dbus_file_get_contents (DBusString *str, 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; @@ -1592,60 +1884,136 @@ _dbus_file_get_contents (DBusString *str, 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); @@ -1658,22 +2026,59 @@ _dbus_string_save_to_file (const DBusString *str, if (bytes_written <= 0) { - DBusResultCode result; - - result = _dbus_result_from_errno (errno); /* prior to close() */ + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not write to %s: %s", tmp_filename_c, + _dbus_strerror (errno)); - _dbus_verbose ("write() failed: %s", - _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. @@ -1689,7 +2094,9 @@ _dbus_create_file_exclusively (const DBusString *filename, int fd; 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); fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, 0600); @@ -1730,10 +2137,49 @@ _dbus_delete_file (const DBusString *filename, { 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); if (unlink (filename_c) < 0) - return FALSE; + { + 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; } @@ -1785,31 +2231,34 @@ struct DBusDirIter * 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; } @@ -1819,39 +2268,36 @@ _dbus_directory_open (const DBusString *filename, } /** - * 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] == '.' && @@ -1863,7 +2309,8 @@ _dbus_directory_get_next_file (DBusDirIter *iter, _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 @@ -1907,6 +2354,8 @@ _dbus_generate_random_bytes (DBusString *str, 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); @@ -1915,7 +2364,7 @@ _dbus_generate_random_bytes (DBusString *str, while (i < n_bytes) { double r; - int b; + unsigned int b; r = rand (); b = (r / (double) RAND_MAX) * 255.0; @@ -1933,6 +2382,9 @@ _dbus_generate_random_bytes (DBusString *str, 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; @@ -1948,6 +2400,9 @@ _dbus_generate_random_bytes (DBusString *str, /** * 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) */ @@ -1963,6 +2418,25 @@ _dbus_errno_to_string (int errnum) 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) @@ -1987,6 +2461,8 @@ static dbus_bool_t make_pipe (int p[2], DBusError *error) { + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + if (pipe (p) < 0) { dbus_set_error (error, @@ -2030,6 +2506,8 @@ read_ints (int fd, DBusError *error) { size_t bytes = 0; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); while (TRUE) { @@ -2128,6 +2606,8 @@ _dbus_spawn_async (char **argv, 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; @@ -2287,4 +2767,300 @@ _dbus_fd_set_close_on_exec (int fd) 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 */