From 3801b6de7878074a8e77445bad2a2d093889a3af Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 02:46:35 +0200 Subject: build-system: get rid of config.h inclusion checks These header files include config.h explicitly anyway. These checks are hence pointless. Of course one could argue that including config.h from header files sucks, but D-Bus generally seems not to have a problem with that, so let's unify this. --- dbus/dbus-marshal-basic.h | 4 ---- dbus/dbus-marshal-byteswap.h | 4 ---- dbus/dbus-marshal-header.h | 4 ---- dbus/dbus-marshal-recursive.h | 4 ---- dbus/dbus-marshal-validate.h | 4 ---- 5 files changed, 20 deletions(-) diff --git a/dbus/dbus-marshal-basic.h b/dbus/dbus-marshal-basic.h index 28c751f4..bcc15ee9 100644 --- a/dbus/dbus-marshal-basic.h +++ b/dbus/dbus-marshal-basic.h @@ -31,10 +31,6 @@ #include #include -#ifndef PACKAGE -#error "config.h not included here" -#endif - #ifdef WORDS_BIGENDIAN #define DBUS_COMPILER_BYTE_ORDER DBUS_BIG_ENDIAN #else diff --git a/dbus/dbus-marshal-byteswap.h b/dbus/dbus-marshal-byteswap.h index 880c837e..e0f90e3a 100644 --- a/dbus/dbus-marshal-byteswap.h +++ b/dbus/dbus-marshal-byteswap.h @@ -28,10 +28,6 @@ #include #include -#ifndef PACKAGE -#error "config.h not included here" -#endif - void _dbus_marshal_byteswap (const DBusString *signature, int signature_start, int old_byte_order, diff --git a/dbus/dbus-marshal-header.h b/dbus/dbus-marshal-header.h index 52b6c737..e1cf52ee 100644 --- a/dbus/dbus-marshal-header.h +++ b/dbus/dbus-marshal-header.h @@ -28,10 +28,6 @@ #include #include -#ifndef PACKAGE -#error "config.h not included here" -#endif - typedef struct DBusHeader DBusHeader; typedef struct DBusHeaderField DBusHeaderField; diff --git a/dbus/dbus-marshal-recursive.h b/dbus/dbus-marshal-recursive.h index 14f38b2c..e533227c 100644 --- a/dbus/dbus-marshal-recursive.h +++ b/dbus/dbus-marshal-recursive.h @@ -28,10 +28,6 @@ #include #include -#ifndef PACKAGE -#error "config.h not included here" -#endif - typedef struct DBusTypeReader DBusTypeReader; typedef struct DBusTypeWriter DBusTypeWriter; typedef struct DBusTypeReaderClass DBusTypeReaderClass; diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h index d09acc60..29419991 100644 --- a/dbus/dbus-marshal-validate.h +++ b/dbus/dbus-marshal-validate.h @@ -26,10 +26,6 @@ #include -#ifndef PACKAGE -#error "config.h not included here" -#endif - /** * @addtogroup DBusMarshal * -- cgit From 6d2eacba89328029891ffe774ce37df44387cfbb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 02:48:58 +0200 Subject: memory: remove semicolons from macros Due to some unknown reasons the dbus_new() macros had a semicolon at the end which makes it impossible to use them in some situations. --- dbus/dbus-memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbus/dbus-memory.h b/dbus/dbus-memory.h index 6aab4e24..11b92252 100644 --- a/dbus/dbus-memory.h +++ b/dbus/dbus-memory.h @@ -43,8 +43,8 @@ void* dbus_realloc (void *memory, size_t bytes); void dbus_free (void *memory); -#define dbus_new(type, count) ((type*)dbus_malloc (sizeof (type) * (count))); -#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count))); +#define dbus_new(type, count) ((type*)dbus_malloc (sizeof (type) * (count))) +#define dbus_new0(type, count) ((type*)dbus_malloc0 (sizeof (type) * (count))) void dbus_free_string_array (char **str_array); -- cgit From ff0a8993d4716cb7729f4a0ffab9ab242f82b242 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 02:54:23 +0200 Subject: sysdeps-unix: Use MSG_NOSIGNAL when available On Linux send()/sendmsg() know the special flag MSG_NOSIGNAL which if set makes sure that no SIGPIPE signal is raised when we write to a socket that has been disconnected. By using this flag we don't have to play games with SIGPIPE which is pretty ugly stuff since it touches the global process context. --- dbus/dbus-sysdeps-unix.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 29d234a4..2e129826 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -179,7 +179,24 @@ _dbus_write_socket (int fd, int start, int len) { +#ifdef MSG_NOSIGNAL + const char *data; + int bytes_written; + + data = _dbus_string_get_const_data_len (buffer, start, len); + + again: + + bytes_written = send (fd, data, len, MSG_NOSIGNAL); + + if (bytes_written < 0 && errno == EINTR) + goto again; + + return bytes_written; + +#else return _dbus_write (fd, buffer, start, len); +#endif } /** @@ -255,8 +272,52 @@ _dbus_write_socket_two (int fd, int start2, int len2) { +#ifdef MSG_NOSIGNAL + struct iovec vectors[2]; + const char *data1; + const char *data2; + int bytes_written; + struct msghdr m; + + _dbus_assert (buffer1 != NULL); + _dbus_assert (start1 >= 0); + _dbus_assert (start2 >= 0); + _dbus_assert (len1 >= 0); + _dbus_assert (len2 >= 0); + + data1 = _dbus_string_get_const_data_len (buffer1, start1, len1); + + if (buffer2 != NULL) + data2 = _dbus_string_get_const_data_len (buffer2, start2, len2); + else + { + data2 = NULL; + start2 = 0; + len2 = 0; + } + + vectors[0].iov_base = (char*) data1; + vectors[0].iov_len = len1; + vectors[1].iov_base = (char*) data2; + vectors[1].iov_len = len2; + + memset(&m, 0, sizeof(m)); + m.msg_iov = vectors; + m.msg_iovlen = data2 ? 2 : 1; + + again: + + bytes_written = sendmsg (fd, &m, MSG_NOSIGNAL); + + if (bytes_written < 0 && errno == EINTR) + goto again; + + return bytes_written; + +#else return _dbus_write_two (fd, buffer1, start1, len1, buffer2, start2, len2); +#endif } -- cgit From 03d50fbd77481568bb2127d8b92e22d2cdc61ab8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:05:39 +0200 Subject: sysdeps-unix: if MSG_NOSIGNAL is available don't touch SIGPIPE by default If we can use MSG_NOSIGNAL we don't have to play games with SIGPIPE --- configure.in | 3 +++ dbus/dbus-connection.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/configure.in b/configure.in index 9e047eae..88d2164c 100644 --- a/configure.in +++ b/configure.in @@ -769,6 +769,9 @@ AC_CHECK_HEADERS(sys/uio.h, [AC_CHECK_FUNCS(writev)]) dnl needed on darwin for NAME_MAX AC_CHECK_HEADERS(sys/syslimits.h) +dnl Make it easy to check if we have MSG_NOSIGNAL without actually having to include sys/socket.h +AC_CHECK_DECLS([MSG_NOSIGNAL], [], [], [[ #include ]]) + dnl check for flavours of varargs macros (test from GLib) AC_MSG_CHECKING(for ISO C99 varargs macros in C) AC_TRY_COMPILE([],[ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 947c0afe..afa0ca6a 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -223,7 +223,11 @@ struct DBusPreallocatedSend DBusList *counter_link; /**< Preallocated link in the resource counter */ }; +#ifdef HAVE_DECL_MSG_NOSIGNAL +static dbus_bool_t _dbus_modify_sigpipe = FALSE; +#else static dbus_bool_t _dbus_modify_sigpipe = TRUE; +#endif /** * Implementation details of DBusConnection. All fields are private. -- cgit From 18f7259a439740d02f6cd727e32348b51191de14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:08:48 +0200 Subject: sysdeps-unix: introduce _dbus_dup() This is a simple wrapper around dup()-like functionality. Also handles CLOEXEC and makes sure we don't interfere with the standard I/O file descriptors 0, 1 and 2. --- dbus/dbus-sysdeps-unix.c | 42 ++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps-unix.h | 4 +++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 2e129826..5f94c6a5 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -2711,6 +2711,48 @@ _dbus_close (int fd, return TRUE; } +/** + * Duplicates a file descriptor. Makes sure the fd returned is >= 3 + * (i.e. avoids stdin/stdout/stderr). Sets O_CLOEXEC. + * + * @param fd the file descriptor to duplicate + * @returns duplicated file descriptor + * */ +int +_dbus_dup(int fd, + DBusError *error) +{ + int new_fd; + +#ifdef F_DUPFD_CLOEXEC + dbus_bool_t cloexec_done; + + new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + cloexec_done = new_fd >= 0; + + if (new_fd < 0 && errno == EINVAL) +#endif + { + new_fd = fcntl(fd, F_DUPFD, 3); + } + + if (new_fd < 0) { + + dbus_set_error (error, _dbus_error_from_errno (errno), + "Could not duplicate fd %d", fd); + return -1; + } + +#ifndef F_DUPFD_CLOEXEC + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec(new_fd); + } + + return new_fd; +} + /** * Sets a file descriptor to be nonblocking. * diff --git a/dbus/dbus-sysdeps-unix.h b/dbus/dbus-sysdeps-unix.h index 0005cd87..5b7723a2 100644 --- a/dbus/dbus-sysdeps-unix.h +++ b/dbus/dbus-sysdeps-unix.h @@ -44,7 +44,9 @@ DBUS_BEGIN_DECLS dbus_bool_t _dbus_close (int fd, DBusError *error); -int +int _dbus_dup (int fd, + DBusError *error); +int _dbus_read (int fd, DBusString *buffer, int count); -- cgit From 64c63db21ebdc81f4f830d0d9f1957259cff8f24 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:14:42 +0200 Subject: sysdeps-unix: introduce _dbus_socket_can_pass_unix_fd() This function can be used to check if a socket can be used to pass file descriptors. On platforms that don't support this at all this is hardcoded to return FALSE. --- dbus/dbus-sysdeps-unix.c | 32 ++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 5f94c6a5..74a95661 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -3462,4 +3462,36 @@ _dbus_get_is_errno_eagain_or_ewouldblock (void) return errno == EAGAIN || errno == EWOULDBLOCK; } +/** + * Checks whether file descriptors may be passed via the socket + * + * @param fd the socket + * @return TRUE when fd passing over this socket is supported + * + */ +dbus_bool_t +_dbus_socket_can_pass_unix_fd(int fd) { + +#ifdef SCM_RIGHTS + union { + struct sockaddr sa; + struct sockaddr_storage storage; + struct sockaddr_un un; + } sa_buf; + + socklen_t sa_len = sizeof(sa_buf); + + _DBUS_ZERO(sa_buf); + + if (getsockname(fd, &sa_buf.sa, &sa_len) < 0) + return FALSE; + + return sa_buf.sa.sa_family == AF_UNIX; + +#else + return FALSE; + +#endif +} + /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index b766f3f9..68fcdf61 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -192,6 +192,8 @@ dbus_bool_t _dbus_windows_user_is_process_owner (const char *windows_sid) dbus_bool_t _dbus_append_keyring_directory_for_credentials (DBusString *directory, DBusCredentials *credentials); +dbus_bool_t _dbus_socket_can_pass_unix_fd(int fd); + /** Opaque type representing an atomically-modifiable integer * that can be used from multiple threads. */ -- cgit From 4c4db7f9da1aa29c264a9f9d7d9fb1d774e28ee1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:20:27 +0200 Subject: sysdeps-unix: add basic IO primitives for unix fd passing This introduces three new functions: _dbus_read_socket_with_unix_fds _dbus_write_socket_with_unix_fds _dbus_read_socket_with_unix_fds_two These work exactly like their counterpart sans 'with_unix_fds' except that they also send/recieve file descriptors along with the actual payload data. --- configure.in | 10 ++ dbus/dbus-sysdeps-unix.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++- dbus/dbus-sysdeps.h | 22 +++++ 3 files changed, 268 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 88d2164c..0732adcd 100644 --- a/configure.in +++ b/configure.in @@ -1094,6 +1094,16 @@ else AC_MSG_RESULT(no) fi +# Check for SCM_RIGHTS +AC_MSG_CHECKING([for SCM_RIGHTS]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +static int x = SCM_RIGHTS; +]], [[]])], +[ AC_MSG_RESULT([supported]) + AC_DEFINE([HAVE_UNIX_FD_PASSING], [1], [Supports sending UNIX file descriptors]) ], +[ AC_MSG_RESULT([not supported]) ]) #### Set up final flags DBUS_CLIENT_CFLAGS= diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 74a95661..597398bb 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -199,6 +199,241 @@ _dbus_write_socket (int fd, #endif } +/** + * Like _dbus_read_socket() but also tries to read unix fds from the + * socket. When there are more fds to read than space in the array + * passed this function will fail with ENOSPC. + * + * @param fd the socket + * @param buffer string to append data to + * @param count max amount of data to read + * @param fds array to place read file descriptors in + * @param n_fds on input space in fds array, on output how many fds actually got read + * @returns number of bytes appended to string + */ +int +_dbus_read_socket_with_unix_fds (int fd, + DBusString *buffer, + int count, + int *fds, + int *n_fds) { +#ifndef HAVE_UNIX_FD_PASSING + int r; + + if ((r = _dbus_read_socket(fd, buffer, count)) < 0) + return r; + + *n_fds = 0; + return r; + +#else + int bytes_read; + int start; + struct msghdr m; + struct iovec iov; + + _dbus_assert (count >= 0); + _dbus_assert (*n_fds >= 0); + + start = _dbus_string_get_length (buffer); + + if (!_dbus_string_lengthen (buffer, count)) + { + errno = ENOMEM; + return -1; + } + + _DBUS_ZERO(iov); + iov.iov_base = _dbus_string_get_data_len (buffer, start, count); + iov.iov_len = count; + + _DBUS_ZERO(m); + m.msg_iov = &iov; + m.msg_iovlen = 1; + + /* Hmm, we have no clue how long the control data will actually be + that is queued for us. The least we can do is assume that the + caller knows. Hence let's make space for the number of fds that + we shall read at max plus the cmsg header. */ + m.msg_controllen = CMSG_SPACE(*n_fds * sizeof(int)); + + /* It's probably safe to assume that systems with SCM_RIGHTS also + know alloca() */ + m.msg_control = alloca(m.msg_controllen); + memset(m.msg_control, 0, m.msg_controllen); + + again: + + bytes_read = recvmsg(fd, &m, 0 +#ifdef MSG_CMSG_CLOEXEC + |MSG_CMSG_CLOEXEC +#endif + ); + + if (bytes_read < 0) + { + if (errno == EINTR) + goto again; + else + { + /* put length back (note that this doesn't actually realloc anything) */ + _dbus_string_set_length (buffer, start); + return -1; + } + } + else + { + struct cmsghdr *cm; + dbus_bool_t found = FALSE; + + if (m.msg_flags & MSG_CTRUNC) + { + /* Hmm, apparently the control data was truncated. The bad + thing is that we might have completely lost a couple of fds + without chance to recover them. Hence let's treat this as a + serious error. */ + + errno = ENOSPC; + _dbus_string_set_length (buffer, start); + return -1; + } + + for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) + if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) + { + unsigned i; + + _dbus_assert(cm->cmsg_len <= CMSG_LEN(*n_fds * sizeof(int))); + *n_fds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + memcpy(fds, CMSG_DATA(cm), *n_fds * sizeof(int)); + found = TRUE; + + /* Linux doesn't tell us whether MSG_CMSG_CLOEXEC actually + worked, hence we need to go through this list and set + CLOEXEC everywhere in any case */ + for (i = 0; i < *n_fds; i++) + _dbus_fd_set_close_on_exec(fds[i]); + + break; + } + + if (!found) + *n_fds = 0; + + /* put length back (doesn't actually realloc) */ + _dbus_string_set_length (buffer, start + bytes_read); + +#if 0 + if (bytes_read > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_read); +#endif + + return bytes_read; + } +#endif +} + +int +_dbus_write_socket_with_unix_fds(int fd, + const DBusString *buffer, + int start, + int len, + const int *fds, + int n_fds) { + +#ifndef HAVE_UNIX_FD_PASSING + + if (n_fds > 0) { + errno = ENOTSUP; + return -1; + } + + return _dbus_write_socket(fd, buffer, start, len); +#else + return _dbus_write_socket_with_unix_fds_two(fd, buffer, start, len, NULL, 0, 0, fds, n_fds); +#endif +} + +int +_dbus_write_socket_with_unix_fds_two(int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2, + const int *fds, + int n_fds) { + +#ifndef HAVE_UNIX_FD_PASSING + + if (n_fds > 0) { + errno = ENOTSUP; + return -1; + } + + return _dbus_write_socket_two(fd, + buffer1, start1, len1, + buffer2, start2, len2); +#else + + struct msghdr m; + struct cmsghdr *cm; + struct iovec iov[2]; + int bytes_written; + + _dbus_assert (len1 >= 0); + _dbus_assert (len2 >= 0); + _dbus_assert (n_fds >= 0); + + _DBUS_ZERO(iov); + iov[0].iov_base = (char*) _dbus_string_get_const_data_len (buffer1, start1, len1); + iov[0].iov_len = len1; + + if (buffer2) + { + iov[1].iov_base = (char*) _dbus_string_get_const_data_len (buffer2, start2, len2); + iov[1].iov_len = len2; + } + + _DBUS_ZERO(m); + m.msg_iov = iov; + m.msg_iovlen = buffer2 ? 2 : 1; + + if (n_fds > 0) + { + m.msg_controllen = CMSG_SPACE(n_fds * sizeof(int)); + m.msg_control = alloca(m.msg_controllen); + memset(m.msg_control, 0, m.msg_controllen); + + cm = CMSG_FIRSTHDR(&m); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + cm->cmsg_len = CMSG_LEN(n_fds * sizeof(int)); + memcpy(CMSG_DATA(cm), fds, n_fds * sizeof(int)); + } + + again: + + bytes_written = sendmsg (fd, &m, 0 +#ifdef MSG_NOSIGNAL + |MSG_NOSIGNAL +#endif + ); + + if (bytes_written < 0 && errno == EINTR) + goto again; + +#if 0 + if (bytes_written > 0) + _dbus_verbose_bytes_of_string (buffer, start, bytes_written); +#endif + + return bytes_written; +#endif +} + /** * write data to a pipe. * @@ -301,7 +536,7 @@ _dbus_write_socket_two (int fd, vectors[1].iov_base = (char*) data2; vectors[1].iov_len = len2; - memset(&m, 0, sizeof(m)); + _DBUS_ZERO(m); m.msg_iov = vectors; m.msg_iovlen = data2 ? 2 : 1; diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 68fcdf61..6f47e48b 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -153,6 +153,28 @@ int _dbus_write_socket_two (int fd, const DBusString *buffer2, int start2, int len2); + +int _dbus_read_socket_with_unix_fds (int fd, + DBusString *buffer, + int count, + int *fds, + int *n_fds); +int _dbus_write_socket_with_unix_fds (int fd, + const DBusString *buffer, + int start, + int len, + const int *fds, + int n_fds); +int _dbus_write_socket_with_unix_fds_two (int fd, + const DBusString *buffer1, + int start1, + int len1, + const DBusString *buffer2, + int start2, + int len2, + const int *fds, + int n_fds); + int _dbus_connect_tcp_socket (const char *host, const char *port, const char *family, -- cgit From faac66e3ea52644a5efa2f69ea2f6825a15b31a6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:32:35 +0200 Subject: unix-fd: introduce basic protocol definitions We introduce a new type code for the unix fds. The data stored in unix fd fields will be an integer index into the array of fds that are attached to a specific message. We also introduce a new header field that stores how many fds belong to the message. And finally we introduce a new error for messages where the payload and the meta data (i.e. unix fds read for it) don't match up. --- dbus/dbus-protocol.h | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index 814deae0..117195cd 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -110,6 +110,10 @@ extern "C" { #define DBUS_TYPE_SIGNATURE ((int) 'g') /** #DBUS_TYPE_SIGNATURE as a string literal instead of a int literal */ #define DBUS_TYPE_SIGNATURE_AS_STRING "g" +/** Type code marking a unix file descriptor */ +#define DBUS_TYPE_UNIX_FD ((int) 'h') +/** #DBUS_TYPE_UNIX_FD as a string literal instead of a int literal */ +#define DBUS_TYPE_UNIX_FD_AS_STRING "h" /* Compound types */ /** Type code marking a D-Bus array type */ @@ -207,6 +211,14 @@ extern "C" { /** Number of bits you need in an unsigned to store the max message size */ #define DBUS_MAXIMUM_MESSAGE_LENGTH_BITS 27 +/** The maximum total number of unix fds in a message. Similar + * rationale as DBUS_MAXIMUM_MESSAGE_LENGTH. However we divide by four + * given that one fd is an int and hence at least 32 bits. + */ +#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS (DBUS_MAXIMUM_MESSAGE_LENGTH/4) +/** Number of bits you need in an unsigned to store the max message unix fds */ +#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS_BITS (DBUS_MAXIMUM_MESSAGE_LENGTH_BITS-2) + /** Depth of recursion in the type tree. This is automatically limited * to DBUS_MAXIMUM_SIGNATURE_LENGTH since you could only have an array * of array of array of ... that fit in the max signature. But that's @@ -276,6 +288,12 @@ extern "C" { * Header field code for the type signature of a message. */ #define DBUS_HEADER_FIELD_SIGNATURE 8 +/** + * Header field code for the number of unix file descriptors associated + * with this message. + */ +#define DBUS_HEADER_FIELD_UNIX_FDS 9 + /** * Value of the highest-numbered header field code, can be used to determine @@ -283,7 +301,7 @@ extern "C" { * that unknown codes must be ignored, so check for that before * indexing the array. */ -#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_SIGNATURE +#define DBUS_HEADER_FIELD_LAST DBUS_HEADER_FIELD_UNIX_FDS /** Header format is defined as a signature: * byte byte order @@ -415,6 +433,9 @@ extern "C" { #define DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN "org.freedesktop.DBus.Error.AdtAuditDataUnknown" /** There's already an object with the requested object path. */ #define DBUS_ERROR_OBJECT_PATH_IN_USE "org.freedesktop.DBus.Error.ObjectPathInUse" +/** The message meta data does not match the payload. e.g. expected + number of file descriptors were not sent over the socket this message was received on. */ +#define DBUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" /* XML introspection format */ -- cgit From ba7daa606cf20ff3b5e992907f380a425feaef01 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:31:20 +0200 Subject: unix-fd: add basic marshalling code for unix fds This is actually pretty boring since we store our fds as indexes that are stored as uint32_t's. --- dbus/dbus-marshal-basic.c | 9 +++++++++ dbus/dbus-marshal-byteswap.c | 5 +++++ dbus/dbus-marshal-header.c | 7 ++++++- dbus/dbus-marshal-validate.c | 4 +++- dbus/dbus-message-factory.c | 1 + dbus/dbus-signature.c | 1 + 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/dbus/dbus-marshal-basic.c b/dbus/dbus-marshal-basic.c index 38fbe2d6..d396b6db 100644 --- a/dbus/dbus-marshal-basic.c +++ b/dbus/dbus-marshal-basic.c @@ -414,6 +414,7 @@ _dbus_marshal_set_basic (DBusString *str, case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: pos = _DBUS_ALIGN_VALUE (pos, 4); set_4_octets (str, pos, vp->u32, byte_order); if (old_end_pos) @@ -540,6 +541,7 @@ _dbus_marshal_read_basic (const DBusString *str, case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_UNIX_FD: { volatile dbus_uint32_t *vp = value; pos = _DBUS_ALIGN_VALUE (pos, 4); @@ -839,6 +841,7 @@ _dbus_marshal_write_basic (DBusString *str, break; case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: return marshal_4_octets (str, insert_at, vp->u32, byte_order, pos_after); break; @@ -1066,6 +1069,7 @@ _dbus_marshal_write_fixed_multi (DBusString *str, case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: return marshal_fixed_multi (str, insert_at, vp, n_elements, byte_order, 4, pos_after); break; case DBUS_TYPE_INT64: @@ -1114,6 +1118,7 @@ _dbus_marshal_skip_basic (const DBusString *str, case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: *pos = _DBUS_ALIGN_VALUE (*pos, 4); *pos += 4; break; @@ -1202,6 +1207,7 @@ _dbus_type_get_alignment (int typecode) case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: /* this stuff is 4 since it starts with a length */ case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: @@ -1256,6 +1262,7 @@ _dbus_type_is_valid (int typecode) case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: case DBUS_TYPE_VARIANT: + case DBUS_TYPE_UNIX_FD: return TRUE; default: @@ -1316,6 +1323,8 @@ _dbus_type_to_string (int typecode) return "begin_dict_entry"; case DBUS_DICT_ENTRY_END_CHAR: return "end_dict_entry"; + case DBUS_TYPE_UNIX_FD: + return "unix_fd"; default: return "unknown"; } diff --git a/dbus/dbus-marshal-byteswap.c b/dbus/dbus-marshal-byteswap.c index 6c9fff5d..5f0945b9 100644 --- a/dbus/dbus-marshal-byteswap.c +++ b/dbus/dbus-marshal-byteswap.c @@ -191,6 +191,11 @@ byteswap_body_helper (DBusTypeReader *reader, } break; + case DBUS_TYPE_UNIX_FD: + /* fds can only be passed on a local machine, so byte order must always match */ + _dbus_assert_not_reached("attempted to byteswap unix fds which makes no sense"); + break; + default: _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); break; diff --git a/dbus/dbus-marshal-header.c b/dbus/dbus-marshal-header.c index 8aba6a94..50b5f73c 100644 --- a/dbus/dbus-marshal-header.c +++ b/dbus/dbus-marshal-header.c @@ -81,7 +81,8 @@ _dbus_header_field_types[DBUS_HEADER_FIELD_LAST+1] = { { DBUS_HEADER_FIELD_REPLY_SERIAL, DBUS_TYPE_UINT32 }, { DBUS_HEADER_FIELD_DESTINATION, DBUS_TYPE_STRING }, { DBUS_HEADER_FIELD_SENDER, DBUS_TYPE_STRING }, - { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE } + { DBUS_HEADER_FIELD_SIGNATURE, DBUS_TYPE_SIGNATURE }, + { DBUS_HEADER_FIELD_UNIX_FDS, DBUS_TYPE_UINT32 } }; /** Macro to look up the correct type for a field */ @@ -888,6 +889,10 @@ load_and_validate_field (DBusHeader *header, } break; + case DBUS_HEADER_FIELD_UNIX_FDS: + /* Every value makes sense */ + break; + case DBUS_HEADER_FIELD_SIGNATURE: /* SIGNATURE validated generically due to its type */ string_validation_func = NULL; diff --git a/dbus/dbus-marshal-validate.c b/dbus/dbus-marshal-validate.c index 78d55941..fbfb1c31 100644 --- a/dbus/dbus-marshal-validate.c +++ b/dbus/dbus-marshal-validate.c @@ -100,6 +100,7 @@ _dbus_validate_signature_with_reason (const DBusString *type_str, case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: @@ -319,12 +320,13 @@ validate_body_helper (DBusTypeReader *reader, case DBUS_TYPE_BYTE: ++p; break; - + case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: + case DBUS_TYPE_UNIX_FD: case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: diff --git a/dbus/dbus-message-factory.c b/dbus/dbus-message-factory.c index 8550ee86..bd86dab9 100644 --- a/dbus/dbus-message-factory.c +++ b/dbus/dbus-message-factory.c @@ -949,6 +949,7 @@ static const int typecodes[] = { DBUS_STRUCT_END_CHAR, DBUS_DICT_ENTRY_BEGIN_CHAR, DBUS_DICT_ENTRY_END_CHAR, + DBUS_TYPE_UNIX_FD, 255 /* random invalid typecode */ }; diff --git a/dbus/dbus-signature.c b/dbus/dbus-signature.c index c7f8d0e3..b1e69315 100644 --- a/dbus/dbus-signature.c +++ b/dbus/dbus-signature.c @@ -355,6 +355,7 @@ dbus_type_is_fixed (int typecode) case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_UNIX_FD: return TRUE; default: return FALSE; -- cgit From a0cc21f8bb6752ffe0ee5f4f5b575dc50d6d46ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:41:05 +0200 Subject: unix-fd: add message encoding/decoding for unix fds When appending unix fds to the message a new entry in the fd array will be allocated and the index to it will be written to the message payload. When parsing unix fds from the message the index will be read from the payload and then looked up in the fd array. When we read fds we put them in a queue first. Since each message knows how many fds are attached to it we will then pop enough fds from this queue each time we decode a message from the stream. This should make sending and receiving more portable since we don't make any strong requirements on the exact semantics of the SCM_RIGHTS implementation: as long as fds are recieved in order, none or lost and the arrive at the same time as at least one byte from the actual message dat we should be able to handle them correctly. --- dbus/dbus-marshal-validate.h | 1 + dbus/dbus-message-internal.h | 15 ++ dbus/dbus-message-private.h | 29 ++- dbus/dbus-message-util.c | 108 +++++++++- dbus/dbus-message.c | 434 +++++++++++++++++++++++++++++++++++++++- dbus/dbus-transport-protected.h | 6 + dbus/dbus-transport-socket.c | 104 ++++++++-- dbus/dbus-transport.c | 24 ++- dbus/dbus-transport.h | 2 + 9 files changed, 680 insertions(+), 43 deletions(-) diff --git a/dbus/dbus-marshal-validate.h b/dbus/dbus-marshal-validate.h index 29419991..bf68ecd6 100644 --- a/dbus/dbus-marshal-validate.h +++ b/dbus/dbus-marshal-validate.h @@ -113,6 +113,7 @@ typedef enum DBUS_INVALID_DICT_ENTRY_HAS_TOO_MANY_FIELDS = 53, DBUS_INVALID_DICT_ENTRY_NOT_INSIDE_ARRAY = 54, DBUS_INVALID_DICT_KEY_MUST_BE_BASIC_TYPE = 55, + DBUS_INVALID_MISSING_UNIX_FDS = 56, DBUS_VALIDITY_LAST } DBusValidity; diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index 2e995b47..032268f1 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -34,6 +34,9 @@ typedef struct DBusMessageLoader DBusMessageLoader; void _dbus_message_get_network_data (DBusMessage *message, const DBusString **header, const DBusString **body); +void _dbus_message_get_unix_fds (DBusMessage *messgage, + const int **fds, + unsigned *n_fds); void _dbus_message_lock (DBusMessage *message); void _dbus_message_unlock (DBusMessage *message); @@ -54,6 +57,14 @@ void _dbus_message_loader_get_buffer (DBusMessageLoader void _dbus_message_loader_return_buffer (DBusMessageLoader *loader, DBusString *buffer, int bytes_read); + +dbus_bool_t _dbus_message_loader_get_unix_fds (DBusMessageLoader *loader, + int **fds, + unsigned *max_n_fds); +void _dbus_message_loader_return_unix_fds (DBusMessageLoader *loader, + int *fds, + unsigned n_fds); + dbus_bool_t _dbus_message_loader_queue_messages (DBusMessageLoader *loader); DBusMessage* _dbus_message_loader_peek_message (DBusMessageLoader *loader); DBusMessage* _dbus_message_loader_pop_message (DBusMessageLoader *loader); @@ -67,6 +78,10 @@ void _dbus_message_loader_set_max_message_size (DBusMessageLoader long size); long _dbus_message_loader_get_max_message_size (DBusMessageLoader *loader); +void _dbus_message_loader_set_max_message_unix_fds(DBusMessageLoader *loader, + long n); +long _dbus_message_loader_get_max_message_unix_fds(DBusMessageLoader *loader); + DBUS_END_DECLS #endif /* DBUS_MESSAGE_INTERNAL_H */ diff --git a/dbus/dbus-message-private.h b/dbus/dbus-message-private.h index c1e368f7..1e8b107a 100644 --- a/dbus/dbus-message-private.h +++ b/dbus/dbus-message-private.h @@ -23,6 +23,8 @@ #ifndef DBUS_MESSAGE_PRIVATE_H #define DBUS_MESSAGE_PRIVATE_H +#include + #include #include #include @@ -66,12 +68,21 @@ struct DBusMessageLoader DBusList *messages; /**< Complete messages. */ long max_message_size; /**< Maximum size of a message */ + long max_message_unix_fds; /**< Maximum unix fds in a message */ - unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ + DBusValidity corruption_reason; /**< why we were corrupted */ unsigned int corrupted : 1; /**< We got broken data, and are no longer working */ - DBusValidity corruption_reason; /**< why we were corrupted */ + unsigned int buffer_outstanding : 1; /**< Someone is using the buffer to read */ + +#ifdef HAVE_UNIX_FD_PASSING + unsigned int unix_fds_outstanding : 1; /**< Someone is using the unix fd array to read */ + + int *unix_fds; /**< File descriptors that have been read from the transport but not yet been handed to any message. Array will be allocated at first use. */ + unsigned n_unix_fds_allocated; /**< Number of file descriptors this array has space for */ + unsigned n_unix_fds; /**< Number of valid file descriptors in array */ +#endif }; @@ -100,7 +111,7 @@ struct DBusMessage #ifndef DBUS_DISABLE_CHECKS unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */ #endif - + DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ long size_counter_delta; /**< Size we incremented the size counters by. */ @@ -111,6 +122,15 @@ struct DBusMessage #ifndef DBUS_DISABLE_CHECKS int generation; /**< _dbus_current_generation when message was created */ #endif + +#ifdef HAVE_UNIX_FD_PASSING + int *unix_fds; + /**< Unix file descriptors associated with this message. These are + closed when the message is destroyed, hence make sure to dup() + them when adding or removing them here. */ + unsigned n_unix_fds; /**< Number of valid fds in the array */ + unsigned n_unix_fds_allocated; /**< Allocated size of the array */ +#endif }; dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter, @@ -118,6 +138,9 @@ dbus_bool_t _dbus_message_iter_get_args_valist (DBusMessageIter *iter, int first_arg_type, va_list var_args); + +void _dbus_check_fdleaks(void); + /** @} */ DBUS_END_DECLS diff --git a/dbus/dbus-message-util.c b/dbus/dbus-message-util.c index 46cbe4e3..1b139436 100644 --- a/dbus/dbus-message-util.c +++ b/dbus/dbus-message-util.c @@ -27,6 +27,17 @@ #include "dbus-message-private.h" #include "dbus-marshal-recursive.h" #include "dbus-string.h" +#ifdef HAVE_UNIX_FD_PASSING +#include "dbus-sysdeps-unix.h" +#endif + +#ifdef __linux__ +/* Necessary for the Linux-specific fd leak checking code only */ +#include +#include +#include +#include +#endif /** * @addtogroup DBusMessage @@ -126,6 +137,50 @@ check_memleaks (void) } } +void +_dbus_check_fdleaks(void) +{ + +#ifdef __linux__ + + DIR *d; + + /* This works on Linux only */ + + if ((d = opendir("/proc/self/fd"))) + { + struct dirent *de; + + while ((de = readdir(d))) + { + long l; + char *e = NULL; + int fd; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol(de->d_name, &e, 10); + _dbus_assert(errno == 0 && e && !*e); + + fd = (int) l; + + if (fd < 3) + continue; + + if (fd == dirfd(d)) + continue; + + _dbus_warn("file descriptor %i leaked in %s.\n", fd, __FILE__); + _dbus_assert_not_reached("fdleaks"); + } + + closedir(d); + } +#endif +} + static dbus_bool_t check_have_valid_message (DBusMessageLoader *loader) { @@ -895,7 +950,7 @@ verify_test_message (DBusMessage *message) dbus_bool_t _dbus_message_test (const char *test_data_dir) { - DBusMessage *message; + DBusMessage *message, *message_without_unix_fds; DBusMessageLoader *loader; int i; const char *data; @@ -939,6 +994,9 @@ _dbus_message_test (const char *test_data_dir) unsigned char v_BYTE; unsigned char v2_BYTE; dbus_bool_t v_BOOLEAN; +#ifdef HAVE_UNIX_FD_PASSING + int v_UNIX_FD; +#endif message = dbus_message_new_method_call ("org.freedesktop.DBus.TestService", "/org/freedesktop/TestPath", @@ -1057,6 +1115,9 @@ _dbus_message_test (const char *test_data_dir) v_BOOLEAN = TRUE; v_BYTE = 42; v2_BYTE = 24; +#ifdef HAVE_UNIX_FD_PASSING + v_UNIX_FD = 1; +#endif dbus_message_append_args (message, DBUS_TYPE_INT16, &v_INT16, @@ -1090,6 +1151,7 @@ _dbus_message_test (const char *test_data_dir) _DBUS_N_ELEMENTS (our_boolean_array), DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v_ARRAY_STRING, _DBUS_N_ELEMENTS (our_string_array), + DBUS_TYPE_INVALID); i = 0; @@ -1124,7 +1186,16 @@ _dbus_message_test (const char *test_data_dir) sig[i++] = DBUS_TYPE_BOOLEAN; sig[i++] = DBUS_TYPE_ARRAY; sig[i++] = DBUS_TYPE_STRING; - sig[i++] = DBUS_TYPE_INVALID; + + message_without_unix_fds = dbus_message_copy(message); + _dbus_assert(message_without_unix_fds); +#ifdef HAVE_UNIX_FD_PASSING + dbus_message_append_args (message, + DBUS_TYPE_UNIX_FD, &v_UNIX_FD, + DBUS_TYPE_INVALID); + sig[i++] = DBUS_TYPE_UNIX_FD; +#endif + sig[i++] = DBUS_TYPE_INVALID; _dbus_assert (i < (int) _DBUS_N_ELEMENTS (sig)); @@ -1201,6 +1272,20 @@ _dbus_message_test (const char *test_data_dir) _dbus_message_loader_return_buffer (loader, buffer, 1); } +#ifdef HAVE_UNIX_FD_PASSING + { + int *unix_fds; + unsigned n_unix_fds; + /* Write unix fd */ + _dbus_message_loader_get_unix_fds(loader, &unix_fds, &n_unix_fds); + _dbus_assert(n_unix_fds > 0); + _dbus_assert(message->n_unix_fds == 1); + unix_fds[0] = _dbus_dup(message->unix_fds[0], NULL); + _dbus_assert(unix_fds[0] >= 0); + _dbus_message_loader_return_unix_fds(loader, unix_fds, 1); + } +#endif + dbus_message_unref (message); /* Now pop back the message */ @@ -1217,7 +1302,14 @@ _dbus_message_test (const char *test_data_dir) if (dbus_message_get_reply_serial (message) != 5678) _dbus_assert_not_reached ("reply serial fields differ"); - verify_test_message (message); + dbus_message_unref (message); + + /* ovveride the serial, since it was reset by dbus_message_copy() */ + dbus_message_set_serial(message_without_unix_fds, 8901); + + dbus_message_lock (message_without_unix_fds); + + verify_test_message (message_without_unix_fds); { /* Marshal and demarshal the message. */ @@ -1228,7 +1320,7 @@ _dbus_message_test (const char *test_data_dir) int len = 0; char garbage_header[DBUS_MINIMUM_HEADER_SIZE] = "xxx"; - if (!dbus_message_marshal (message, &marshalled, &len)) + if (!dbus_message_marshal (message_without_unix_fds, &marshalled, &len)) _dbus_assert_not_reached ("failed to marshal message"); _dbus_assert (len != 0); @@ -1267,10 +1359,11 @@ _dbus_message_test (const char *test_data_dir) _dbus_assert (dbus_message_demarshal_bytes_needed (garbage_header, DBUS_MINIMUM_HEADER_SIZE) == -1); } - dbus_message_unref (message); + dbus_message_unref (message_without_unix_fds); _dbus_message_loader_unref (loader); check_memleaks (); + _dbus_check_fdleaks(); /* Load all the sample messages from the message factory */ { @@ -1304,9 +1397,10 @@ _dbus_message_test (const char *test_data_dir) print_validities_seen (FALSE); print_validities_seen (TRUE); } - + check_memleaks (); - + _dbus_check_fdleaks(); + /* Now load every message in test_data_dir if we have one */ if (test_data_dir == NULL) return TRUE; diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index edae4258..54bceb0d 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -33,6 +33,10 @@ #include "dbus-memory.h" #include "dbus-list.h" #include "dbus-threads-internal.h" +#ifdef HAVE_UNIX_FD_PASSING +#include "dbus-sysdeps-unix.h" +#endif + #include static void dbus_message_finalize (DBusMessage *message); @@ -159,6 +163,30 @@ _dbus_message_get_network_data (DBusMessage *message, *body = &message->body; } +/** + * Gets the unix fds to be sent over the network for this message. + * This function is guaranteed to always return the same data once a + * message is locked (with dbus_message_lock()). + * + * @param message the message. + * @param fds return location of unix fd array + * @param n_fds return number of entries in array + */ +void _dbus_message_get_unix_fds(DBusMessage *message, + const int **fds, + unsigned *n_fds) +{ + _dbus_assert (message->locked); + +#ifdef HAVE_UNIX_FD_PASSING + *fds = message->unix_fds; + *n_fds = message->n_unix_fds; +#else + *fds = NULL; + *n_fds = 0; +#endif +} + /** * Sets the serial number of a message. * This can only be done once on a message. @@ -494,6 +522,33 @@ dbus_message_get_cached (void) return message; } +#ifdef HAVE_UNIX_FD_PASSING +static void +close_unix_fds(int *fds, unsigned *n_fds) +{ + DBusError e; + int i; + + if (*n_fds <= 0) + return; + + dbus_error_init(&e); + + for (i = 0; i < *n_fds; i++) + { + if (!_dbus_close(fds[i], &e)) + { + _dbus_warn("Failed to close file descriptor: %s\n", e.message); + dbus_error_free(&e); + } + } + + *n_fds = 0; + + /* We don't free the array here, in case we can recycle it later */ +} +#endif + static void free_size_counter (void *element, void *data) @@ -528,6 +583,10 @@ dbus_message_cache_or_finalize (DBusMessage *message) free_size_counter, message); _dbus_list_clear (&message->size_counters); +#ifdef HAVE_UNIX_FD_PASSING + close_unix_fds(message->unix_fds, &message->n_unix_fds); +#endif + was_cached = FALSE; _DBUS_LOCK (message_cache); @@ -634,6 +693,12 @@ _dbus_message_iter_check (DBusMessageRealIter *iter) * dbus_message_get_args() is the place to go for complete * documentation. * + * Unix file descriptors that are read with this function will have + * the FD_CLOEXEC flag set. If you need them without this flag set, + * make sure to unset it with fcntl(). + * + * @todo This may leak memory and file descriptors if parsing fails. See #21259 + * * @see dbus_message_get_args * @param iter the message iter * @param error error to be filled in @@ -673,7 +738,38 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter, goto out; } - if (dbus_type_is_basic (spec_type)) + if (spec_type == DBUS_TYPE_UNIX_FD) + { +#ifdef HAVE_UNIX_FD_PASSING + DBusBasicValue idx; + int *pfd, nfd; + + pfd = va_arg (var_args, int*); + _dbus_assert(pfd); + + _dbus_type_reader_read_basic(&real->u.reader, &idx); + + if (idx.u32 >= real->message->n_unix_fds) + { + dbus_set_error (error, DBUS_ERROR_INCONSISTENT_MESSAGE, + "Message refers to file descriptor at index %i," + "but has only %i descriptors attached.\n", + idx.u32, + real->message->n_unix_fds); + goto out; + } + + if ((nfd = _dbus_dup(real->message->unix_fds[idx.u32], error)) < 0) + goto out; + + *pfd = nfd; +#else + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "Platform does not support file desciptor passing.\n"); + goto out; +#endif + } + else if (dbus_type_is_basic (spec_type)) { DBusBasicValue *ptr; @@ -707,7 +803,8 @@ _dbus_message_iter_get_args_valist (DBusMessageIter *iter, goto out; } - if (dbus_type_is_fixed (spec_element_type)) + if (dbus_type_is_fixed (spec_element_type) && + element_type != DBUS_TYPE_UNIX_FD) { ptr = va_arg (var_args, const DBusBasicValue**); n_elements_p = va_arg (var_args, int*); @@ -943,6 +1040,11 @@ dbus_message_finalize (DBusMessage *message) _dbus_header_free (&message->header); _dbus_string_free (&message->body); +#ifdef HAVE_UNIX_FD_PASSING + close_unix_fds(message->unix_fds, &message->n_unix_fds); + dbus_free(message->unix_fds); +#endif + _dbus_assert (message->refcount.value == 0); dbus_free (message); @@ -969,6 +1071,11 @@ dbus_message_new_empty_header (void) #ifndef DBUS_DISABLE_CHECKS message->generation = _dbus_current_generation; #endif + +#ifdef HAVE_UNIX_FD_PASSING + message->unix_fds = NULL; + message->n_unix_fds_allocated = 0; +#endif } message->refcount.value = 1; @@ -981,6 +1088,10 @@ dbus_message_new_empty_header (void) message->size_counter_delta = 0; message->changed_stamp = 0; +#ifdef HAVE_UNIX_FD_PASSING + message->n_unix_fds = 0; +#endif + if (!from_cache) _dbus_data_slot_list_init (&message->slot_list); @@ -1308,8 +1419,10 @@ dbus_message_new_error_printf (DBusMessage *reply_to, * outgoing message queue and thus not modifiable) the new message * will not be locked. * + * @todo This function can't be used in programs that try to recover from OOM errors. + * * @param message the message - * @returns the new message.or #NULL if not enough memory + * @returns the new message.or #NULL if not enough memory or Unix file descriptors (in case the message to copy includes Unix file descriptors) can be allocated. */ DBusMessage * dbus_message_copy (const DBusMessage *message) @@ -1347,11 +1460,36 @@ dbus_message_copy (const DBusMessage *message) &retval->body, 0)) goto failed_copy; +#ifdef HAVE_UNIX_FD_PASSING + retval->unix_fds = dbus_new(int, message->n_unix_fds); + if (retval->unix_fds == NULL && message->n_unix_fds > 0) + goto failed_copy; + + retval->n_unix_fds_allocated = message->n_unix_fds; + + for (retval->n_unix_fds = 0; + retval->n_unix_fds < message->n_unix_fds; + retval->n_unix_fds++) + { + retval->unix_fds[retval->n_unix_fds] = _dbus_dup(message->unix_fds[retval->n_unix_fds], NULL); + + if (retval->unix_fds[retval->n_unix_fds] < 0) + goto failed_copy; + } + +#endif + return retval; failed_copy: _dbus_header_free (&retval->header); _dbus_string_free (&retval->body); + +#ifdef HAVE_UNIX_FD_PASSING + close_unix_fds(retval->unix_fds, &retval->n_unix_fds); + dbus_free(retval->unix_fds); +#endif + dbus_free (retval); return NULL; @@ -1555,8 +1693,9 @@ dbus_message_append_args_valist (DBusMessage *message, buf, &array)) goto failed; - - if (dbus_type_is_fixed (element_type)) + + if (dbus_type_is_fixed (element_type) && + element_type != DBUS_TYPE_UNIX_FD) { const DBusBasicValue **value; int n_elements; @@ -1973,8 +2112,30 @@ dbus_message_iter_get_basic (DBusMessageIter *iter, _dbus_return_if_fail (_dbus_message_iter_check (real)); _dbus_return_if_fail (value != NULL); - _dbus_type_reader_read_basic (&real->u.reader, - value); + if (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_UNIX_FD) + { +#ifdef HAVE_UNIX_FD_PASSING + DBusBasicValue idx; + + _dbus_type_reader_read_basic(&real->u.reader, &idx); + + if (idx.u32 >= real->message->n_unix_fds) { + /* Hmm, we cannot really signal an error here, so let's make + sure to return an invalid fd. */ + *((int*) value) = -1; + return; + } + + *((int*) value) = _dbus_dup(real->message->unix_fds[idx.u32], NULL); +#else + *((int*) value) = -1; +#endif + } + else + { + _dbus_type_reader_read_basic (&real->u.reader, + value); + } } /** @@ -2047,7 +2208,7 @@ dbus_message_iter_get_fixed_array (DBusMessageIter *iter, _dbus_return_if_fail (_dbus_message_iter_check (real)); _dbus_return_if_fail (value != NULL); _dbus_return_if_fail ((subtype == DBUS_TYPE_INVALID) || - dbus_type_is_fixed (subtype)); + (dbus_type_is_fixed (subtype) && subtype != DBUS_TYPE_UNIX_FD)); _dbus_type_reader_read_fixed_multi (&real->u.reader, value, n_elements); @@ -2217,6 +2378,40 @@ _dbus_message_iter_append_check (DBusMessageRealIter *iter) } #endif /* DBUS_DISABLE_CHECKS */ +#ifdef HAVE_UNIX_FD_PASSING +static int * +expand_fd_array(DBusMessage *m, + unsigned n) +{ + _dbus_assert(m); + + /* This makes space for adding n new fds to the array and returns a + pointer to the place were the first fd should be put. */ + + if (m->n_unix_fds + n > m->n_unix_fds_allocated) + { + unsigned k; + int *p; + + /* Make twice as much space as necessary */ + k = (m->n_unix_fds + n) * 2; + + /* Allocate at least four */ + if (k < 4) + k = 4; + + p = dbus_realloc(m->unix_fds, k * sizeof(int)); + if (p == NULL) + return NULL; + + m->unix_fds = p; + m->n_unix_fds_allocated = k; + } + + return m->unix_fds + m->n_unix_fds; +} +#endif + /** * Appends a basic-typed value to the message. The basic types are the * non-container types such as integer and string. @@ -2248,7 +2443,50 @@ dbus_message_iter_append_basic (DBusMessageIter *iter, if (!_dbus_message_iter_open_signature (real)) return FALSE; - ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); + if (type == DBUS_TYPE_UNIX_FD) + { +#ifdef HAVE_UNIX_FD_PASSING + int *fds; + dbus_uint32_t u; + + /* First step, include the fd in the fd list of this message */ + if (!(fds = expand_fd_array(real->message, 1))) + return FALSE; + + *fds = _dbus_dup(*(int*) value, NULL); + if (*fds < 0) + return FALSE; + + u = real->message->n_unix_fds; + + /* Second step, write the index to the fd */ + if (!(ret = _dbus_type_writer_write_basic (&real->u.writer, DBUS_TYPE_UNIX_FD, &u))) { + _dbus_close(*fds, NULL); + return FALSE; + } + + real->message->n_unix_fds += 1; + u += 1; + + /* Final step, update the header accordingly */ + ret = _dbus_header_set_field_basic (&real->message->header, + DBUS_HEADER_FIELD_UNIX_FDS, + DBUS_TYPE_UINT32, + &u); + + /* If any of these operations fail the message is + hosed. However, no memory or fds should be leaked since what + has been added to message has been added to the message, and + can hence be accounted for when the message is being + freed. */ +#else + ret = FALSE; +#endif + } + else + { + ret = _dbus_type_writer_write_basic (&real->u.writer, type, value); + } if (!_dbus_message_iter_close_signature (real)) ret = FALSE; @@ -2302,7 +2540,7 @@ dbus_message_iter_append_fixed_array (DBusMessageIter *iter, _dbus_return_val_if_fail (_dbus_message_iter_append_check (real), FALSE); _dbus_return_val_if_fail (real->iter_type == DBUS_MESSAGE_ITER_TYPE_WRITER, FALSE); - _dbus_return_val_if_fail (dbus_type_is_fixed (element_type), FALSE); + _dbus_return_val_if_fail (dbus_type_is_fixed (element_type) && element_type != DBUS_TYPE_UNIX_FD, FALSE); _dbus_return_val_if_fail (real->u.writer.container_type == DBUS_TYPE_ARRAY, FALSE); _dbus_return_val_if_fail (value != NULL, FALSE); _dbus_return_val_if_fail (n_elements >= 0, FALSE); @@ -3321,6 +3559,12 @@ _dbus_message_loader_new (void) /* this can be configured by the app, but defaults to the protocol max */ loader->max_message_size = DBUS_MAXIMUM_MESSAGE_LENGTH; + /* We set a very relatively conservative default here since due to how + SCM_RIGHTS works we need to preallocate an fd array of the maximum + number of unix fds we want to receive in advance. A + try-and-reallocate loop is not possible. */ + loader->max_message_unix_fds = 1024; + if (!_dbus_string_init (&loader->data)) { dbus_free (loader); @@ -3331,6 +3575,12 @@ _dbus_message_loader_new (void) _dbus_string_set_length (&loader->data, INITIAL_LOADER_DATA_LEN); _dbus_string_set_length (&loader->data, 0); +#ifdef HAVE_UNIX_FD_PASSING + loader->unix_fds = NULL; + loader->n_unix_fds = loader->n_unix_fds_allocated = 0; + loader->unix_fds_outstanding = FALSE; +#endif + return loader; } @@ -3360,6 +3610,10 @@ _dbus_message_loader_unref (DBusMessageLoader *loader) loader->refcount -= 1; if (loader->refcount == 0) { +#ifdef HAVE_UNIX_FD_PASSING + close_unix_fds(loader->unix_fds, &loader->n_unix_fds); + dbus_free(loader->unix_fds); +#endif _dbus_list_foreach (&loader->messages, (DBusForeachFunction) dbus_message_unref, NULL); @@ -3419,6 +3673,81 @@ _dbus_message_loader_return_buffer (DBusMessageLoader *loader, loader->buffer_outstanding = FALSE; } +/** + * Gets the buffer to use for reading unix fds from the network. + * + * This works similar to _dbus_message_loader_get_buffer() + * + * @param loader the message loader. + * @param fds the array to read fds into + * @param max_n_fds how many fds to read at most + * @return TRUE on success, FALSE on OOM + */ +dbus_bool_t +_dbus_message_loader_get_unix_fds(DBusMessageLoader *loader, + int **fds, + unsigned *max_n_fds) +{ +#ifdef HAVE_UNIX_FD_PASSING + _dbus_assert (!loader->unix_fds_outstanding); + + /* Allocate space where we can put the fds we read. We allocate + space for max_message_unix_fds since this is an + upper limit how many fds can be received within a single + message. Since SCM_RIGHTS doesn't allow a reallocate+retry logic + we are allocating the maximum possible array size right from the + beginning. This sucks a bit, however unless SCM_RIGHTS is fixed + there is no better way. */ + + if (loader->n_unix_fds_allocated < loader->max_message_unix_fds) + { + int *a = dbus_realloc(loader->unix_fds, + loader->max_message_unix_fds * sizeof(loader->unix_fds[0])); + + if (!a) + return FALSE; + + loader->unix_fds = a; + loader->n_unix_fds_allocated = loader->max_message_unix_fds; + } + + *fds = loader->unix_fds + loader->n_unix_fds; + *max_n_fds = loader->n_unix_fds_allocated - loader->n_unix_fds; + + loader->unix_fds_outstanding = TRUE; + return TRUE; +#else + _dbus_assert_not_reached("Platform doesn't support unix fd passing"); +#endif +} + +/** + * Returns a buffer obtained from _dbus_message_loader_get_unix_fds(). + * + * This works similar to _dbus_message_loader_return_buffer() + * + * @param loader the message loader. + * @param fds the array fds were read into + * @param max_n_fds how many fds were read + */ + +void +_dbus_message_loader_return_unix_fds(DBusMessageLoader *loader, + int *fds, + unsigned n_fds) +{ +#ifdef HAVE_UNIX_FD_PASSING + _dbus_assert(loader->unix_fds_outstanding); + _dbus_assert(loader->unix_fds + loader->n_unix_fds == fds); + _dbus_assert(loader->n_unix_fds + n_fds <= loader->n_unix_fds_allocated); + + loader->n_unix_fds += n_fds; + loader->unix_fds_outstanding = FALSE; +#else + _dbus_assert_not_reached("Platform doesn't support unix fd passing"); +#endif +} + /* * FIXME when we move the header out of the buffer, that memmoves all * buffered messages. Kind of crappy. @@ -3458,6 +3787,7 @@ load_message (DBusMessageLoader *loader, const DBusString *type_str; int type_pos; DBusValidationMode mode; + dbus_uint32_t n_unix_fds = 0; mode = DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED; @@ -3527,6 +3857,59 @@ load_message (DBusMessageLoader *loader, } } + /* 3. COPY OVER UNIX FDS */ + _dbus_header_get_field_basic(&message->header, + DBUS_HEADER_FIELD_UNIX_FDS, + DBUS_TYPE_UINT32, + &n_unix_fds); + +#ifdef HAVE_UNIX_FD_PASSING + + if (n_unix_fds > loader->n_unix_fds) + { + _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n", + n_unix_fds, loader->n_unix_fds); + + loader->corrupted = TRUE; + loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS; + goto failed; + } + + /* If this was a recycled message there might still be + some memory allocated for the fds */ + dbus_free(message->unix_fds); + + if (n_unix_fds > 0) + { + message->unix_fds = _dbus_memdup(loader->unix_fds, n_unix_fds * sizeof(message->unix_fds[0])); + if (message->unix_fds == NULL) + { + _dbus_verbose ("Failed to allocate file descriptor array\n"); + oom = TRUE; + goto failed; + } + + message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds; + loader->n_unix_fds -= n_unix_fds; + memmove(loader->unix_fds + n_unix_fds, loader->unix_fds, loader->n_unix_fds); + } + else + message->unix_fds = NULL; + +#else + + if (n_unix_fds > 0) + { + _dbus_verbose ("Hmm, message claims to come with file descriptors " + "but that's not supported on our platform, disconnecting.\n"); + + loader->corrupted = TRUE; + loader->corruption_reason = DBUS_INVALID_MISSING_UNIX_FDS; + goto failed; + } + +#endif + /* 3. COPY OVER BODY AND QUEUE MESSAGE */ if (!_dbus_list_append (&loader->messages, message)) @@ -3756,6 +4139,37 @@ _dbus_message_loader_get_max_message_size (DBusMessageLoader *loader) return loader->max_message_size; } +/** + * Sets the maximum unix fds per message we allow. + * + * @param loader the loader + * @param size the max number of unix fds in a message + */ +void +_dbus_message_loader_set_max_message_unix_fds (DBusMessageLoader *loader, + long n) +{ + if (n > DBUS_MAXIMUM_MESSAGE_UNIX_FDS) + { + _dbus_verbose ("clamping requested max message unix_fds %ld to %d\n", + n, DBUS_MAXIMUM_MESSAGE_UNIX_FDS); + n = DBUS_MAXIMUM_MESSAGE_UNIX_FDS; + } + loader->max_message_unix_fds = n; +} + +/** + * Gets the maximum allowed number of unix fds per message + * + * @param loader the loader + * @returns max unix fds + */ +long +_dbus_message_loader_get_max_message_unix_fds (DBusMessageLoader *loader) +{ + return loader->max_message_unix_fds; +} + static DBusDataSlotAllocator slot_allocator; _DBUS_DEFINE_GLOBAL_LOCK (message_slots); diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 4d56a72f..023549d0 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -23,6 +23,8 @@ #ifndef DBUS_TRANSPORT_PROTECTED_H #define DBUS_TRANSPORT_PROTECTED_H +#include + #include #include #include @@ -71,6 +73,10 @@ struct DBusTransportVTable /**< Get socket file descriptor */ }; +/** How many unix file descriptors may be queued up before they are + handed off to messages */ +#define DBUS_MAX_QUEUED_FDS 1024 + /** * Object representing a transport such as a socket. * A transport can shuttle messages from point A to point B, diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 6d7c89cd..c9d4d93c 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -28,7 +28,6 @@ #include "dbus-watch.h" #include "dbus-credentials.h" - /** * @defgroup DBusTransportSocket DBusTransport implementations for sockets * @ingroup DBusInternals @@ -551,6 +550,9 @@ do_writing (DBusTransport *transport) if (_dbus_auth_needs_encoding (transport->auth)) { + /* Does fd passing even make sense with encoded data? */ + _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); + if (_dbus_string_get_length (&socket_transport->encoded_outgoing) == 0) { if (!_dbus_auth_encode_data (transport->auth, @@ -588,27 +590,53 @@ do_writing (DBusTransport *transport) #if 0 _dbus_verbose ("message is %d bytes\n", - total_bytes_to_write); + total_bytes_to_write); #endif - - if (socket_transport->message_bytes_written < header_len) + +#ifdef HAVE_UNIX_FD_PASSING + if (socket_transport->message_bytes_written <= 0 && transport->can_pass_unix_fd) { + /* Send the fds along with the first byte of the message */ + const int *unix_fds; + unsigned n; + + _dbus_message_get_unix_fds(message, &unix_fds, &n); + bytes_written = - _dbus_write_socket_two (socket_transport->fd, - header, - socket_transport->message_bytes_written, - header_len - socket_transport->message_bytes_written, - body, - 0, body_len); + _dbus_write_socket_with_unix_fds_two (socket_transport->fd, + header, + socket_transport->message_bytes_written, + header_len - socket_transport->message_bytes_written, + body, + 0, body_len, + unix_fds, + n); + + if (bytes_written > 0 && n > 0) + _dbus_verbose("Wrote %i unix fds\n", n); } else +#endif { - bytes_written = - _dbus_write_socket (socket_transport->fd, - body, - (socket_transport->message_bytes_written - header_len), - body_len - - (socket_transport->message_bytes_written - header_len)); + if (socket_transport->message_bytes_written < header_len) + { + bytes_written = + _dbus_write_socket_two (socket_transport->fd, + header, + socket_transport->message_bytes_written, + header_len - socket_transport->message_bytes_written, + body, + 0, body_len); + } + else + { + bytes_written = + _dbus_write_socket (socket_transport->fd, + body, + (socket_transport->message_bytes_written - header_len), + body_len - + (socket_transport->message_bytes_written - header_len)); + } } } @@ -700,6 +728,9 @@ do_reading (DBusTransport *transport) if (_dbus_auth_needs_decoding (transport->auth)) { + /* Does fd passing even make sense with encoded data? */ + _dbus_assert(!DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)); + if (_dbus_string_get_length (&socket_transport->encoded_incoming) > 0) bytes_read = _dbus_string_get_length (&socket_transport->encoded_incoming); else @@ -744,10 +775,37 @@ do_reading (DBusTransport *transport) { _dbus_message_loader_get_buffer (transport->loader, &buffer); - - bytes_read = _dbus_read_socket (socket_transport->fd, - buffer, socket_transport->max_bytes_read_per_iteration); - + +#ifdef HAVE_UNIX_FD_PASSING + if (transport->can_pass_unix_fd) + { + int *fds, n_fds; + + if (!_dbus_message_loader_get_unix_fds(transport->loader, &fds, &n_fds)) + { + _dbus_verbose ("Out of memory reading file descriptors\n"); + _dbus_message_loader_return_buffer (transport->loader, buffer, 0); + oom = TRUE; + goto out; + } + + bytes_read = _dbus_read_socket_with_unix_fds(socket_transport->fd, + buffer, + socket_transport->max_bytes_read_per_iteration, + fds, &n_fds); + + if (bytes_read >= 0 && n_fds > 0) + _dbus_verbose("Read %i unix fds\n", n_fds); + + _dbus_message_loader_return_unix_fds(transport->loader, fds, bytes_read < 0 ? 0 : n_fds); + } + else +#endif + { + bytes_read = _dbus_read_socket (socket_transport->fd, + buffer, socket_transport->max_bytes_read_per_iteration); + } + _dbus_message_loader_return_buffer (transport->loader, buffer, bytes_read < 0 ? 0 : bytes_read); @@ -1184,7 +1242,11 @@ _dbus_transport_new_for_socket (int fd, &socket_vtable, server_guid, address)) goto failed_4; - + +#ifdef HAVE_UNIX_FD_PASSING + socket_transport->base.can_pass_unix_fd = _dbus_socket_can_pass_unix_fd(fd); +#endif + socket_transport->fd = fd; socket_transport->message_bytes_written = 0; diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 35b7027d..97ee0e9b 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -29,6 +29,8 @@ #include "dbus-auth.h" #include "dbus-address.h" #include "dbus-credentials.h" +#include "dbus-message-private.h" +#include "dbus-marshal-header.h" #ifdef DBUS_BUILD_TESTS #include "dbus-server-debug-pipe.h" #endif @@ -180,7 +182,13 @@ _dbus_transport_init_base (DBusTransport *transport, /* credentials read from socket if any */ transport->credentials = creds; - + +#ifdef HAVE_UNIX_FD_PASSING + transport->can_pass_unix_fd = FALSE; + transport->unix_fds = NULL; + transport->n_unix_fds = 0; +#endif + _dbus_counter_set_notify (transport->live_messages_size, transport->max_live_messages_size, live_messages_size_notify, @@ -188,7 +196,7 @@ _dbus_transport_init_base (DBusTransport *transport, if (transport->address) _dbus_verbose ("Initialized transport on address %s\n", transport->address); - + return TRUE; } @@ -802,6 +810,18 @@ _dbus_transport_get_is_anonymous (DBusTransport *transport) return FALSE; } +/** + * Returns TRUE if the transport supports sending unix fds. + * + * @param transport the transport + * @returns #TRUE if TRUE it is possible to send unix fds across the transport. + */ +dbus_bool_t +_dbus_transport_can_pass_unix_fd(DBusTransport *transport) +{ + return DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport); +} + /** * Gets the address of a transport. It will be * #NULL for a server-side transport. diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 691763ca..4554faf3 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -40,6 +40,8 @@ void _dbus_transport_disconnect (DBusTransport dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport); dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport); dbus_bool_t _dbus_transport_get_is_anonymous (DBusTransport *transport); +dbus_bool_t _dbus_transport_can_pass_unix_fd (DBusTransport *transport); + const char* _dbus_transport_get_address (DBusTransport *transport); const char* _dbus_transport_get_server_id (DBusTransport *transport); dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport, -- cgit From 08e49d9b53b675ee2292ac35173dfb4ab97e8d13 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 03:56:18 +0200 Subject: unix-fd: introduce dbus_connection_can_send_type() This is just a wrapper around _dbus_transport_can_pass_unix_fd() however it is more generic. The reason for keeping this generic is to ease later addition of more types without having to add a new API for that. --- dbus/dbus-connection.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- dbus/dbus-connection.h | 2 ++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index afa0ca6a..fc872bc2 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -33,6 +33,7 @@ #include "dbus-list.h" #include "dbus-hash.h" #include "dbus-message-internal.h" +#include "dbus-message-private.h" #include "dbus-threads.h" #include "dbus-protocol.h" #include "dbus-dataslot.h" @@ -41,6 +42,7 @@ #include "dbus-object-tree.h" #include "dbus-threads-internal.h" #include "dbus-bus.h" +#include "dbus-marshal-basic.h" #ifdef DBUS_DISABLE_CHECKS #define TOOK_LOCK_CHECK(connection) @@ -2932,14 +2934,58 @@ dbus_connection_get_server_id (DBusConnection *connection) char *id; _dbus_return_val_if_fail (connection != NULL, NULL); - + CONNECTION_LOCK (connection); id = _dbus_strdup (_dbus_transport_get_server_id (connection->transport)); CONNECTION_UNLOCK (connection); - + return id; } +/** + * Tests whether a certain type can be send via the connection. This + * will always return TRUE for all types, with the exception of + * DBUS_TYPE_UNIX_FD. The function will return TRUE for + * DBUS_TYPE_UNIX_FD only on systems that know Unix file descriptors + * and can send them via the chosen transport and when the remote side + * supports this. + * + * This function can be used to do runtime checking for types that + * might be unknown to the specific D-Bus client implementation + * version, i.e. it will return FALSE for all types this + * implementation does not know. + * + * @param connection the connection + * @param type the type to check + * @returns TRUE if the type may be send via the connection + */ +dbus_bool_t +dbus_connection_can_send_type(DBusConnection *connection, + int type) +{ + _dbus_return_val_if_fail (connection != NULL, FALSE); + + if (!_dbus_type_is_valid(type)) + return FALSE; + + if (type != DBUS_TYPE_UNIX_FD) + return TRUE; + +#ifdef HAVE_UNIX_FD_PASSING + { + dbus_bool_t b; + + CONNECTION_LOCK(connection); + b = _dbus_transport_can_pass_unix_fd(connection->transport); + CONNECTION_UNLOCK(connection); + + return b; + } +#endif + + return FALSE; +} + /** * Set whether _exit() should be called when the connection receives a * disconnect signal. The call to _exit() comes after any handlers for diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index b8fd35fb..c9b9aa34 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -180,6 +180,8 @@ dbus_bool_t dbus_connection_get_is_connected (DBusConnection dbus_bool_t dbus_connection_get_is_authenticated (DBusConnection *connection); dbus_bool_t dbus_connection_get_is_anonymous (DBusConnection *connection); char* dbus_connection_get_server_id (DBusConnection *connection); +dbus_bool_t dbus_connection_can_send_type (DBusConnection *connection, + int type); void dbus_connection_set_exit_on_disconnect (DBusConnection *connection, dbus_bool_t exit_on_disconnect); void dbus_connection_flush (DBusConnection *connection); -- cgit From 2eb14dbcac1870dda1b36ce1e6fedfe7500572cb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:01:01 +0200 Subject: unix-fd: when sending a message with unix fds verify that the connection can do it Not all of the send function flavours allow returning proper error codes. For the cases where this is not easily possible the client should call dbus_connection_can_send_type() first. --- dbus/dbus-connection.c | 76 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index fc872bc2..8433b643 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -3086,8 +3086,23 @@ dbus_connection_send_preallocated (DBusConnection *connection, _dbus_return_if_fail (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL || (dbus_message_get_interface (message) != NULL && dbus_message_get_member (message) != NULL)); - + CONNECTION_LOCK (connection); + +#ifdef HAVE_UNIX_FD_PASSING + + if (!_dbus_transport_can_pass_unix_fd(connection->transport) && + message->n_unix_fds > 0) + { + /* Refuse to send fds on a connection that cannot handle + them. Unfortunately we cannot return a proper error here, so + the best we can is just return. */ + CONNECTION_UNLOCK (connection); + return; + } + +#endif + _dbus_connection_send_preallocated_and_unlock (connection, preallocated, message, client_serial); @@ -3151,6 +3166,20 @@ dbus_connection_send (DBusConnection *connection, CONNECTION_LOCK (connection); +#ifdef HAVE_UNIX_FD_PASSING + + if (!_dbus_transport_can_pass_unix_fd(connection->transport) && + message->n_unix_fds > 0) + { + /* Refuse to send fds on a connection that cannot handle + them. Unfortunately we cannot return a proper error here, so + the best we can is just return. */ + CONNECTION_UNLOCK (connection); + return FALSE; + } + +#endif + return _dbus_connection_send_and_unlock (connection, message, serial); @@ -3207,12 +3236,16 @@ reply_handler_timeout (void *data) * timeout to mean "very long timeout." libdbus clamps an INT_MAX * timeout down to a few hours timeout though. * - * @warning if the connection is disconnected, the #DBusPendingCall - * will be set to #NULL, so be careful with this. - * + * @warning if the connection is disconnected or you try to send Unix + * file descriptors on a connection that does not support them, the + * #DBusPendingCall will be set to #NULL, so be careful with this. + * * @param connection the connection * @param message the message to send - * @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected + * @param pending_return return location for a #DBusPendingCall + * object, or #NULL if connection is disconnected or when you try to + * send Unix file descriptors on a connection that does not support + * them. * @param timeout_milliseconds timeout in milliseconds or -1 for default * @returns #FALSE if no memory, #TRUE otherwise. * @@ -3236,6 +3269,21 @@ dbus_connection_send_with_reply (DBusConnection *connection, CONNECTION_LOCK (connection); +#ifdef HAVE_UNIX_FD_PASSING + + if (!_dbus_transport_can_pass_unix_fd(connection->transport) && + message->n_unix_fds > 0) + { + /* Refuse to send fds on a connection that cannot handle + them. Unfortunately we cannot return a proper error here, so + the best we can do is return TRUE but leave *pending_return + as NULL. */ + CONNECTION_UNLOCK (connection); + return TRUE; + } + +#endif + if (!_dbus_connection_get_is_connected_unlocked (connection)) { CONNECTION_UNLOCK (connection); @@ -3344,12 +3392,26 @@ dbus_connection_send_with_reply_and_block (DBusConnection *connection, { DBusMessage *reply; DBusPendingCall *pending; - + _dbus_return_val_if_fail (connection != NULL, NULL); _dbus_return_val_if_fail (message != NULL, NULL); _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL); _dbus_return_val_if_error_is_set (error, NULL); - + +#ifdef HAVE_UNIX_FD_PASSING + + CONNECTION_LOCK (connection); + if (!_dbus_transport_can_pass_unix_fd(connection->transport) && + message->n_unix_fds > 0) + { + CONNECTION_UNLOCK (connection); + dbus_set_error(error, DBUS_ERROR_FAILED, "Cannot send file descriptors on this connection."); + return NULL; + } + CONNECTION_UNLOCK (connection); + +#endif + if (!dbus_connection_send_with_reply (connection, message, &pending, timeout_milliseconds)) { -- cgit From 004f01fa451b0341e7ea69ce7f08a1c4690f759a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:07:01 +0200 Subject: unix-fd: add test for passing unix fds This adds a full test for passing multiple fds across a D-Bus connection. --- bus/dispatch.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bus/test-main.c | 10 ++++ bus/test.h | 4 +- 3 files changed, 164 insertions(+), 1 deletion(-) diff --git a/bus/dispatch.c b/bus/dispatch.c index 209bde3e..ca9a8433 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -35,6 +35,11 @@ #include #include +#ifdef HAVE_UNIX_FD_PASSING +#include +#include +#endif + static dbus_bool_t send_one_message (DBusConnection *connection, BusContext *context, @@ -4716,4 +4721,150 @@ bus_dispatch_sha1_test (const DBusString *test_data_dir) return TRUE; } +#ifdef HAVE_UNIX_FD_PASSING + +dbus_bool_t +bus_unix_fds_passing_test(const DBusString *test_data_dir) +{ + BusContext *context; + DBusConnection *foo, *bar; + DBusError error; + DBusMessage *m; + dbus_bool_t b; + int one[2], two[2], x, y, z; + char r; + + dbus_error_init (&error); + + context = bus_context_new_test (test_data_dir, "valid-config-files/debug-allow-all.conf"); + if (context == NULL) + _dbus_assert_not_reached ("could not alloc context"); + + foo = dbus_connection_open_private ("debug-pipe:name=test-server", &error); + if (foo == NULL) + _dbus_assert_not_reached ("could not alloc connection"); + + if (!bus_setup_debug_client (foo)) + _dbus_assert_not_reached ("could not set up connection"); + + spin_connection_until_authenticated (context, foo); + + if (!check_hello_message (context, foo)) + _dbus_assert_not_reached ("hello message failed"); + + if (!check_add_match_all (context, foo)) + _dbus_assert_not_reached ("AddMatch message failed"); + + bar = dbus_connection_open_private ("debug-pipe:name=test-server", &error); + if (bar == NULL) + _dbus_assert_not_reached ("could not alloc connection"); + + if (!bus_setup_debug_client (bar)) + _dbus_assert_not_reached ("could not set up connection"); + + spin_connection_until_authenticated (context, bar); + + if (!check_hello_message (context, bar)) + _dbus_assert_not_reached ("hello message failed"); + + if (!check_add_match_all (context, bar)) + _dbus_assert_not_reached ("AddMatch message failed"); + + if (!(m = dbus_message_new_signal("/", "a.b.c", "d"))) + _dbus_assert_not_reached ("could not alloc message"); + + if (!(_dbus_full_duplex_pipe(one, one+1, TRUE, &error))) + _dbus_assert_not_reached("Failed to allocate pipe #1"); + + if (!(_dbus_full_duplex_pipe(two, two+1, TRUE, &error))) + _dbus_assert_not_reached("Failed to allocate pipe #2"); + + if (!dbus_message_append_args(m, + DBUS_TYPE_UNIX_FD, one, + DBUS_TYPE_UNIX_FD, two, + DBUS_TYPE_UNIX_FD, two, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached("Failed to attach fds."); + + if (!_dbus_close(one[0], &error)) + _dbus_assert_not_reached("Failed to close pipe #1 "); + if (!_dbus_close(two[0], &error)) + _dbus_assert_not_reached("Failed to close pipe #2 "); + + + if (!dbus_connection_send (foo, m, NULL)) + _dbus_assert_not_reached("Failed to send fds"); + + dbus_message_unref(m); + + bus_test_run_clients_loop (SEND_PENDING (foo)); + + bus_test_run_everything (context); + + block_connection_until_message_from_bus (context, foo, "unix fd reception on foo"); + + if (!(m = pop_message_waiting_for_memory (foo))) + _dbus_assert_not_reached("Failed to receive msg"); + + if (!dbus_message_is_signal(m, "a.b.c", "d")) + _dbus_assert_not_reached("bogus message received"); + + dbus_message_unref(m); + + block_connection_until_message_from_bus (context, bar, "unix fd reception on bar"); + + if (!(m = pop_message_waiting_for_memory (bar))) + _dbus_assert_not_reached("Failed to receive msg"); + + if (!dbus_message_is_signal(m, "a.b.c", "d")) + _dbus_assert_not_reached("bogus message received"); + + if (!dbus_message_get_args(m, + &error, + DBUS_TYPE_UNIX_FD, &x, + DBUS_TYPE_UNIX_FD, &y, + DBUS_TYPE_UNIX_FD, &z, + DBUS_TYPE_INVALID)) + _dbus_assert_not_reached("Failed to parse fds."); + + dbus_message_unref(m); + + if (write(x, "X", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #1"); + if (write(y, "Y", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #2"); + if (write(z, "Z", 1) != 1) + _dbus_assert_not_reached("Failed to write to pipe #2/2nd fd"); + + if (!_dbus_close(x, &error)) + _dbus_assert_not_reached("Failed to close pipe #1/other side "); + if (!_dbus_close(y, &error)) + _dbus_assert_not_reached("Failed to close pipe #2/other side "); + if (!_dbus_close(z, &error)) + _dbus_assert_not_reached("Failed to close pipe #2/other size 2nd fd "); + + if (read(one[1], &r, 1) != 1 || r != 'X') + _dbus_assert_not_reached("Failed to read value from pipe."); + if (read(two[1], &r, 1) != 1 || r != 'Y') + _dbus_assert_not_reached("Failed to read value from pipe."); + if (read(two[1], &r, 1) != 1 || r != 'Z') + _dbus_assert_not_reached("Failed to read value from pipe."); + + if (!_dbus_close(one[1], &error)) + _dbus_assert_not_reached("Failed to close pipe #1 "); + if (!_dbus_close(two[1], &error)) + _dbus_assert_not_reached("Failed to close pipe #2 "); + + _dbus_verbose ("Disconnecting foo\n"); + kill_client_connection_unchecked (foo); + + _dbus_verbose ("Disconnecting bar\n"); + kill_client_connection_unchecked (bar); + + bus_context_unref (context); + + return TRUE; +} +#endif + #endif /* DBUS_BUILD_TESTS */ diff --git a/bus/test-main.c b/bus/test-main.c index 994550e2..2583f9a8 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "selinux.h" #ifdef DBUS_BUILD_TESTS @@ -69,6 +70,7 @@ test_post_hook (void) if (_dbus_getenv ("DBUS_TEST_SELINUX")) bus_selinux_shutdown (); check_memleaks (progname); + _dbus_check_fdleaks(); } int @@ -138,6 +140,14 @@ main (int argc, char **argv) die ("service reload"); test_post_hook (); +#ifdef HAVE_UNIX_FD_PASSING + test_pre_hook (); + printf ("%s: Running unix fd passing test\n", argv[0]); + if (!bus_unix_fds_passing_test (&test_data_dir)) + die ("unix fd passing"); + test_post_hook (); +#endif + printf ("%s: Success\n", argv[0]); diff --git a/bus/test.h b/bus/test.h index 5aab0ea9..6d36b9c4 100644 --- a/bus/test.h +++ b/bus/test.h @@ -51,7 +51,9 @@ void bus_test_run_everything (BusContext *context); BusContext* bus_context_new_test (const DBusString *test_data_dir, const char *filename); - +#ifdef HAVE_UNIX_FD_PASSING +dbus_bool_t bus_unix_fds_passing_test (const DBusString *test_data_dir); +#endif #endif -- cgit From 44f3a1465a5a3f6d2e022409a960443441662629 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:22:51 +0200 Subject: cloexec: set FD_CLOEXEC for all full duplex pipes All users of full duplex pipes enable FD_CLOEXEC later anyway so let's just do it as part of _dbus_full_duplex_pipe. By side effect this allows to make use of SOCK_CLOEXEC which fixes a race when forking/execing from a different thread at the same time as we ar in this function. --- bus/main.c | 3 --- dbus/dbus-server-debug-pipe.c | 3 --- dbus/dbus-spawn.c | 3 --- dbus/dbus-sysdeps-unix.c | 29 ++++++++++++++++++++++++++--- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/bus/main.c b/bus/main.c index 51538fe7..d86825a5 100644 --- a/bus/main.c +++ b/bus/main.c @@ -208,9 +208,6 @@ setup_reload_pipe (DBusLoop *loop) exit (1); } - _dbus_fd_set_close_on_exec (reload_pipe[0]); - _dbus_fd_set_close_on_exec (reload_pipe[1]); - watch = _dbus_watch_new (reload_pipe[RELOAD_READ_END], DBUS_WATCH_READABLE, TRUE, handle_reload_watch, NULL, NULL); diff --git a/dbus/dbus-server-debug-pipe.c b/dbus/dbus-server-debug-pipe.c index 24b0ce34..b59f7491 100644 --- a/dbus/dbus-server-debug-pipe.c +++ b/dbus/dbus-server-debug-pipe.c @@ -253,9 +253,6 @@ _dbus_transport_debug_pipe_new (const char *server_name, return NULL; } - _dbus_fd_set_close_on_exec (client_fd); - _dbus_fd_set_close_on_exec (server_fd); - client_transport = _dbus_transport_new_for_socket (client_fd, NULL, &address); if (client_transport == NULL) diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index f4e3b587..b0b843ea 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -1123,9 +1123,6 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) goto cleanup_and_fail; - _dbus_fd_set_close_on_exec (babysitter_pipe[0]); - _dbus_fd_set_close_on_exec (babysitter_pipe[1]); - /* Setting up the babysitter is only useful in the parent, * but we don't want to run out of memory and fail * after we've already forked, since then we'd leak diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 597398bb..8b225e7e 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -3067,6 +3067,8 @@ _dbus_print_backtrace (void) * Creates a full-duplex pipe (as in socketpair()). * Sets both ends of the pipe nonblocking. * + * Marks both file descriptors as close-on-exec + * * @todo libdbus only uses this for the debug-pipe server, so in * principle it could be in dbus-sysdeps-util.c, except that * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the @@ -3086,16 +3088,37 @@ _dbus_full_duplex_pipe (int *fd1, { #ifdef HAVE_SOCKETPAIR int fds[2]; + int retval; - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) < 0) +#ifdef SOCK_CLOEXEC + dbus_bool_t cloexec_done; + + retval = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds); + cloexec_done = retval >= 0; + + if (retval < 0 && errno == EINVAL) +#endif + { + retval = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + } + + if (retval < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not create full-duplex pipe"); return FALSE; } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + +#ifdef SOCK_CLOEXEC + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec (fds[0]); + _dbus_fd_set_close_on_exec (fds[1]); + } + if (!blocking && (!_dbus_set_fd_nonblocking (fds[0], NULL) || !_dbus_set_fd_nonblocking (fds[1], NULL))) -- cgit From 18b08180aa5a4417fa1d6d268a1aad894e8a4549 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:43:31 +0200 Subject: build-system: define _GNU_SOURCE centrally Instead of having everyone define _GNU_SOURCE and similar macros seperately, simply do so centrally by using AC_USE_SYSTEM_EXTENSIONS --- bus/dir-watch-inotify.c | 1 - configure.in | 1 + dbus/dbus-sysdeps-unix.c | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bus/dir-watch-inotify.c b/bus/dir-watch-inotify.c index 44ec9cf0..12fdbb07 100644 --- a/bus/dir-watch-inotify.c +++ b/bus/dir-watch-inotify.c @@ -24,7 +24,6 @@ #include -#define _GNU_SOURCE #include #include #include diff --git a/configure.in b/configure.in index 0732adcd..62312eb0 100644 --- a/configure.in +++ b/configure.in @@ -57,6 +57,7 @@ AC_SUBST(DBUS_VERSION) AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX +AC_USE_SYSTEM_EXTENSIONS AC_ISC_POSIX AC_HEADER_STDC AC_C_INLINE diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 8b225e7e..9631be18 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -22,7 +22,7 @@ * */ -#define _GNU_SOURCE +#include #include "dbus-internals.h" #include "dbus-sysdeps.h" -- cgit From 3c319c71938ab06cf6f1750750927ffb5c200de4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Apr 2009 04:45:46 +0200 Subject: cloexec: make use of pipe2(O_CLOEXEC) when available This should fix another CLOEXEC race. --- configure.in | 2 ++ dbus/dbus-spawn.c | 34 +++++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/configure.in b/configure.in index 62312eb0..7e0c7d5a 100644 --- a/configure.in +++ b/configure.in @@ -816,6 +816,8 @@ fi AC_CHECK_FUNCS(getpeerucred getpeereid) +AC_CHECK_FUNCS(pipe2) + #### Abstract sockets if test x$enable_abstract_sockets = xauto; then diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index b0b843ea..fa2e15c1 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -21,6 +21,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ + +#include + #include "dbus-spawn.h" #include "dbus-sysdeps-unix.h" #include "dbus-internals.h" @@ -805,9 +808,25 @@ static dbus_bool_t make_pipe (int p[2], DBusError *error) { + int retval; + +#ifdef HAVE_PIPE2 + dbus_bool_t cloexec_done; + + retval = pipe2 (p, O_CLOEXEC); + cloexec_done = retval >= 0; + + /* Check if kernel seems to be too old to know pipe2(). We assume + that if pipe2 is available, O_CLOEXEC is too. */ + if (retval < 0 && errno == ENOSYS) +#endif + { + retval = pipe(p); + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - if (pipe (p) < 0) + + if (retval < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FAILED, @@ -816,6 +835,14 @@ make_pipe (int p[2], return FALSE; } +#ifdef HAVE_PIPE2 + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec (p[0]); + _dbus_fd_set_close_on_exec (p[1]); + } + return TRUE; } @@ -1117,9 +1144,6 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, if (!make_pipe (child_err_report_pipe, error)) goto cleanup_and_fail; - _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]); - _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]); - if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) goto cleanup_and_fail; -- cgit From 89318bbeb4076d8d9de9831d69621fc1411760d3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 6 May 2009 03:41:50 +0200 Subject: cloexec: set all sockets that are created with SOCK_CLOEXEC Since all socket users enable FD_CLOEXEC anyway we can just do that in _dbus_open_socket() and be done with it for all cases. By side effect this allows us to use SOCK_CLOEXEC and hence close the CLOEXEC race. --- configure.in | 2 +- dbus/dbus-server-socket.c | 5 ---- dbus/dbus-server-unix.c | 3 +-- dbus/dbus-sysdeps-unix.c | 62 ++++++++++++++++++++++++++++++++++++++++---- dbus/dbus-transport-socket.c | 2 -- dbus/dbus-transport-unix.c | 2 -- 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/configure.in b/configure.in index 7e0c7d5a..b087b003 100644 --- a/configure.in +++ b/configure.in @@ -816,7 +816,7 @@ fi AC_CHECK_FUNCS(getpeerucred getpeereid) -AC_CHECK_FUNCS(pipe2) +AC_CHECK_FUNCS(pipe2 accept4) #### Abstract sockets diff --git a/dbus/dbus-server-socket.c b/dbus/dbus-server-socket.c index 0cd2bb6c..b663e49d 100644 --- a/dbus/dbus-server-socket.c +++ b/dbus/dbus-server-socket.c @@ -195,8 +195,6 @@ socket_handle_watch (DBusWatch *watch, } else { - _dbus_fd_set_close_on_exec (client_fd); - if (!handle_new_client_fd_and_unlock (server, client_fd)) _dbus_verbose ("Rejected client connection due to lack of memory\n"); } @@ -413,9 +411,6 @@ _dbus_server_new_for_tcp_socket (const char *host, goto failed_1; } - for (i = 0 ; i < nlisten_fds ; i++) - _dbus_fd_set_close_on_exec (listen_fds[i]); - _dbus_string_init_const (&host_str, host); if (!_dbus_string_append (&address, "tcp:host=") || !_dbus_address_append_escaped (&address, &host_str) || diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c index 1dda5d19..86a64c86 100644 --- a/dbus/dbus-server-unix.c +++ b/dbus/dbus-server-unix.c @@ -201,8 +201,7 @@ _dbus_server_new_for_domain_socket (const char *path, } listen_fd = _dbus_listen_unix_socket (path, abstract, error); - _dbus_fd_set_close_on_exec (listen_fd); - + if (listen_fd < 0) { _DBUS_ASSERT_ERROR_IS_SET (error); diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 9631be18..3312bd17 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -94,9 +94,28 @@ _dbus_open_socket (int *fd_p, int protocol, DBusError *error) { - *fd_p = socket (domain, type, protocol); +#ifdef SOCK_CLOEXEC + dbus_bool_t cloexec_done; + + *fd_p = socket (domain, type | SOCK_CLOEXEC, protocol); + cloexec_done = *fd_p >= 0; + + /* Check if kernel seems to be too old to know SOCK_CLOEXEC */ + if (*fd_p < 0 && errno == EINVAL) +#endif + { + *fd_p = socket (domain, type, protocol); + } + if (*fd_p >= 0) { +#ifdef SOCK_CLOEXEC + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec(*fd_p); + } + _dbus_verbose ("socket fd %d opened\n", *fd_p); return TRUE; } @@ -120,6 +139,9 @@ _dbus_open_tcp_socket (int *fd, /** * Opens a UNIX domain socket (as in the socket() call). * Does not bind the socket. + * + * This will set FD_CLOEXEC for the socket returned + * * @param fd return location for socket descriptor * @param error return location for an error * @returns #FALSE if error is set @@ -770,6 +792,8 @@ _dbus_write_two (int fd, * requested (it's possible only on Linux; see "man 7 unix" on Linux). * On non-Linux abstract socket usage always fails. * + * This will set FD_CLOEXEC for the socket returned. + * * @param path the path to UNIX domain socket * @param abstract #TRUE to use abstract namespace * @param error return location for error code @@ -906,6 +930,8 @@ _dbus_set_local_creds (int fd, dbus_bool_t on) * see "man 7 unix" on Linux). * On non-Linux abstract socket usage always fails. * + * This will set FD_CLOEXEC for the socket returned + * * @param path the socket name * @param abstract #TRUE to use abstract namespace * @param error return location for errors @@ -1042,6 +1068,8 @@ _dbus_listen_unix_socket (const char *path, * and port. The connection fd is returned, and is set up as * nonblocking. * + * This will set FD_CLOEXEC for the socket returned + * * @param host the host name to connect to * @param port the port to connect to * @param family the address family to listen on, NULL for all @@ -1149,6 +1177,8 @@ _dbus_connect_tcp_socket (const char *host, * a random free port is used and returned in the port parameter. * If inaddr_any is specified, the hostname is ignored. * + * This will set FD_CLOEXEC for the socket returned + * * @param host the host name to listen on * @param port the port to listen on, if zero a free port will be used * @param family the address family to listen on, NULL for all @@ -1669,6 +1699,8 @@ _dbus_send_credentials_socket (int server_fd, * Accepts a connection on a listening socket. * Handles EINTR for you. * + * This will enable FD_CLOEXEC for the returned socket. + * * @param listen_fd the listen file descriptor * @returns the connection fd of the client, or -1 on error */ @@ -1678,12 +1710,25 @@ _dbus_accept (int listen_fd) int client_fd; struct sockaddr addr; socklen_t addrlen; +#ifdef HAVE_ACCEPT4 + dbus_bool_t cloexec_done; +#endif addrlen = sizeof (addr); - + retry: - client_fd = accept (listen_fd, &addr, &addrlen); - + +#ifdef HAVE_ACCEPT4 + /* We assume that if accept4 is available SOCK_CLOEXEC is too */ + client_fd = accept4 (listen_fd, &addr, &addrlen, SOCK_CLOEXEC); + cloexec_done = client_fd >= 0; + + if (client_fd < 0 && errno == ENOSYS) +#endif + { + client_fd = accept (listen_fd, &addr, &addrlen); + } + if (client_fd < 0) { if (errno == EINTR) @@ -1691,7 +1736,14 @@ _dbus_accept (int listen_fd) } _dbus_verbose ("client fd %d accepted\n", client_fd); - + +#ifdef HAVE_ACCEPT4 + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec(client_fd); + } + return client_fd; } diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index c9d4d93c..cc49c851 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -1324,8 +1324,6 @@ _dbus_transport_new_for_tcp_socket (const char *host, return NULL; } - _dbus_fd_set_close_on_exec (fd); - _dbus_verbose ("Successfully connected to tcp socket %s:%s\n", host, port); diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index a13fde17..777c9411 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -85,8 +85,6 @@ _dbus_transport_new_for_domain_socket (const char *path, goto failed_0; } - _dbus_fd_set_close_on_exec (fd); - _dbus_verbose ("Successfully connected to unix socket %s\n", path); -- cgit From c200e0304d6f53a0fd47f524386b02b27c0c45f6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Apr 2009 04:38:27 +0200 Subject: auth: add fd passing negotiation support This adds two new directives to the auth protocol: NEGOTIATE_UNIX_FD is sent by the client after the authentication was sucessful, i.e. OK was received. AGREE_UNIX_FD is then sent by the server if it can do unix fd passing as well. ERROR is returned when the server cannot or is unwilling to do unix fd passing. This should be compatible with existing D-Bus implementations which will naturally return ERROR on NEGOTIATE_UNIX_FD. --- bus/dispatch.c | 5 ++ dbus/dbus-auth.c | 184 ++++++++++++++++++++++++++++++++++------ dbus/dbus-auth.h | 2 + dbus/dbus-transport-protected.h | 3 + dbus/dbus-transport-socket.c | 6 +- dbus/dbus-transport.c | 1 - 6 files changed, 170 insertions(+), 31 deletions(-) diff --git a/bus/dispatch.c b/bus/dispatch.c index ca9a8433..8ed88dad 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -4791,6 +4791,11 @@ bus_unix_fds_passing_test(const DBusString *test_data_dir) if (!_dbus_close(two[0], &error)) _dbus_assert_not_reached("Failed to close pipe #2 "); + if (!(dbus_connection_can_send_type(foo, DBUS_TYPE_UNIX_FD))) + _dbus_assert_not_reached("Connection cannot do fd passing"); + + if (!(dbus_connection_can_send_type(bar, DBUS_TYPE_UNIX_FD))) + _dbus_assert_not_reached("Connection cannot do fd passing"); if (!dbus_connection_send (foo, m, NULL)) _dbus_assert_not_reached("Failed to send fds"); diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index ec7cf312..f1c83ae4 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -122,7 +122,9 @@ typedef enum { DBUS_AUTH_COMMAND_REJECTED, DBUS_AUTH_COMMAND_OK, DBUS_AUTH_COMMAND_ERROR, - DBUS_AUTH_COMMAND_UNKNOWN + DBUS_AUTH_COMMAND_UNKNOWN, + DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD, + DBUS_AUTH_COMMAND_AGREE_UNIX_FD } DBusAuthCommand; /** @@ -184,6 +186,9 @@ struct DBusAuth unsigned int already_got_mechanisms : 1; /**< Client already got mech list */ unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */ unsigned int buffer_outstanding : 1; /**< Buffer is "checked out" for reading data into */ + + unsigned int unix_fd_possible : 1; /**< This side could do unix fd passing */ + unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */ }; /** @@ -223,9 +228,10 @@ static dbus_bool_t send_rejected (DBusAuth *auth); static dbus_bool_t send_error (DBusAuth *auth, const char *message); static dbus_bool_t send_ok (DBusAuth *auth); -static dbus_bool_t send_begin (DBusAuth *auth, - const DBusString *args_from_ok); +static dbus_bool_t send_begin (DBusAuth *auth); static dbus_bool_t send_cancel (DBusAuth *auth); +static dbus_bool_t send_negotiate_unix_fd (DBusAuth *auth); +static dbus_bool_t send_agree_unix_fd (DBusAuth *auth); /** * Client states @@ -264,6 +270,9 @@ static dbus_bool_t handle_client_state_waiting_for_ok (DBusAuth *aut static dbus_bool_t handle_client_state_waiting_for_reject (DBusAuth *auth, DBusAuthCommand command, const DBusString *args); +static dbus_bool_t handle_client_state_waiting_for_agree_unix_fd (DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args); static const DBusAuthStateData client_state_need_send_auth = { "NeedSendAuth", NULL @@ -277,7 +286,10 @@ static const DBusAuthStateData client_state_waiting_for_ok = { static const DBusAuthStateData client_state_waiting_for_reject = { "WaitingForReject", handle_client_state_waiting_for_reject }; - +static const DBusAuthStateData client_state_waiting_for_agree_unix_fd = { + "WaitingForAgreeUnixFD", handle_client_state_waiting_for_agree_unix_fd +}; + /** * Common terminal states. Terminal states have handler == NULL. */ @@ -1522,9 +1534,21 @@ send_ok (DBusAuth *auth) } static dbus_bool_t -send_begin (DBusAuth *auth, - const DBusString *args_from_ok) +send_begin (DBusAuth *auth) { + + if (!_dbus_string_append (&auth->outgoing, + "BEGIN\r\n")) + return FALSE; + + goto_state (auth, &common_state_authenticated); + return TRUE; +} + +static dbus_bool_t +process_ok(DBusAuth *auth, + const DBusString *args_from_ok) { + int end_of_hex; /* "args_from_ok" should be the GUID, whitespace already pulled off the front */ @@ -1549,20 +1573,19 @@ send_begin (DBusAuth *auth, return TRUE; } - if (_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0) && - _dbus_string_append (&auth->outgoing, "BEGIN\r\n")) - { - _dbus_verbose ("Got GUID '%s' from the server\n", - _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); - - goto_state (auth, &common_state_authenticated); - return TRUE; - } - else - { + if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) { _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0); return FALSE; - } + } + + _dbus_verbose ("Got GUID '%s' from the server\n", + _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server)); + + if (auth->unix_fd_possible) + return send_negotiate_unix_fd(auth); + + _dbus_verbose("Not negotiating unix fd passing, since not possible\n"); + return send_begin (auth); } static dbus_bool_t @@ -1621,6 +1644,33 @@ process_data (DBusAuth *auth, return TRUE; } +static dbus_bool_t +send_negotiate_unix_fd (DBusAuth *auth) +{ + if (!_dbus_string_append (&auth->outgoing, + "NEGOTIATE_UNIX_FD\r\n")) + return FALSE; + + goto_state (auth, &client_state_waiting_for_agree_unix_fd); + return TRUE; +} + +static dbus_bool_t +send_agree_unix_fd (DBusAuth *auth) +{ + _dbus_assert(auth->unix_fd_possible); + + auth->unix_fd_negotiated = TRUE; + _dbus_verbose("Agreed to UNIX FD passing\n"); + + if (!_dbus_string_append (&auth->outgoing, + "AGREE_UNIX_FD\r\n")) + return FALSE; + + goto_state (auth, &server_state_waiting_for_begin); + return TRUE; +} + static dbus_bool_t handle_auth (DBusAuth *auth, const DBusString *args) { @@ -1712,9 +1762,13 @@ handle_server_state_waiting_for_auth (DBusAuth *auth, case DBUS_AUTH_COMMAND_ERROR: return send_rejected (auth); + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + return send_error (auth, "Need to authenticate first"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1741,9 +1795,13 @@ handle_server_state_waiting_for_data (DBusAuth *auth, goto_state (auth, &common_state_need_disconnect); return TRUE; + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + return send_error (auth, "Need to authenticate first"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1766,9 +1824,16 @@ handle_server_state_waiting_for_begin (DBusAuth *auth, goto_state (auth, &common_state_authenticated); return TRUE; + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + if (auth->unix_fd_possible) + return send_agree_unix_fd(auth); + else + return send_error(auth, "Unix FD passing not supported, not authenticated or otherwise not possible"); + case DBUS_AUTH_COMMAND_REJECTED: case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); @@ -1933,7 +1998,7 @@ handle_client_state_waiting_for_data (DBusAuth *auth, return process_rejected (auth, args); case DBUS_AUTH_COMMAND_OK: - return send_begin (auth, args); + return process_ok(auth, args); case DBUS_AUTH_COMMAND_ERROR: return send_cancel (auth); @@ -1942,6 +2007,8 @@ handle_client_state_waiting_for_data (DBusAuth *auth, case DBUS_AUTH_COMMAND_CANCEL: case DBUS_AUTH_COMMAND_BEGIN: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1958,7 +2025,7 @@ handle_client_state_waiting_for_ok (DBusAuth *auth, return process_rejected (auth, args); case DBUS_AUTH_COMMAND_OK: - return send_begin (auth, args); + return process_ok(auth, args); case DBUS_AUTH_COMMAND_DATA: case DBUS_AUTH_COMMAND_ERROR: @@ -1968,6 +2035,8 @@ handle_client_state_waiting_for_ok (DBusAuth *auth, case DBUS_AUTH_COMMAND_CANCEL: case DBUS_AUTH_COMMAND_BEGIN: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: return send_error (auth, "Unknown command"); } @@ -1990,12 +2059,46 @@ handle_client_state_waiting_for_reject (DBusAuth *auth, case DBUS_AUTH_COMMAND_OK: case DBUS_AUTH_COMMAND_ERROR: case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: default: goto_state (auth, &common_state_need_disconnect); return TRUE; } } +static dbus_bool_t +handle_client_state_waiting_for_agree_unix_fd(DBusAuth *auth, + DBusAuthCommand command, + const DBusString *args) +{ + switch (command) + { + case DBUS_AUTH_COMMAND_AGREE_UNIX_FD: + _dbus_assert(auth->unix_fd_possible); + auth->unix_fd_negotiated = TRUE; + _dbus_verbose("Sucessfully negotiated UNIX FD passing\n"); + return send_begin (auth); + + case DBUS_AUTH_COMMAND_ERROR: + _dbus_assert(auth->unix_fd_possible); + auth->unix_fd_negotiated = FALSE; + _dbus_verbose("Failed to negotiate UNIX FD passing\n"); + return send_begin (auth); + + case DBUS_AUTH_COMMAND_OK: + case DBUS_AUTH_COMMAND_DATA: + case DBUS_AUTH_COMMAND_REJECTED: + case DBUS_AUTH_COMMAND_AUTH: + case DBUS_AUTH_COMMAND_CANCEL: + case DBUS_AUTH_COMMAND_BEGIN: + case DBUS_AUTH_COMMAND_UNKNOWN: + case DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD: + default: + return send_error (auth, "Unknown command"); + } +} + /** * Mapping from command name to enum */ @@ -2005,13 +2108,15 @@ typedef struct { } DBusAuthCommandName; static const DBusAuthCommandName auth_command_names[] = { - { "AUTH", DBUS_AUTH_COMMAND_AUTH }, - { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, - { "DATA", DBUS_AUTH_COMMAND_DATA }, - { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, - { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, - { "OK", DBUS_AUTH_COMMAND_OK }, - { "ERROR", DBUS_AUTH_COMMAND_ERROR } + { "AUTH", DBUS_AUTH_COMMAND_AUTH }, + { "CANCEL", DBUS_AUTH_COMMAND_CANCEL }, + { "DATA", DBUS_AUTH_COMMAND_DATA }, + { "BEGIN", DBUS_AUTH_COMMAND_BEGIN }, + { "REJECTED", DBUS_AUTH_COMMAND_REJECTED }, + { "OK", DBUS_AUTH_COMMAND_OK }, + { "ERROR", DBUS_AUTH_COMMAND_ERROR }, + { "NEGOTIATE_UNIX_FD", DBUS_AUTH_COMMAND_NEGOTIATE_UNIX_FD }, + { "AGREE_UNIX_FD", DBUS_AUTH_COMMAND_AGREE_UNIX_FD } }; static DBusAuthCommand @@ -2685,6 +2790,31 @@ _dbus_auth_set_context (DBusAuth *auth, &auth->context, 0, _dbus_string_get_length (context)); } +/** + * Sets whether unix fd passing is potentially on the transport and + * hence shall be negotiated. + * + * @param auth the auth conversation + * @param b TRUE when unix fd passing shall be negotiated, otherwise FALSE + */ +void +_dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b) +{ + auth->unix_fd_possible = b; +} + +/** + * Queries whether unix fd passing was sucessfully negotiated. + * + * @param auth the auth conversion + * @returns #TRUE when unix fd passing was negotiated. + */ +dbus_bool_t +_dbus_auth_get_unix_fd_negotiated(DBusAuth *auth) +{ + return auth->unix_fd_negotiated; +} + /** @} */ /* tests in dbus-auth-util.c */ diff --git a/dbus/dbus-auth.h b/dbus/dbus-auth.h index 14f8320a..5680df11 100644 --- a/dbus/dbus-auth.h +++ b/dbus/dbus-auth.h @@ -75,6 +75,8 @@ dbus_bool_t _dbus_auth_set_context (DBusAuth *auth, const DBusString *context); const char* _dbus_auth_get_guid_from_server(DBusAuth *auth); +void _dbus_auth_set_unix_fd_possible(DBusAuth *auth, dbus_bool_t b); +dbus_bool_t _dbus_auth_get_unix_fd_negotiated(DBusAuth *auth); DBUS_END_DECLS diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 023549d0..8c389a6d 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -144,6 +144,9 @@ DBusTransportOpenResult _dbus_transport_open_platform_specific (DBusAddressEntry DBusTransport **transport_p, DBusError *error); +#define DBUS_TRANSPORT_CAN_SEND_UNIX_FD(x) \ + _dbus_auth_get_unix_fd_negotiated((x)->auth) + DBUS_END_DECLS #endif /* DBUS_TRANSPORT_PROTECTED_H */ diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index cc49c851..76699506 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -594,7 +594,7 @@ do_writing (DBusTransport *transport) #endif #ifdef HAVE_UNIX_FD_PASSING - if (socket_transport->message_bytes_written <= 0 && transport->can_pass_unix_fd) + if (socket_transport->message_bytes_written <= 0 && DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)) { /* Send the fds along with the first byte of the message */ const int *unix_fds; @@ -777,7 +777,7 @@ do_reading (DBusTransport *transport) &buffer); #ifdef HAVE_UNIX_FD_PASSING - if (transport->can_pass_unix_fd) + if (DBUS_TRANSPORT_CAN_SEND_UNIX_FD(transport)) { int *fds, n_fds; @@ -1244,7 +1244,7 @@ _dbus_transport_new_for_socket (int fd, goto failed_4; #ifdef HAVE_UNIX_FD_PASSING - socket_transport->base.can_pass_unix_fd = _dbus_socket_can_pass_unix_fd(fd); + _dbus_auth_set_unix_fd_possible(socket_transport->base.auth, _dbus_socket_can_pass_unix_fd(fd)); #endif socket_transport->fd = fd; diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 97ee0e9b..7ba01357 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -184,7 +184,6 @@ _dbus_transport_init_base (DBusTransport *transport, transport->credentials = creds; #ifdef HAVE_UNIX_FD_PASSING - transport->can_pass_unix_fd = FALSE; transport->unix_fds = NULL; transport->n_unix_fds = 0; #endif -- cgit From cbf0874f018c6ddd831de26b1ac5bcf7b517e2da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 May 2009 00:01:50 +0200 Subject: memset: replace memset() by _DBUS_ZERO where applicable --- bus/desktop-file.c | 2 +- dbus/dbus-md5.c | 2 +- dbus/dbus-sha.c | 2 +- dbus/dbus-sysdeps-unix.c | 8 ++++---- dbus/dbus-sysdeps-win.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bus/desktop-file.c b/bus/desktop-file.c index 2ba77292..e925b2ea 100644 --- a/bus/desktop-file.c +++ b/bus/desktop-file.c @@ -330,7 +330,7 @@ new_line (BusDesktopFileParser *parser) line = §ion->lines[section->n_lines++]; - memset (line, 0, sizeof (BusDesktopFileLine)); + _DBUS_ZERO(*line); return line; } diff --git a/dbus/dbus-md5.c b/dbus/dbus-md5.c index 589c8d2a..8405f360 100644 --- a/dbus/dbus-md5.c +++ b/dbus/dbus-md5.c @@ -451,7 +451,7 @@ _dbus_md5_final (DBusMD5Context *context, /* some kind of security paranoia, though it seems pointless * to me given the nonzeroed stuff flying around */ - memset ((void*)context, '\0', sizeof (DBusMD5Context)); + _DBUS_ZERO(*context); return TRUE; } diff --git a/dbus/dbus-sha.c b/dbus/dbus-sha.c index 8ec50b6f..eb3439f8 100644 --- a/dbus/dbus-sha.c +++ b/dbus/dbus-sha.c @@ -465,7 +465,7 @@ _dbus_sha_final (DBusSHAContext *context, /* some kind of security paranoia, though it seems pointless * to me given the nonzeroed stuff flying around */ - memset ((void*)context, '\0', sizeof (DBusSHAContext)); + _DBUS_ZERO(*context); return TRUE; } diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 3312bd17..07db1c5f 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1382,13 +1382,13 @@ write_credentials_byte (int server_fd, iov.iov_base = buf; iov.iov_len = 1; - memset (&msg, 0, sizeof (msg)); + _DBUS_ZERO(msg); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsg; msg.msg_controllen = sizeof (cmsg); - memset (&cmsg, 0, sizeof (cmsg)); + _DBUS_ZERO(cmsg); cmsg.hdr.cmsg_len = sizeof (cmsg); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; @@ -1498,12 +1498,12 @@ _dbus_read_credentials_socket (int client_fd, iov.iov_base = &buf; iov.iov_len = 1; - memset (&msg, 0, sizeof (msg)); + _DBUS_ZERO(msg); msg.msg_iov = &iov; msg.msg_iovlen = 1; #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) - memset (&cmsg, 0, sizeof (cmsg)); + _DBUS_ZERO(cmsg); msg.msg_control = &cmsg; msg.msg_controllen = sizeof (cmsg); #endif diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index a67e502a..507f2c00 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -2616,7 +2616,7 @@ static void dump_backtrace_for_thread(HANDLE hThread) DPRINTF("Backtrace:\n"); - memset(&context, 0, sizeof(context)); + _DBUS_ZERO(context); context.ContextFlags = CONTEXT_FULL; SuspendThread(hThread); @@ -2628,7 +2628,7 @@ static void dump_backtrace_for_thread(HANDLE hThread) return; } - memset(&sf, 0, sizeof(sf)); + _DBUS_ZERO(sf); #ifdef __i386__ sf.AddrFrame.Offset = context.Ebp; -- cgit From 74bff5af804817372aece931c792b53c8ec534e1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 May 2009 01:56:54 +0200 Subject: git: enable whitespace checking commit hook during in autogen.sh Trailing whitespace sucks. This change modifies autogen.sh to activate the example pre-commit that ships with git. It will make sure that from then on no further commits with trailing whitespace can be made --- autogen.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/autogen.sh b/autogen.sh index 76561338..bf53b277 100755 --- a/autogen.sh +++ b/autogen.sh @@ -13,6 +13,12 @@ FILE=dbus-1.pc.in DIE=0 +if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then + echo "Activating pre-commit hook." + cp -av .git/hooks/pre-commit.sample .git/hooks/pre-commit + chmod -c +x .git/hooks/pre-commit +fi + (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "You must have autoconf installed to compile $PROJECT." -- cgit From 9293e823767daee79386cc797510808f4eed01a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 May 2009 22:30:14 +0200 Subject: atomic: implement atomic operations based on gcc's __sync extension Newer gccs and intel ccs support a __sync extension for making use of atomic operations. This patch replaces the handcrafted x86 atomic operation support with usage of __sync. __sync is supported by more processors and by more compilers than the old assembler code. Also, this extension has been available on gcc for quite a while now for x86, so replacing the old assembler code should only be a loss when very old compiilers are used. --- configure.in | 50 ++++++++++++++++-------------------------------- dbus/dbus-internals.h | 9 +++++++-- dbus/dbus-sysdeps-unix.c | 29 +++++----------------------- dbus/dbus-threads.c | 2 ++ 4 files changed, 30 insertions(+), 60 deletions(-) diff --git a/configure.in b/configure.in index b087b003..c2c6a8c9 100644 --- a/configure.in +++ b/configure.in @@ -590,41 +590,23 @@ if test "x$dbus_cv_va_val_copy" = "xno"; then fi -#### Atomic integers (checks by Sebastian Wilhelmi for GLib) -AC_MSG_CHECKING([whether to use inline assembler routines for atomic integers]) -have_atomic_inc_cond=0 -if test x"$GCC" = xyes; then - if test "x$enable_ansi" = "xyes"; then - AC_MSG_RESULT([no]) - else - case $host_cpu in - i386) - AC_MSG_RESULT([no]) - ;; - i?86) - case $host_os in - darwin*) - AC_MSG_RESULT([darwin]) - # check at compile-time, so that it is possible to build universal - # (with multiple architectures at once on the compile line) - have_atomic_inc_cond="(defined(__i386__) || defined(__x86_64__))" - ;; - *) - AC_MSG_RESULT([i486]) - have_atomic_inc_cond=1 - ;; - esac - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac - fi +#### Atomic integers + +AC_CACHE_CHECK([whether $CC knows __sync_sub_and_fetch()], + dbus_cv_sync_sub_and_fetch, + [AC_LINK_IFELSE( + AC_LANG_PROGRAM([], [[int a = 4; __sync_sub_and_fetch(&a, 4);]]), + [dbus_cv_sync_sub_and_fetch=yes], + [dbus_cv_sync_sub_and_fetch=no]) + ]) + +if test "x$dbus_cv_sync_sub_and_fetch" = "xyes" ; then + have_sync=1 +else + have_sync=0 fi -AC_DEFINE_UNQUOTED([DBUS_USE_ATOMIC_INT_486_COND], [$have_atomic_inc_cond], - [Always defined; expands to 1 if we should use atomic integer implementation for 486, else 0]) -AC_DEFINE_UNQUOTED(DBUS_HAVE_ATOMIC_INT_COND, [$have_atomic_inc_cond], - [Always defined; expands to 1 if we have an atomic integer implementation, else 0]) + +AC_DEFINE_UNQUOTED([DBUS_USE_SYNC], [$have_sync], [Use the gcc __sync extension]) #### Various functions AC_CHECK_LIB(socket,socket) diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 3e5f989d..835fe46a 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -294,18 +294,23 @@ _DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots); _DBUS_DECLARE_GLOBAL_LOCK (server_slots); _DBUS_DECLARE_GLOBAL_LOCK (message_slots); /* 5-10 */ -_DBUS_DECLARE_GLOBAL_LOCK (atomic); _DBUS_DECLARE_GLOBAL_LOCK (bus); _DBUS_DECLARE_GLOBAL_LOCK (bus_datas); _DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs); _DBUS_DECLARE_GLOBAL_LOCK (system_users); -/* 10-15 */ _DBUS_DECLARE_GLOBAL_LOCK (message_cache); +/* 10-14 */ _DBUS_DECLARE_GLOBAL_LOCK (shared_connections); _DBUS_DECLARE_GLOBAL_LOCK (win_fds); _DBUS_DECLARE_GLOBAL_LOCK (sid_atom_cache); _DBUS_DECLARE_GLOBAL_LOCK (machine_uuid); + +#if !DBUS_USE_SYNC +_DBUS_DECLARE_GLOBAL_LOCK (atomic); #define _DBUS_N_GLOBAL_LOCKS (15) +#else +#define _DBUS_N_GLOBAL_LOCKS (14) +#endif dbus_bool_t _dbus_threads_init_debug (void); diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 07db1c5f..42aa9674 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -2180,23 +2180,8 @@ _dbus_parse_uid (const DBusString *uid_str, return TRUE; } - +#if !DBUS_USE_SYNC _DBUS_DEFINE_GLOBAL_LOCK (atomic); - -#if DBUS_USE_ATOMIC_INT_486_COND -/* Taken from CVS version 1.7 of glibc's sysdeps/i386/i486/atomicity.h */ -/* Since the asm stuff here is gcc-specific we go ahead and use "inline" also */ -static inline dbus_int32_t -atomic_exchange_and_add (DBusAtomic *atomic, - volatile dbus_int32_t val) -{ - register dbus_int32_t result; - - __asm__ __volatile__ ("lock; xaddl %0,%1" - : "=r" (result), "=m" (atomic->value) - : "0" (val), "m" (atomic->value)); - return result; -} #endif /** @@ -2204,14 +2189,12 @@ atomic_exchange_and_add (DBusAtomic *atomic, * * @param atomic pointer to the integer to increment * @returns the value before incrementing - * - * @todo implement arch-specific faster atomic ops */ dbus_int32_t _dbus_atomic_inc (DBusAtomic *atomic) { -#if DBUS_USE_ATOMIC_INT_486_COND - return atomic_exchange_and_add (atomic, 1); +#if DBUS_USE_SYNC + return __sync_add_and_fetch(&atomic->value, 1)-1; #else dbus_int32_t res; _DBUS_LOCK (atomic); @@ -2227,14 +2210,12 @@ _dbus_atomic_inc (DBusAtomic *atomic) * * @param atomic pointer to the integer to decrement * @returns the value before decrementing - * - * @todo implement arch-specific faster atomic ops */ dbus_int32_t _dbus_atomic_dec (DBusAtomic *atomic) { -#if DBUS_USE_ATOMIC_INT_486_COND - return atomic_exchange_and_add (atomic, -1); +#if DBUS_USE_SYNC + return __sync_sub_and_fetch(&atomic->value, 1)+1; #else dbus_int32_t res; diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index 00c1a4b1..cb9c7237 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -424,7 +424,9 @@ init_locks (void) LOCK_ADDR (pending_call_slots), LOCK_ADDR (server_slots), LOCK_ADDR (message_slots), +#if !DBUS_USE_SYNC LOCK_ADDR (atomic), +#endif LOCK_ADDR (bus), LOCK_ADDR (bus_datas), LOCK_ADDR (shutdown_funcs), -- cgit From ec901d786f6de6e6f870279e2d955f491619c559 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 May 2009 22:34:43 +0200 Subject: byteswap: make use of glibc specific bytswap primitives glibc knows three bswap_{16|32|64}() calls that internally make use of a gcc extension to implement faster byteswapping. We should make use of it if we can. --- configure.in | 2 ++ dbus/dbus-marshal-basic.h | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/configure.in b/configure.in index c2c6a8c9..036679ce 100644 --- a/configure.in +++ b/configure.in @@ -679,6 +679,8 @@ AC_CHECK_HEADERS(execinfo.h, [AC_CHECK_FUNCS(backtrace)]) AC_CHECK_HEADERS(errno.h) +AC_CHECK_HEADERS(byteswap.h) + AC_CHECK_HEADERS(unistd.h) # checking for a posix version of getpwnam_r diff --git a/dbus/dbus-marshal-basic.h b/dbus/dbus-marshal-basic.h index bcc15ee9..f09c5208 100644 --- a/dbus/dbus-marshal-basic.h +++ b/dbus/dbus-marshal-basic.h @@ -26,6 +26,11 @@ #define DBUS_MARSHAL_BASIC_H #include + +#ifdef HAVE_BYTESWAP_H +#include +#endif + #include #include #include @@ -37,6 +42,11 @@ #define DBUS_COMPILER_BYTE_ORDER DBUS_LITTLE_ENDIAN #endif +#ifdef HAVE_BYTESWAP_H +#define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) bswap_16(val) +#define DBUS_UINT32_SWAP_LE_BE_CONSTANT(val) bswap_32(val) +#else /* HAVE_BYTESWAP_H */ + #define DBUS_UINT16_SWAP_LE_BE_CONSTANT(val) ((dbus_uint16_t) ( \ (dbus_uint16_t) ((dbus_uint16_t) (val) >> 8) | \ (dbus_uint16_t) ((dbus_uint16_t) (val) << 8))) @@ -47,8 +57,14 @@ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0x00ff0000U) >> 8) | \ (((dbus_uint32_t) (val) & (dbus_uint32_t) 0xff000000U) >> 24))) +#endif /* HAVE_BYTESWAP_H */ + #ifdef DBUS_HAVE_INT64 +#ifdef HAVE_BYTESWAP_H +#define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) bswap_64(val) +#else /* HAVE_BYTESWAP_H */ + #define DBUS_UINT64_SWAP_LE_BE_CONSTANT(val) ((dbus_uint64_t) ( \ (((dbus_uint64_t) (val) & \ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0x00000000000000ff)) << 56) | \ @@ -68,6 +84,8 @@ (dbus_uint64_t) DBUS_UINT64_CONSTANT (0xff00000000000000)) >> 56))) #endif /* DBUS_HAVE_INT64 */ +#endif /* HAVE_BYTESWAP_H */ + #define DBUS_UINT16_SWAP_LE_BE(val) (DBUS_UINT16_SWAP_LE_BE_CONSTANT (val)) #define DBUS_INT16_SWAP_LE_BE(val) ((dbus_int16_t)DBUS_UINT16_SWAP_LE_BE_CONSTANT (val)) -- cgit From bfad32422f1f78bce4de1e88a4afb5cc295bb877 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 May 2009 01:32:46 +0200 Subject: unix-fd: add logic to count unix fds the same way as allocated memory This make all counters count both bytes of memory and unix fds. --- dbus/dbus-connection.c | 119 +++++++++++++++++++++++++++++++++++++--- dbus/dbus-connection.h | 9 +++ dbus/dbus-message-internal.h | 6 +- dbus/dbus-message-private.h | 4 +- dbus/dbus-message.c | 89 +++++++++++++++++------------- dbus/dbus-resources.c | 98 +++++++++++++++++++++++++-------- dbus/dbus-resources.h | 21 ++++--- dbus/dbus-transport-protected.h | 8 +-- dbus/dbus-transport-socket.c | 3 +- dbus/dbus-transport.c | 94 +++++++++++++++++++++++++------ dbus/dbus-transport.h | 9 +++ 11 files changed, 353 insertions(+), 107 deletions(-) diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 8433b643..e46fbc3d 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -627,8 +627,8 @@ _dbus_connection_message_sent (DBusConnection *connection, connection, connection->n_outgoing); /* Save this link in the link cache also */ - _dbus_message_remove_size_counter (message, connection->outgoing_counter, - &link); + _dbus_message_remove_counter (message, connection->outgoing_counter, + &link); _dbus_list_prepend_link (&connection->link_cache, link); dbus_message_unref (message); @@ -1922,8 +1922,8 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection *con _dbus_list_prepend_link (&connection->outgoing_messages, preallocated->queue_link); - _dbus_message_add_size_counter_link (message, - preallocated->counter_link); + _dbus_message_add_counter_link (message, + preallocated->counter_link); dbus_free (preallocated); preallocated = NULL; @@ -2539,9 +2539,9 @@ free_outgoing_message (void *element, DBusMessage *message = element; DBusConnection *connection = data; - _dbus_message_remove_size_counter (message, - connection->outgoing_counter, - NULL); + _dbus_message_remove_counter (message, + connection->outgoing_counter, + NULL); dbus_message_unref (message); } @@ -5931,6 +5931,45 @@ dbus_connection_get_max_message_size (DBusConnection *connection) return res; } +/** + * Specifies the maximum number of unix fds a message on this + * connection is allowed to receive. Messages with more unix fds will + * result in disconnecting the connection. + * + * @param connection a #DBusConnection + * @param size maximum message unix fds the connection can receive + */ +void +dbus_connection_set_max_message_unix_fds (DBusConnection *connection, + long n) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_max_message_unix_fds (connection->transport, + n); + CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_message_unix_fds(). + * + * @param connection the connection + * @returns the max numer of unix fds of a single message + */ +long +dbus_connection_get_max_message_unix_fds (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_max_message_unix_fds (connection->transport); + CONNECTION_UNLOCK (connection); + return res; +} + /** * Sets the maximum total number of bytes that can be used for all messages * received on this connection. Messages count toward the maximum until @@ -5987,6 +6026,48 @@ dbus_connection_get_max_received_size (DBusConnection *connection) return res; } +/** + * Sets the maximum total number of unix fds that can be used for all messages + * received on this connection. Messages count toward the maximum until + * they are finalized. When the maximum is reached, the connection will + * not read more data until some messages are finalized. + * + * The semantics are analogous to those of dbus_connection_set_max_received_size(). + * + * @param connection the connection + * @param size the maximum size in bytes of all outstanding messages + */ +void +dbus_connection_set_max_received_unix_fds (DBusConnection *connection, + long n) +{ + _dbus_return_if_fail (connection != NULL); + + CONNECTION_LOCK (connection); + _dbus_transport_set_max_received_unix_fds (connection->transport, + n); + CONNECTION_UNLOCK (connection); +} + +/** + * Gets the value set by dbus_connection_set_max_received_unix_fds(). + * + * @param connection the connection + * @returns the max unix fds of all live messages + */ +long +dbus_connection_get_max_received_unix_fds (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + + CONNECTION_LOCK (connection); + res = _dbus_transport_get_max_received_unix_fds (connection->transport); + CONNECTION_UNLOCK (connection); + return res; +} + /** * Gets the approximate size in bytes of all messages in the outgoing * message queue. The size is approximate in that you shouldn't use @@ -6003,9 +6084,29 @@ dbus_connection_get_outgoing_size (DBusConnection *connection) long res; _dbus_return_val_if_fail (connection != NULL, 0); - + + CONNECTION_LOCK (connection); + res = _dbus_counter_get_size_value (connection->outgoing_counter); + CONNECTION_UNLOCK (connection); + return res; +} + +/** + * Gets the approximate number of uni fds of all messages in the + * outgoing message queue. + * + * @param connection the connection + * @returns the number of unix fds that have been queued up but not sent + */ +long +dbus_connection_get_outgoing_unix_fds (DBusConnection *connection) +{ + long res; + + _dbus_return_val_if_fail (connection != NULL, 0); + CONNECTION_LOCK (connection); - res = _dbus_counter_get_value (connection->outgoing_counter); + res = _dbus_counter_get_unix_fd_value (connection->outgoing_counter); CONNECTION_UNLOCK (connection); return res; } diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index c9b9aa34..3facc873 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -281,7 +281,16 @@ long dbus_connection_get_max_message_size (DBusConnection *connection); void dbus_connection_set_max_received_size (DBusConnection *connection, long size); long dbus_connection_get_max_received_size (DBusConnection *connection); + +void dbus_connection_set_max_message_unix_fds (DBusConnection *connection, + long n); +long dbus_connection_get_max_message_unix_fds (DBusConnection *connection); +void dbus_connection_set_max_received_unix_fds(DBusConnection *connection, + long n); +long dbus_connection_get_max_received_unix_fds(DBusConnection *connection); + long dbus_connection_get_outgoing_size (DBusConnection *connection); +long dbus_connection_get_outgoing_unix_fds (DBusConnection *connection); DBusPreallocatedSend* dbus_connection_preallocate_send (DBusConnection *connection); void dbus_connection_free_preallocated_send (DBusConnection *connection, diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index 032268f1..0565d611 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -40,11 +40,11 @@ void _dbus_message_get_unix_fds (DBusMessage *messgage, void _dbus_message_lock (DBusMessage *message); void _dbus_message_unlock (DBusMessage *message); -dbus_bool_t _dbus_message_add_size_counter (DBusMessage *message, +dbus_bool_t _dbus_message_add_counter (DBusMessage *message, DBusCounter *counter); -void _dbus_message_add_size_counter_link (DBusMessage *message, +void _dbus_message_add_counter_link (DBusMessage *message, DBusList *link); -void _dbus_message_remove_size_counter (DBusMessage *message, +void _dbus_message_remove_counter (DBusMessage *message, DBusCounter *counter, DBusList **link_return); diff --git a/dbus/dbus-message-private.h b/dbus/dbus-message-private.h index 1e8b107a..03257a9b 100644 --- a/dbus/dbus-message-private.h +++ b/dbus/dbus-message-private.h @@ -112,7 +112,7 @@ struct DBusMessage unsigned int in_cache : 1; /**< Has been "freed" since it's in the cache (this is a debug feature) */ #endif - DBusList *size_counters; /**< 0-N DBusCounter used to track message size. */ + DBusList *counters; /**< 0-N DBusCounter used to track message size/unix fds. */ long size_counter_delta; /**< Size we incremented the size counters by. */ dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; /**< Incremented when iterators are invalidated. */ @@ -130,6 +130,8 @@ struct DBusMessage them when adding or removing them here. */ unsigned n_unix_fds; /**< Number of valid fds in the array */ unsigned n_unix_fds_allocated; /**< Allocated size of the array */ + + long unix_fd_counter_delta; /**< Size we incremented the unix fd counter by */ #endif }; diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 54bceb0d..4533d5a2 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -209,20 +209,19 @@ dbus_message_set_serial (DBusMessage *message, } /** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. - * The link contains a counter with its refcount already - * incremented, but the counter itself not incremented. - * Ownership of link and counter refcount is passed to - * the message. + * Adds a counter to be incremented immediately with the size/unix fds + * of this message, and decremented by the size/unix fds of this + * message when this message if finalized. The link contains a + * counter with its refcount already incremented, but the counter + * itself not incremented. Ownership of link and counter refcount is + * passed to the message. * * @param message the message * @param link link with counter as data */ void -_dbus_message_add_size_counter_link (DBusMessage *message, - DBusList *link) +_dbus_message_add_counter_link (DBusMessage *message, + DBusList *link) { /* right now we don't recompute the delta when message * size changes, and that's OK for current purposes @@ -230,35 +229,43 @@ _dbus_message_add_size_counter_link (DBusMessage *message, * Do recompute it whenever there are no outstanding counters, * since it's basically free. */ - if (message->size_counters == NULL) + if (message->counters == NULL) { message->size_counter_delta = _dbus_string_get_length (&message->header.data) + _dbus_string_get_length (&message->body); +#ifdef HAVE_UNIX_FD_PASSING + message->unix_fd_counter_delta = message->n_unix_fds; +#endif + #if 0 _dbus_verbose ("message has size %ld\n", message->size_counter_delta); #endif } - _dbus_list_append_link (&message->size_counters, link); + _dbus_list_append_link (&message->counters, link); - _dbus_counter_adjust (link->data, message->size_counter_delta); + _dbus_counter_adjust_size (link->data, message->size_counter_delta); + +#ifdef HAVE_UNIX_FD_PASSING + _dbus_counter_adjust_unix_fd (link->data, message->unix_fd_counter_delta); +#endif } /** - * Adds a counter to be incremented immediately with the - * size of this message, and decremented by the size - * of this message when this message if finalized. + * Adds a counter to be incremented immediately with the size/unix fds + * of this message, and decremented by the size/unix fds of this + * message when this message if finalized. * * @param message the message * @param counter the counter * @returns #FALSE if no memory */ dbus_bool_t -_dbus_message_add_size_counter (DBusMessage *message, - DBusCounter *counter) +_dbus_message_add_counter (DBusMessage *message, + DBusCounter *counter) { DBusList *link; @@ -267,38 +274,42 @@ _dbus_message_add_size_counter (DBusMessage *message, return FALSE; _dbus_counter_ref (counter); - _dbus_message_add_size_counter_link (message, link); + _dbus_message_add_counter_link (message, link); return TRUE; } /** - * Removes a counter tracking the size of this message, and decrements - * the counter by the size of this message. + * Removes a counter tracking the size/unix fds of this message, and + * decrements the counter by the size/unix fds of this message. * * @param message the message * @param link_return return the link used * @param counter the counter */ void -_dbus_message_remove_size_counter (DBusMessage *message, - DBusCounter *counter, - DBusList **link_return) +_dbus_message_remove_counter (DBusMessage *message, + DBusCounter *counter, + DBusList **link_return) { DBusList *link; - link = _dbus_list_find_last (&message->size_counters, + link = _dbus_list_find_last (&message->counters, counter); _dbus_assert (link != NULL); - _dbus_list_unlink (&message->size_counters, + _dbus_list_unlink (&message->counters, link); if (link_return) *link_return = link; else _dbus_list_free_link (link); - _dbus_counter_adjust (counter, - message->size_counter_delta); + _dbus_counter_adjust_size (counter, - message->size_counter_delta); + +#ifdef HAVE_UNIX_FD_PASSING + _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); +#endif _dbus_counter_unref (counter); } @@ -515,7 +526,7 @@ dbus_message_get_cached (void) _dbus_assert (message != NULL); _dbus_assert (message->refcount.value == 0); - _dbus_assert (message->size_counters == NULL); + _dbus_assert (message->counters == NULL); _DBUS_UNLOCK (message_cache); @@ -550,13 +561,16 @@ close_unix_fds(int *fds, unsigned *n_fds) #endif static void -free_size_counter (void *element, - void *data) +free_counter (void *element, + void *data) { DBusCounter *counter = element; DBusMessage *message = data; - _dbus_counter_adjust (counter, - message->size_counter_delta); + _dbus_counter_adjust_size (counter, - message->size_counter_delta); +#ifdef HAVE_UNIX_FD_PASSING + _dbus_counter_adjust_unix_fd (counter, - message->unix_fd_counter_delta); +#endif _dbus_counter_unref (counter); } @@ -579,9 +593,9 @@ dbus_message_cache_or_finalize (DBusMessage *message) */ _dbus_data_slot_list_clear (&message->slot_list); - _dbus_list_foreach (&message->size_counters, - free_size_counter, message); - _dbus_list_clear (&message->size_counters); + _dbus_list_foreach (&message->counters, + free_counter, message); + _dbus_list_clear (&message->counters); #ifdef HAVE_UNIX_FD_PASSING close_unix_fds(message->unix_fds, &message->n_unix_fds); @@ -1033,9 +1047,9 @@ dbus_message_finalize (DBusMessage *message) /* This calls application callbacks! */ _dbus_data_slot_list_free (&message->slot_list); - _dbus_list_foreach (&message->size_counters, - free_size_counter, message); - _dbus_list_clear (&message->size_counters); + _dbus_list_foreach (&message->counters, + free_counter, message); + _dbus_list_clear (&message->counters); _dbus_header_free (&message->header); _dbus_string_free (&message->body); @@ -1084,12 +1098,13 @@ dbus_message_new_empty_header (void) #ifndef DBUS_DISABLE_CHECKS message->in_cache = FALSE; #endif - message->size_counters = NULL; + message->counters = NULL; message->size_counter_delta = 0; message->changed_stamp = 0; #ifdef HAVE_UNIX_FD_PASSING message->n_unix_fds = 0; + message->unix_fd_counter_delta = 0; #endif if (!from_cache) diff --git a/dbus/dbus-resources.c b/dbus/dbus-resources.c index 5ff16225..e6dcde1b 100644 --- a/dbus/dbus-resources.c +++ b/dbus/dbus-resources.c @@ -29,7 +29,7 @@ * @brief DBusCounter and other stuff related to resource limits * * Types and functions related to tracking resource limits, - * such as the maximum amount of memory a connection can use + * such as the maximum amount of memory/unix fds a connection can use * for messages, etc. */ @@ -53,9 +53,12 @@ struct DBusCounter { int refcount; /**< reference count */ - long value; /**< current counter value */ + long size_value; /**< current size counter value */ + long unix_fd_value; /**< current unix fd counter value */ + + long notify_size_guard_value; /**< call notify function when crossing this size value */ + long notify_unix_fd_guard_value; /**< call notify function when crossing this unix fd value */ - long notify_guard_value; /**< call notify function when crossing this value */ DBusCounterNotifyFunction notify_function; /**< notify function */ void *notify_data; /**< data for notify function */ }; @@ -83,9 +86,11 @@ _dbus_counter_new (void) return NULL; counter->refcount = 1; - counter->value = 0; + counter->size_value = 0; + counter->unix_fd_value = 0; - counter->notify_guard_value = 0; + counter->notify_size_guard_value = 0; + counter->notify_unix_fd_guard_value = 0; counter->notify_function = NULL; counter->notify_data = NULL; @@ -129,64 +134,109 @@ _dbus_counter_unref (DBusCounter *counter) } /** - * Adjusts the value of the counter by the given + * Adjusts the value of the size counter by the given + * delta which may be positive or negative. + * Calls the notify function from _dbus_counter_set_notify() + * if that function has been specified. + * + * @param counter the counter + * @param delta value to add to the size counter's current value + */ +void +_dbus_counter_adjust_size (DBusCounter *counter, + long delta) +{ + long old = counter->size_value; + + counter->size_value += delta; + +#if 0 + _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n", + old, delta, counter->size_value); +#endif + + if (counter->notify_function != NULL && + ((old < counter->notify_size_guard_value && + counter->size_value >= counter->notify_size_guard_value) || + (old >= counter->notify_size_guard_value && + counter->size_value < counter->notify_size_guard_value))) + (* counter->notify_function) (counter, counter->notify_data); +} + +/** + * Adjusts the value of the unix fd counter by the given * delta which may be positive or negative. * Calls the notify function from _dbus_counter_set_notify() * if that function has been specified. * * @param counter the counter - * @param delta value to add to the counter's current value + * @param delta value to add to the unix fds counter's current value */ void -_dbus_counter_adjust (DBusCounter *counter, - long delta) +_dbus_counter_adjust_unix_fd (DBusCounter *counter, + long delta) { - long old = counter->value; + long old = counter->unix_fd_value; - counter->value += delta; + counter->unix_fd_value += delta; #if 0 _dbus_verbose ("Adjusting counter %ld by %ld = %ld\n", - old, delta, counter->value); + old, delta, counter->unix_fd_value); #endif if (counter->notify_function != NULL && - ((old < counter->notify_guard_value && - counter->value >= counter->notify_guard_value) || - (old >= counter->notify_guard_value && - counter->value < counter->notify_guard_value))) + ((old < counter->notify_unix_fd_guard_value && + counter->unix_fd_value >= counter->notify_unix_fd_guard_value) || + (old >= counter->notify_unix_fd_guard_value && + counter->unix_fd_value < counter->notify_unix_fd_guard_value))) (* counter->notify_function) (counter, counter->notify_data); } /** - * Gets the current value of the counter. + * Gets the current value of the size counter. + * + * @param counter the counter + * @returns its current size value + */ +long +_dbus_counter_get_size_value (DBusCounter *counter) +{ + return counter->size_value; +} + +/** + * Gets the current value of the unix fd counter. * * @param counter the counter - * @returns its current value + * @returns its current unix fd value */ long -_dbus_counter_get_value (DBusCounter *counter) +_dbus_counter_get_unix_fd_value (DBusCounter *counter) { - return counter->value; + return counter->unix_fd_value; } /** * Sets the notify function for this counter; the notify function is - * called whenever the counter's value crosses the guard value in + * called whenever the counter's values cross the guard values in * either direction (moving up, or moving down). * * @param counter the counter - * @param guard_value the value we're notified if the counter crosses + * @param size_guard_value the value we're notified if the size counter crosses + * @param unix_fd_guard_value the value we're notified if the unix fd counter crosses * @param function function to call in order to notify * @param user_data data to pass to the function */ void _dbus_counter_set_notify (DBusCounter *counter, - long guard_value, + long size_guard_value, + long unix_fd_guard_value, DBusCounterNotifyFunction function, void *user_data) { - counter->notify_guard_value = guard_value; + counter->notify_size_guard_value = size_guard_value; + counter->notify_unix_fd_guard_value = unix_fd_guard_value; counter->notify_function = function; counter->notify_data = user_data; } diff --git a/dbus/dbus-resources.h b/dbus/dbus-resources.h index 7b6e0d46..cf3cc1a6 100644 --- a/dbus/dbus-resources.h +++ b/dbus/dbus-resources.h @@ -37,14 +37,19 @@ typedef void (* DBusCounterNotifyFunction) (DBusCounter *counter, DBusCounter* _dbus_counter_new (void); DBusCounter* _dbus_counter_ref (DBusCounter *counter); void _dbus_counter_unref (DBusCounter *counter); -void _dbus_counter_adjust (DBusCounter *counter, - long delta); -long _dbus_counter_get_value (DBusCounter *counter); - -void _dbus_counter_set_notify (DBusCounter *counter, - long guard_value, - DBusCounterNotifyFunction function, - void *user_data); + +void _dbus_counter_adjust_size (DBusCounter *counter, + long delta); +void _dbus_counter_adjust_unix_fd (DBusCounter *counter, + long delta); +long _dbus_counter_get_size_value (DBusCounter *counter); +long _dbus_counter_get_unix_fd_value (DBusCounter *counter); + +void _dbus_counter_set_notify (DBusCounter *counter, + long size_guard_value, + long unix_fd_guard_value, + DBusCounterNotifyFunction function, + void *user_data); DBUS_END_DECLS diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 8c389a6d..50ec6ce0 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -73,10 +73,6 @@ struct DBusTransportVTable /**< Get socket file descriptor */ }; -/** How many unix file descriptors may be queued up before they are - handed off to messages */ -#define DBUS_MAX_QUEUED_FDS 1024 - /** * Object representing a transport such as a socket. * A transport can shuttle messages from point A to point B, @@ -98,9 +94,9 @@ struct DBusTransport DBusCredentials *credentials; /**< Credentials of other end read from the socket */ long max_live_messages_size; /**< Max total size of received messages. */ + long max_live_messages_unix_fds; /**< Max total unix fds of received messages. */ - DBusCounter *live_messages_size; /**< Counter for size of all live messages. */ - + DBusCounter *live_messages; /**< Counter for size/unix fds of all live messages. */ char *address; /**< Address of the server we are connecting to (#NULL for the server side of a transport) */ diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 76699506..8e13baeb 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -191,7 +191,8 @@ check_read_watch (DBusTransport *transport) if (_dbus_transport_get_is_authenticated (transport)) need_read_watch = - _dbus_counter_get_value (transport->live_messages_size) < transport->max_live_messages_size; + (_dbus_counter_get_size_value (transport->live_messages) < transport->max_live_messages_size) && + (_dbus_counter_get_unix_fd_value (transport->live_messages) < transport->max_live_messages_unix_fds); else { if (transport->receive_credentials_pending) diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 7ba01357..11edc6a8 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -57,7 +57,7 @@ */ static void -live_messages_size_notify (DBusCounter *counter, +live_messages_notify (DBusCounter *counter, void *user_data) { DBusTransport *transport = user_data; @@ -65,8 +65,10 @@ live_messages_size_notify (DBusCounter *counter, _dbus_transport_ref (transport); #if 0 - _dbus_verbose ("Counter value is now %d\n", - (int) _dbus_counter_get_value (counter)); + _dbus_verbose ("Size counter value is now %d\n", + (int) _dbus_counter_get_size_value (counter)); + _dbus_verbose ("Unix FD counter value is now %d\n", + (int) _dbus_counter_get_unix_fd_value (counter)); #endif /* disable or re-enable the read watch for the transport if @@ -157,7 +159,7 @@ _dbus_transport_init_base (DBusTransport *transport, transport->vtable = vtable; transport->loader = loader; transport->auth = auth; - transport->live_messages_size = counter; + transport->live_messages = counter; transport->authenticated = FALSE; transport->disconnected = FALSE; transport->is_server = (server_guid != NULL); @@ -180,17 +182,17 @@ _dbus_transport_init_base (DBusTransport *transport, */ transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63; + /* On Linux RLIMIT_NOFILE defaults to 1024, so allowing 4096 fds live + should be more than enough */ + transport->max_live_messages_unix_fds = 4096; + /* credentials read from socket if any */ transport->credentials = creds; -#ifdef HAVE_UNIX_FD_PASSING - transport->unix_fds = NULL; - transport->n_unix_fds = 0; -#endif - - _dbus_counter_set_notify (transport->live_messages_size, + _dbus_counter_set_notify (transport->live_messages, transport->max_live_messages_size, - live_messages_size_notify, + transport->max_live_messages_unix_fds, + live_messages_notify, transport); if (transport->address) @@ -219,9 +221,9 @@ _dbus_transport_finalize_base (DBusTransport *transport) _dbus_message_loader_unref (transport->loader); _dbus_auth_unref (transport->auth); - _dbus_counter_set_notify (transport->live_messages_size, - 0, NULL, NULL); - _dbus_counter_unref (transport->live_messages_size); + _dbus_counter_set_notify (transport->live_messages, + 0, 0, NULL, NULL); + _dbus_counter_unref (transport->live_messages); dbus_free (transport->address); dbus_free (transport->expected_guid); if (transport->credentials) @@ -1078,7 +1080,8 @@ recover_unused_bytes (DBusTransport *transport) DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport) { - if (_dbus_counter_get_value (transport->live_messages_size) >= transport->max_live_messages_size) + if (_dbus_counter_get_size_value (transport->live_messages) >= transport->max_live_messages_size || + _dbus_counter_get_unix_fd_value (transport->live_messages) >= transport->max_live_messages_unix_fds) return DBUS_DISPATCH_COMPLETE; /* complete for now */ if (!_dbus_transport_get_is_authenticated (transport)) @@ -1135,7 +1138,7 @@ _dbus_transport_queue_messages (DBusTransport *transport) _dbus_verbose ("queueing received message %p\n", message); - if (!_dbus_message_add_size_counter (message, transport->live_messages_size)) + if (!_dbus_message_add_counter (message, transport->live_messages)) { _dbus_message_loader_putback_message_link (transport->loader, link); @@ -1172,6 +1175,19 @@ _dbus_transport_set_max_message_size (DBusTransport *transport, _dbus_message_loader_set_max_message_size (transport->loader, size); } +/** + * See dbus_connection_set_max_message_unix_fds(). + * + * @param transport the transport + * @param n the max number of unix fds of a single message + */ +void +_dbus_transport_set_max_message_unix_fds (DBusTransport *transport, + long n) +{ + _dbus_message_loader_set_max_message_unix_fds (transport->loader, n); +} + /** * See dbus_connection_get_max_message_size(). * @@ -1184,6 +1200,18 @@ _dbus_transport_get_max_message_size (DBusTransport *transport) return _dbus_message_loader_get_max_message_size (transport->loader); } +/** + * See dbus_connection_get_max_message_unix_fds(). + * + * @param transport the transport + * @returns max message unix fds + */ +long +_dbus_transport_get_max_message_unix_fds (DBusTransport *transport) +{ + return _dbus_message_loader_get_max_message_unix_fds (transport->loader); +} + /** * See dbus_connection_set_max_received_size(). * @@ -1195,12 +1223,30 @@ _dbus_transport_set_max_received_size (DBusTransport *transport, long size) { transport->max_live_messages_size = size; - _dbus_counter_set_notify (transport->live_messages_size, + _dbus_counter_set_notify (transport->live_messages, transport->max_live_messages_size, - live_messages_size_notify, + transport->max_live_messages_unix_fds, + live_messages_notify, transport); } +/** + * See dbus_connection_set_max_received_unix_fds(). + * + * @param transport the transport + * @param n the max unix fds of all incoming messages + */ +void +_dbus_transport_set_max_received_unix_fds (DBusTransport *transport, + long n) +{ + transport->max_live_messages_unix_fds = n; + _dbus_counter_set_notify (transport->live_messages, + transport->max_live_messages_size, + transport->max_live_messages_unix_fds, + live_messages_notify, + transport); +} /** * See dbus_connection_get_max_received_size(). @@ -1214,6 +1260,18 @@ _dbus_transport_get_max_received_size (DBusTransport *transport) return transport->max_live_messages_size; } +/** + * See dbus_connection_set_max_received_unix_fds(). + * + * @param transport the transport + * @returns max unix fds for all live messages + */ +long +_dbus_transport_get_max_received_unix_fds (DBusTransport *transport) +{ + return transport->max_live_messages_unix_fds; +} + /** * See dbus_connection_get_unix_user(). * diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 4554faf3..994bf6e3 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -54,12 +54,21 @@ void _dbus_transport_do_iteration (DBusTransport int timeout_milliseconds); DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); + void _dbus_transport_set_max_message_size (DBusTransport *transport, long size); long _dbus_transport_get_max_message_size (DBusTransport *transport); void _dbus_transport_set_max_received_size (DBusTransport *transport, long size); long _dbus_transport_get_max_received_size (DBusTransport *transport); + +void _dbus_transport_set_max_message_unix_fds (DBusTransport *transport, + long n); +long _dbus_transport_get_max_message_unix_fds (DBusTransport *transport); +void _dbus_transport_set_max_received_unix_fds(DBusTransport *transport, + long n); +long _dbus_transport_get_max_received_unix_fds(DBusTransport *transport); + dbus_bool_t _dbus_transport_get_socket_fd (DBusTransport *transport, int *fd_p); dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport, -- cgit From 64ad8449679c53fefd20baea88fa593f226d59b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 May 2009 01:33:17 +0200 Subject: bus: make use of new unix fd limits Create configuration settings and enforce message unix fd limits the same way we do for allocated message memory. --- bus/bus.c | 10 ++++++++-- bus/bus.h | 3 +++ bus/config-parser.c | 27 +++++++++++++++++++++++++++ bus/dbus-daemon.1.in | 5 +++++ bus/session.conf.in | 3 +++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/bus/bus.c b/bus/bus.c index 1412ea28..129b2e6c 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -190,6 +190,12 @@ new_connection_callback (DBusServer *server, dbus_connection_set_max_message_size (new_connection, context->limits.max_message_size); + + dbus_connection_set_max_received_unix_fds (new_connection, + context->limits.max_incoming_unix_fds); + + dbus_connection_set_max_message_unix_fds (new_connection, + context->limits.max_message_unix_fds); dbus_connection_set_allow_anonymous (new_connection, context->allow_anonymous); @@ -1471,8 +1477,8 @@ bus_context_check_security_policy (BusContext *context, /* See if limits on size have been exceeded */ if (proposed_recipient && - dbus_connection_get_outgoing_size (proposed_recipient) > - context->limits.max_outgoing_bytes) + ((dbus_connection_get_outgoing_size (proposed_recipient) > context->limits.max_outgoing_bytes) || + (dbus_connection_get_outgoing_unix_fds (proposed_recipient) > context->limits.max_outgoing_unix_fds))) { dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, "The destination service \"%s\" has a full message queue", diff --git a/bus/bus.h b/bus/bus.h index 74bdb821..aba17043 100644 --- a/bus/bus.h +++ b/bus/bus.h @@ -47,8 +47,11 @@ typedef struct BusMatchRule BusMatchRule; typedef struct { long max_incoming_bytes; /**< How many incoming message bytes for a single connection */ + long max_incoming_unix_fds; /**< How many incoming message unix fds for a single connection */ long max_outgoing_bytes; /**< How many outgoing bytes can be queued for a single connection */ + long max_outgoing_unix_fds; /**< How many outgoing unix fds can be queued for a single connection */ long max_message_size; /**< Max size of a single message in bytes */ + long max_message_unix_fds; /**< Max number of unix fds of a single message*/ int activation_timeout; /**< How long to wait for an activation to time out */ int auth_timeout; /**< How long to wait for an authentication to time out */ int max_completed_connections; /**< Max number of authorized connections */ diff --git a/bus/config-parser.c b/bus/config-parser.c index c3e8fba1..784c8315 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -404,6 +404,15 @@ bus_config_parser_new (const DBusString *basedir, parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 127; parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 127; parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32; + + /* We set relatively conservative values here since due to the + way SCM_RIGHTS works we need to preallocate an array for the + maximum number of file descriptors we can receive. Picking a + high value here thus translates directly to more memory + allocation. */ + parser->limits.max_incoming_unix_fds = 1024*4; + parser->limits.max_outgoing_unix_fds = 1024*4; + parser->limits.max_message_unix_fds = 1024; /* Making this long means the user has to wait longer for an error * message if something screws up, but making it too short means @@ -1828,16 +1837,31 @@ set_limit (BusConfigParser *parser, must_be_positive = TRUE; parser->limits.max_incoming_bytes = value; } + else if (strcmp (name, "max_incoming_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_incoming_unix_fds = value; + } else if (strcmp (name, "max_outgoing_bytes") == 0) { must_be_positive = TRUE; parser->limits.max_outgoing_bytes = value; } + else if (strcmp (name, "max_outgoing_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_outgoing_unix_fds = value; + } else if (strcmp (name, "max_message_size") == 0) { must_be_positive = TRUE; parser->limits.max_message_size = value; } + else if (strcmp (name, "max_message_unix_fds") == 0) + { + must_be_positive = TRUE; + parser->limits.max_message_unix_fds = value; + } else if (strcmp (name, "service_start_timeout") == 0) { must_be_positive = TRUE; @@ -2979,8 +3003,11 @@ limits_equal (const BusLimits *a, { return (a->max_incoming_bytes == b->max_incoming_bytes + || a->max_incoming_unix_fds == b->max_incoming_unix_fds || a->max_outgoing_bytes == b->max_outgoing_bytes + || a->max_outgoing_unix_fds == b->max_outgoing_unix_fds || a->max_message_size == b->max_message_size + || a->max_message_unix_fds == b->max_message_unix_fds || a->activation_timeout == b->activation_timeout || a->auth_timeout == b->auth_timeout || a->max_completed_connections == b->max_completed_connections diff --git a/bus/dbus-daemon.1.in b/bus/dbus-daemon.1.in index 4b55ac29..8d518136 100644 --- a/bus/dbus-daemon.1.in +++ b/bus/dbus-daemon.1.in @@ -365,10 +365,15 @@ Available limit names are: .nf "max_incoming_bytes" : total size in bytes of messages incoming from a single connection + "max_incoming_unix_fds" : total number of unix fds of messages + incoming from a single connection "max_outgoing_bytes" : total size in bytes of messages queued up for a single connection + "max_outgoing_unix_fds" : total number of unix fds of messages + queued up for a single connection "max_message_size" : max size of a single message in bytes + "max_message_unix_fds" : max unix fds of a single message "service_start_timeout" : milliseconds (thousandths) until a started service has to connect "auth_timeout" : milliseconds (thousandths) a diff --git a/bus/session.conf.in b/bus/session.conf.in index 794eb8da..aed320eb 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -45,8 +45,11 @@ 1000000000 + 250000000 1000000000 + 250000000 1000000000 + 4096 120000 240000 100000 -- cgit From 869291ea5a98485183bbe1a4d2cdab9eb97fbb0d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 May 2009 03:01:37 +0200 Subject: doc: document Unix FD passing in the specification --- doc/dbus-specification.xml | 141 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 125 insertions(+), 16 deletions(-) diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index 9b22c84a..8d10aade 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -415,6 +415,10 @@ DICT_ENTRY 101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}') Entry in a dict or map (array of key-value pairs) + + UNIX_FD + 104 (ASCII 'h') + Unix file descriptor @@ -579,6 +583,15 @@ 8 + + UNIX_FD + 32-bit unsigned integer in the message's byte + order. The actual file descriptors need to be + transferred out-of-band via some platform specific + mechanism. On the wire, values of this type store the index to the + file descriptor in the array of file descriptors that + accompany the message. + 4 @@ -999,6 +1012,22 @@ If omitted, it is assumed to be the empty signature "" (i.e. the body must be 0-length). + + UNIX_FDS + 9 + UINT32 + optional + The number of Unix file descriptors that + accompany the message. If omitted, it is assumed + that no Unix file descriptors accompany the + message. The actual file descriptors need to be + transferred via platform specific mechanism + out-of-band. They must be sent at the same time as + part of the message itself. They may not be sent + before the first byte of the message itself is + transferred or after the last byte of the message + itself. + @@ -1410,6 +1439,7 @@ BEGIN DATA <data in hex encoding> ERROR [human-readable error explanation] + NEGOTIATE_UNIX_FD From server to client are as follows: @@ -1419,6 +1449,7 @@ OK <GUID in hex> DATA <data in hex encoding> ERROR + AGREE_UNIX_FD @@ -1479,16 +1510,17 @@ If authentication succeeds after exchanging DATA commands, an OK command must be sent to the client. - - The first octet received by the client after the \r\n of the OK - command must be the first octet of the authenticated/encrypted - stream of D-Bus messages. - The first octet received by the server after the \r\n of the BEGIN command from the client must be the first octet of the authenticated/encrypted stream of D-Bus messages. + + If BEGIN is received by the server, the first octet received + by the client after the \r\n of the OK command must be the + first octet of the authenticated/encrypted stream of D-Bus + messages. + CANCEL Command @@ -1542,20 +1574,24 @@ OK Command - The OK command indicates that the client has been authenticated, - and that further communication will be a stream of D-Bus messages - (optionally encrypted, as negotiated) rather than this protocol. + The OK command indicates that the client has been + authenticated. The client may now proceed with negotiating + Unix file descriptor passing. To do that it shall send + NEGOTIATE_UNIX_FD to the server. - The first octet received by the client after the \r\n of the OK - command must be the first octet of the authenticated/encrypted - stream of D-Bus messages. + Otherwise, the client must respond to the OK command by + sending a BEGIN command, followed by its stream of messages, + or by disconnecting. The server must not accept additional + commands using this protocol after the BEGIN command has been + received. Further communication will be a stream of D-Bus + messages (optionally encrypted, as negotiated) rather than + this protocol. - The client must respond to the OK command by sending a BEGIN - command, followed by its stream of messages, or by disconnecting. - The server must not accept additional commands using this protocol - after the OK command has been sent. + If a client sends BEGIN the first octet received by the client + after the \r\n of the OK command must be the first octet of + the authenticated/encrypted stream of D-Bus messages. The OK command has one argument, which is the GUID of the server. @@ -1589,6 +1625,56 @@ negotiate extensions or changes to the D-Bus protocol in the future. + + NEGOTIATE_UNIX_FD Command + + The NEGOTIATE_UNIX_FD command indicates that the client + supports Unix file descriptor passing. This command may only + be sent after the connection is authenticated, i.e. after OK + was received by the client. This command may only be sent on + transports that support Unix file descriptor passing. + + + On receiving NEGOTIATE_UNIX_FD the server must respond with + either AGREE_UNIX_FD or ERROR. It shall respond the former if + the transport chosen supports Unix file descriptor passing and + the server supports this feature. It shall respond the latter + if the transport does not support Unix file descriptor + passing, the server does not support this feature, or the + server decides not to enable file descriptor passing due to + security or other reasons. + + + + AGREE_UNIX_FD Command + + The AGREE_UNIX_FD command indicates that the server supports + Unix file descriptor passing. This command may only be sent + after the connection is authenticated, and the client sent + NEGOTIATE_UNIX_FD to enable Unix file descriptor passing. This + command may only be sent on transports that support Unix file + descriptor passing. + + + On receiving AGREE_UNIX_FD the client must respond with BEGIN, + followed by its stream of messages, or by disconnecting. The + server must not accept additional commands using this protocol + after the BEGIN command has been received. Further + communication will be a stream of D-Bus messages (optionally + encrypted, as negotiated) rather than this protocol. + + + + Future Extensions + + Future extensions to the authentication and negotiation + protocol are possible. For that new commands may be + introduced. If a client or server receives an unknown command + it shall respond with ERROR and not consider this fatal. New + commands may be introduced both before, and after + authentication, i.e. both before and after the OK command. + + Authentication examples @@ -1669,6 +1755,30 @@ C: BEGIN +
+ Example of successful magic cookie authentication with successful negotiation of Unix FD passing + + (MAGIC_COOKIE is a made up mechanism) + + C: AUTH MAGIC_COOKIE 3138363935333137393635383634 + S: OK 1234deadbeef + C: NEGOTIATE_UNIX_FD + S: AGREE_UNIX_FD + C: BEGIN + +
+
+ Example of successful magic cookie authentication with unsuccessful negotiation of Unix FD passing + + (MAGIC_COOKIE is a made up mechanism) + + C: AUTH MAGIC_COOKIE 3138363935333137393635383634 + S: OK 1234deadbeef + C: NEGOTIATE_UNIX_FD + S: ERROR + C: BEGIN + +
@@ -4078,4 +4188,3 @@ - -- cgit From 724adb2f61bd8a6ea41932e04df9303d0a1eed18 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 May 2009 23:48:46 +0200 Subject: doxygen: document that we don't support reading/writing arrays of unix fds in once piece right now --- dbus/dbus-message.c | 93 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 4533d5a2..d9ea649e 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -707,10 +707,6 @@ _dbus_message_iter_check (DBusMessageRealIter *iter) * dbus_message_get_args() is the place to go for complete * documentation. * - * Unix file descriptors that are read with this function will have - * the FD_CLOEXEC flag set. If you need them without this flag set, - * make sure to unset it with fcntl(). - * * @todo This may leak memory and file descriptors if parsing fails. See #21259 * * @see dbus_message_get_args @@ -1582,9 +1578,10 @@ dbus_message_get_type (DBusMessage *message) * Appends fields to a message given a variable argument list. The * variable argument list should contain the type of each argument * followed by the value to append. Appendable types are basic types, - * and arrays of fixed-length basic types. To append variable-length - * basic types, or any more complex value, you have to use an iterator - * rather than this function. + * and arrays of fixed-length basic types (except arrays of Unix file + * descriptors). To append variable-length basic types, or any more + * complex value, you have to use an iterator rather than this + * function. * * To append a basic type, specify its type code followed by the * address of the value. For example: @@ -1599,18 +1596,22 @@ dbus_message_get_type (DBusMessage *message) * DBUS_TYPE_INVALID); * @endcode * - * To append an array of fixed-length basic types, pass in the - * DBUS_TYPE_ARRAY typecode, the element typecode, the address of - * the array pointer, and a 32-bit integer giving the number of - * elements in the array. So for example: - * @code - * const dbus_int32_t array[] = { 1, 2, 3 }; - * const dbus_int32_t *v_ARRAY = array; - * dbus_message_append_args (message, - * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, - * DBUS_TYPE_INVALID); + * To append an array of fixed-length basic types (except Unix file + * descriptors), pass in the DBUS_TYPE_ARRAY typecode, the element + * typecode, the address of the array pointer, and a 32-bit integer + * giving the number of elements in the array. So for example: @code + * const dbus_int32_t array[] = { 1, 2, 3 }; const dbus_int32_t + * *v_ARRAY = array; dbus_message_append_args (message, + * DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, &v_ARRAY, 3, DBUS_TYPE_INVALID); * @endcode * + * This function does not support arrays of Unix file descriptors. If + * you need those you need to manually recurse into the array. + * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + * * @warning in C, given "int array[]", "&array == array" (the * comp.lang.c FAQ says otherwise, but gcc and the FAQ don't agree). * So if you're using an array instead of a pointer you have to create @@ -1789,7 +1790,16 @@ dbus_message_append_args_valist (DBusMessage *message, * signature are supported; but these are returned as allocated memory * and must be freed with dbus_free_string_array(), while the other * types are returned as const references. To get a string array - * pass in "char ***array_location" and "int *n_elements" + * pass in "char ***array_location" and "int *n_elements". + * + * Similar to dbus_message_get_fixed_array() this function does not + * support arrays of type DBUS_TYPE_UNIX_FD. If you need to parse + * messages with arrays of Unix file descriptors you need to recurse + * into the array manually. + * + * Unix file descriptors that are read with this function will have + * the FD_CLOEXEC flag set. If you need them without this flag set, + * make sure to unset it with fcntl(). * * The variable argument list should contain the type of the argument * followed by a pointer to where the value should be stored. The list @@ -2014,10 +2024,10 @@ dbus_message_iter_get_element_type (DBusMessageIter *iter) * you won't be able to recurse further. There's no array of int32 to * recurse into. * - * If a container is an array of fixed-length types, it is much more - * efficient to use dbus_message_iter_get_fixed_array() to get the - * whole array in one shot, rather than individually walking over the - * array elements. + * If a container is an array of fixed-length types (except Unix file + * descriptors), it is much more efficient to use + * dbus_message_iter_get_fixed_array() to get the whole array in one + * shot, rather than individually walking over the array elements. * * Be sure you have somehow checked that * dbus_message_iter_get_arg_type() matches the type you are expecting @@ -2087,17 +2097,24 @@ dbus_message_iter_get_signature (DBusMessageIter *iter) * and for string a "const char**". The returned value is * by reference and should not be freed. * + * This call duplicates Unix file descriptors when reading them. It is + * your job to close them when you don't need them anymore. + * + * Unix file descriptors that are read with this function will have + * the FD_CLOEXEC flag set. If you need them without this flag set, + * make sure to unset it with fcntl(). + * * Be sure you have somehow checked that * dbus_message_iter_get_arg_type() matches the type you are * expecting, or you'll crash when you try to use an integer as a * string or something. * - * To read any container type (array, struct, dict) you will need - * to recurse into the container with dbus_message_iter_recurse(). - * If the container is an array of fixed-length values, you can - * get all the array elements at once with - * dbus_message_iter_get_fixed_array(). Otherwise, you have to - * iterate over the container's contents one value at a time. + * To read any container type (array, struct, dict) you will need to + * recurse into the container with dbus_message_iter_recurse(). If + * the container is an array of fixed-length values (except Unix file + * descriptors), you can get all the array elements at once with + * dbus_message_iter_get_fixed_array(). Otherwise, you have to iterate + * over the container's contents one value at a time. * * All basic-typed values are guaranteed to fit in 8 bytes. So you can * write code like this: @@ -2187,6 +2204,10 @@ dbus_message_iter_get_array_len (DBusMessageIter *iter) * such as integers, bool, double. The returned block will be from the * current position in the array until the end of the array. * + * There is one exception here: although DBUS_TYPE_UNIX_FD is + * considered a 'fixed' type arrays of this type may not be read with + * this function. + * * The message iter should be "in" the array (that is, you recurse into the * array, and then you call dbus_message_iter_get_fixed_array() on the * "sub-iterator" created by dbus_message_iter_recurse()). @@ -2434,6 +2455,10 @@ expand_fd_array(DBusMessage *m, * The "value" argument should be the address of a basic-typed value. * So for string, const char**. For integer, dbus_int32_t*. * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + * * @todo If this fails due to lack of memory, the message is hosed and * you have to start over building the whole message. * @@ -2512,10 +2537,10 @@ dbus_message_iter_append_basic (DBusMessageIter *iter, /** * Appends a block of fixed-length values to an array. The * fixed-length types are all basic types that are not string-like. So - * int32, double, bool, etc. You must call - * dbus_message_iter_open_container() to open an array of values - * before calling this function. You may call this function multiple - * times (and intermixed with calls to + * int32, double, bool, etc. (Unix file descriptors however are not + * supported.) You must call dbus_message_iter_open_container() to + * open an array of values before calling this function. You may call + * this function multiple times (and intermixed with calls to * dbus_message_iter_append_basic()) for the same array. * * The "value" argument should be the address of the array. So for @@ -2538,6 +2563,10 @@ dbus_message_iter_append_basic (DBusMessageIter *iter, * @todo If this fails due to lack of memory, the message is hosed and * you have to start over building the whole message. * + * For Unix file descriptors this function will internally duplicate + * the descriptor you passed in. Hence you may close the descriptor + * immediately after this call. + * * @param iter the append iterator * @param element_type the type of the array elements * @param value the address of the array -- cgit From 89f70b19495e9c3dadbbb7b51ce1629fa22ea3af Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 May 2009 01:00:52 +0200 Subject: bus: don't forward messages with unix fds on connections that don't support it This simply verifies that we forward unix fds only on connection that support it. We willr eturn an error if a client attempts to send a message with unix fds to another client that cannot do it. --- bus/dispatch.c | 14 ++++++++++++++ dbus/dbus-message-internal.h | 2 +- dbus/dbus-message.c | 14 ++++++++++++++ dbus/dbus-message.h | 1 + 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bus/dispatch.c b/bus/dispatch.c index 8ed88dad..fa5874d2 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -56,6 +56,10 @@ send_one_message (DBusConnection *connection, message, NULL)) return TRUE; /* silently don't send it */ + + if (dbus_message_contains_unix_fds(message) && + !dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD)) + return TRUE; /* silently don't send it */ if (!bus_transaction_send (transaction, connection, @@ -300,6 +304,16 @@ bus_dispatch (DBusConnection *connection, addressed_recipient, message, &error)) goto out; + + if (dbus_message_contains_unix_fds(message) && + !dbus_connection_can_send_type(addressed_recipient, DBUS_TYPE_UNIX_FD)) + { + dbus_set_error(&error, + DBUS_ERROR_NOT_SUPPORTED, + "Tried to send message with Unix file descriptors" + "to a client that doesn't support that."); + goto out; + } /* Dispatch the message */ if (!bus_transaction_send (transaction, addressed_recipient, message)) diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index 0565d611..4302f091 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -34,7 +34,7 @@ typedef struct DBusMessageLoader DBusMessageLoader; void _dbus_message_get_network_data (DBusMessage *message, const DBusString **header, const DBusString **body); -void _dbus_message_get_unix_fds (DBusMessage *messgage, +void _dbus_message_get_unix_fds (DBusMessage *message, const int **fds, unsigned *n_fds); diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index d9ea649e..d8d746a6 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -3560,6 +3560,20 @@ dbus_set_error_from_message (DBusError *error, return TRUE; } +/** + * Checks whether a message contains unix fds + * + * @param message the message + * @returns #TRUE if the message contains unix fds + */ +dbus_bool_t +dbus_message_contains_unix_fds(DBusMessage *message) +{ + _dbus_assert(message); + + return message->n_unix_fds > 0; +} + /** @} */ /** diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h index 2e29fef0..3a3ddd96 100644 --- a/dbus/dbus-message.h +++ b/dbus/dbus-message.h @@ -159,6 +159,7 @@ dbus_bool_t dbus_message_get_args_valist (DBusMessage *message, int first_arg_type, va_list var_args); +dbus_bool_t dbus_message_contains_unix_fds (DBusMessage *message); dbus_bool_t dbus_message_iter_init (DBusMessage *message, DBusMessageIter *iter); -- cgit From 9f06daccce3f4e75cfac7c97bfb1743affb55cb2 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 16 Jul 2009 15:17:25 +0200 Subject: Fix detection of the GCC __sync intrinsics. We have to use the return value, otherwise GCC optimises the code by using instructions that don't return anything. That won't match what we actually use in dbus-sysdeps-unix.c --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 036679ce..2a0e64e2 100644 --- a/configure.in +++ b/configure.in @@ -595,7 +595,7 @@ fi AC_CACHE_CHECK([whether $CC knows __sync_sub_and_fetch()], dbus_cv_sync_sub_and_fetch, [AC_LINK_IFELSE( - AC_LANG_PROGRAM([], [[int a = 4; __sync_sub_and_fetch(&a, 4);]]), + AC_LANG_PROGRAM([], [[int a = 4; int b = __sync_sub_and_fetch(&a, 4);]]), [dbus_cv_sync_sub_and_fetch=yes], [dbus_cv_sync_sub_and_fetch=no]) ]) -- cgit