X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-sysdeps-unix.c;h=1cb4a58be902986452d1f2fe9bc85ad7b05a2b99;hb=7d9239c9c78cb6d0b9c282376fcf3cda1de23209;hp=d10d48ba1cf6f2f215193ff4ce460ef8e547d766;hpb=6478ec6949c6bb794237b43d03b68f80eba1288c;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index d10d48b..1cb4a58 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1,11 +1,11 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-sysdeps-unix.c Wrappers around UNIX system/libc features (internal to D-Bus implementation) - * + * * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. * Copyright (C) 2003 CodeFactory AB * * Licensed under the Academic Free License version 2.1 - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,14 +15,14 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ -#define _GNU_SOURCE +#include #include "dbus-internals.h" #include "dbus-sysdeps.h" @@ -34,6 +34,7 @@ #include "dbus-userdb.h" #include "dbus-list.h" #include "dbus-credentials.h" +#include "dbus-nonce.h" #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #ifdef HAVE_ERRNO_H #include @@ -70,11 +72,20 @@ #ifdef HAVE_GETPEERUCRED #include #endif +#ifdef HAVE_ALLOCA_H +#include +#endif #ifdef HAVE_ADT #include #endif +#include "sd-daemon.h" + +#if !DBUS_USE_SYNC +#include +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -87,6 +98,38 @@ #define socklen_t int #endif +#if defined (__sun) || defined (__sun__) +/* + * CMS_SPACE etc. definitions for Solaris < 10, based on + * http://mailman.videolan.org/pipermail/vlc-devel/2006-May/024402.html + * via + * http://wiki.opencsw.org/porting-faq#toc10 + * + * These are only redefined for Solaris, for now: if your OS needs these too, + * please file a bug. (Or preferably, improve your OS so they're not needed.) + */ + +# ifndef CMSG_ALIGN +# ifdef __sun__ +# define CMSG_ALIGN(len) _CMSG_DATA_ALIGN (len) +# else + /* aligning to sizeof (long) is assumed to be portable (fd.o#40235) */ +# define CMSG_ALIGN(len) (((len) + sizeof (long) - 1) & \ + ~(sizeof (long) - 1)) +# endif +# endif + +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + \ + CMSG_ALIGN (len)) +# endif + +# ifndef CMSG_LEN +# define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +# endif + +#endif /* Solaris */ + static dbus_bool_t _dbus_open_socket (int *fd_p, int domain, @@ -94,9 +137,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; } @@ -110,21 +172,17 @@ _dbus_open_socket (int *fd_p, } } -dbus_bool_t -_dbus_open_tcp_socket (int *fd, - DBusError *error) -{ - return _dbus_open_socket(fd, AF_INET, SOCK_STREAM, 0, error); -} - /** * 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 */ -dbus_bool_t +static dbus_bool_t _dbus_open_unix_socket (int *fd, DBusError *error) { @@ -139,7 +197,7 @@ _dbus_open_unix_socket (int *fd, * @param error return location for an error * @returns #FALSE if error is set */ -dbus_bool_t +dbus_bool_t _dbus_close_socket (int fd, DBusError *error) { @@ -179,64 +237,265 @@ _dbus_write_socket (int fd, int start, int len) { +#if HAVE_DECL_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 } /** - * write data to a pipe. + * 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 pipe the pipe instance - * @param buffer the buffer to write data from - * @param start the first byte in the buffer to write - * @param len the number of bytes to try to write - * @param error error return - * @returns the number of bytes written or -1 on error + * @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_pipe_write (DBusPipe *pipe, - const DBusString *buffer, - int start, - int len, - DBusError *error) -{ - int written; - - written = _dbus_write (pipe->fd_or_handle, buffer, start, len); - if (written < 0) +_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)) { - dbus_set_error (error, DBUS_ERROR_FAILED, - "Writing to pipe: %s\n", - _dbus_strerror (errno)); + 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; } - return written; +#endif } -/** - * close a pipe. - * - * @param pipe the pipe instance - * @param error return location for an error - * @returns #FALSE if error is set - */ int -_dbus_pipe_close (DBusPipe *pipe, - DBusError *error) -{ - if (_dbus_close (pipe->fd_or_handle, error) < 0) +_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) { - return -1; + iov[1].iov_base = (char*) _dbus_string_get_const_data_len (buffer2, start2, len2); + iov[1].iov_len = len2; } - else + + _DBUS_ZERO(m); + m.msg_iov = iov; + m.msg_iovlen = buffer2 ? 2 : 1; + + if (n_fds > 0) { - _dbus_pipe_invalidate (pipe); - return 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 +#if HAVE_DECL_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 } /** * Like _dbus_write_two() but only works on sockets and is thus * available on Windows. - * + * * @param fd the file descriptor * @param buffer1 first buffer * @param start1 first byte to write in first buffer @@ -255,10 +514,59 @@ _dbus_write_socket_two (int fd, int start2, int len2) { +#if HAVE_DECL_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; + + _DBUS_ZERO(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 } +dbus_bool_t +_dbus_socket_is_invalid (int fd) +{ + return fd < 0 ? TRUE : FALSE; +} /** * Thin wrapper around the read() system call that appends @@ -270,7 +578,7 @@ _dbus_write_socket_two (int fd, * * Unlike _dbus_read_socket(), _dbus_read() is not available * on Windows. - * + * * @param fd the file descriptor to read from * @param buffer the buffer to append data to * @param count the amount of data to read @@ -286,7 +594,7 @@ _dbus_read (int fd, char *data; _dbus_assert (count >= 0); - + start = _dbus_string_get_length (buffer); if (!_dbus_string_lengthen (buffer, count)) @@ -298,7 +606,7 @@ _dbus_read (int fd, data = _dbus_string_get_data_len (buffer, start, count); again: - + bytes_read = read (fd, data, count); if (bytes_read < 0) @@ -321,7 +629,7 @@ _dbus_read (int fd, if (bytes_read > 0) _dbus_verbose_bytes_of_string (buffer, start, bytes_read); #endif - + return bytes_read; } } @@ -329,7 +637,7 @@ _dbus_read (int fd, /** * Thin wrapper around the write() system call that writes a part of a * DBusString and handles EINTR for you. - * + * * @param fd the file descriptor to write * @param buffer the buffer to write data from * @param start the first byte in the buffer to write @@ -344,9 +652,9 @@ _dbus_write (int fd, { const char *data; int bytes_written; - + data = _dbus_string_get_const_data_len (buffer, start, len); - + again: bytes_written = write (fd, data, len); @@ -358,7 +666,7 @@ _dbus_write (int fd, if (bytes_written > 0) _dbus_verbose_bytes_of_string (buffer, start, bytes_written); #endif - + return bytes_written; } @@ -396,7 +704,7 @@ _dbus_write_two (int fd, _dbus_assert (start2 >= 0); _dbus_assert (len1 >= 0); _dbus_assert (len2 >= 0); - + #ifdef HAVE_WRITEV { struct iovec vectors[2]; @@ -414,40 +722,40 @@ _dbus_write_two (int fd, 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; again: - + bytes_written = writev (fd, vectors, data2 ? 2 : 1); if (bytes_written < 0 && errno == EINTR) goto again; - + return bytes_written; } #else /* HAVE_WRITEV */ { int ret1; - + ret1 = _dbus_write (fd, buffer1, start1, len1); if (ret1 == len1 && buffer2 != NULL) { ret2 = _dbus_write (fd, buffer2, start2, len2); if (ret2 < 0) ret2 = 0; /* we can't report an error as the first write was OK */ - + return ret1 + ret2; } else return ret1; } -#endif /* !HAVE_WRITEV */ +#endif /* !HAVE_WRITEV */ } #define _DBUS_MAX_SUN_PATH_LENGTH 99 @@ -469,11 +777,13 @@ _dbus_write_two (int fd, * Creates a socket and connects it to the UNIX domain socket at the * given path. The connection fd is returned, and is set up as * nonblocking. - * + * * Uses abstract sockets instead of filesystem-linked sockets if * requested (it's possible only on Linux; see "man 7 unix" on Linux). * On non-Linux abstract socket usage always fails. * + * 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 @@ -486,14 +796,14 @@ _dbus_connect_unix_socket (const char *path, { int fd; size_t path_len; - struct sockaddr_un addr; + struct sockaddr_un addr; _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_verbose ("connecting to unix socket %s abstract=%d\n", path, abstract); - - + + if (!_dbus_open_unix_socket (&fd, error)) { _DBUS_ASSERT_ERROR_IS_SET(error); @@ -518,7 +828,7 @@ _dbus_connect_unix_socket (const char *path, _dbus_close (fd, NULL); return -1; } - + strncpy (&addr.sun_path[1], path, path_len); /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ #else /* HAVE_ABSTRACT_SOCKETS */ @@ -540,27 +850,23 @@ _dbus_connect_unix_socket (const char *path, strncpy (addr.sun_path, path, path_len); } - + if (connect (fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) - { + { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to connect to socket %s: %s", path, _dbus_strerror (errno)); _dbus_close (fd, NULL); - fd = -1; - return -1; } if (!_dbus_set_fd_nonblocking (fd, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); - - _dbus_close (fd, NULL); - fd = -1; + _dbus_close (fd, NULL); return -1; } @@ -568,37 +874,127 @@ _dbus_connect_unix_socket (const char *path, } /** - * Enables or disables the reception of credentials on the given socket during - * the next message transmission. This is only effective if the #LOCAL_CREDS - * system feature exists, in which case the other side of the connection does - * not have to do anything special to send the credentials. + * Creates a UNIX domain socket and connects it to the specified + * process to execute. * - * @param fd socket on which to change the #LOCAL_CREDS flag. - * @param on whether to enable or disable the #LOCAL_CREDS flag. + * This will set FD_CLOEXEC for the socket returned. + * + * @param path the path to the executable + * @param argv the argument list for the process to execute. + * argv[0] typically is identical to the path of the executable + * @param error return location for error code + * @returns connection file descriptor or -1 on error */ -static dbus_bool_t -_dbus_set_local_creds (int fd, dbus_bool_t on) +int +_dbus_connect_exec (const char *path, + char *const argv[], + DBusError *error) { - dbus_bool_t retval = TRUE; + int fds[2]; + pid_t pid; -#if defined(HAVE_CMSGCRED) - /* NOOP just to make sure only one codepath is used - * and to prefer CMSGCRED - */ -#elif defined(LOCAL_CREDS) - int val = on ? 1 : 0; - if (setsockopt (fd, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("connecting to process %s\n", path); + + if (socketpair (AF_UNIX, SOCK_STREAM +#ifdef SOCK_CLOEXEC + |SOCK_CLOEXEC +#endif + , 0, fds) < 0) { - _dbus_verbose ("Unable to set LOCAL_CREDS socket option on fd %d\n", fd); - retval = FALSE; + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to create socket pair: %s", + _dbus_strerror (errno)); + return -1; } - else - _dbus_verbose ("LOCAL_CREDS %s for further messages on fd %d\n", - on ? "enabled" : "disabled", fd); -#endif - return retval; -} + _dbus_fd_set_close_on_exec (fds[0]); + _dbus_fd_set_close_on_exec (fds[1]); + + pid = fork (); + if (pid < 0) + { + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to fork() to call %s: %s", + path, _dbus_strerror (errno)); + close (fds[0]); + close (fds[1]); + return -1; + } + + if (pid == 0) + { + /* child */ + close (fds[0]); + + dup2 (fds[1], STDIN_FILENO); + dup2 (fds[1], STDOUT_FILENO); + + if (fds[1] != STDIN_FILENO && + fds[1] != STDOUT_FILENO) + close (fds[1]); + + /* Inherit STDERR and the controlling terminal from the + parent */ + + _dbus_close_all (); + + execvp (path, argv); + + fprintf (stderr, "Failed to execute process %s: %s\n", path, _dbus_strerror (errno)); + + _exit(1); + } + + /* parent */ + close (fds[1]); + + if (!_dbus_set_fd_nonblocking (fds[0], error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + + close (fds[0]); + return -1; + } + + return fds[0]; +} + +/** + * Enables or disables the reception of credentials on the given socket during + * the next message transmission. This is only effective if the #LOCAL_CREDS + * system feature exists, in which case the other side of the connection does + * not have to do anything special to send the credentials. + * + * @param fd socket on which to change the #LOCAL_CREDS flag. + * @param on whether to enable or disable the #LOCAL_CREDS flag. + */ +static dbus_bool_t +_dbus_set_local_creds (int fd, dbus_bool_t on) +{ + dbus_bool_t retval = TRUE; + +#if defined(HAVE_CMSGCRED) + /* NOOP just to make sure only one codepath is used + * and to prefer CMSGCRED + */ +#elif defined(LOCAL_CREDS) + int val = on ? 1 : 0; + if (setsockopt (fd, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) + { + _dbus_verbose ("Unable to set LOCAL_CREDS socket option on fd %d\n", fd); + retval = FALSE; + } + else + _dbus_verbose ("LOCAL_CREDS %s for further messages on fd %d\n", + on ? "enabled" : "disabled", fd); +#endif + + return retval; +} /** * Creates a socket and binds it to the given path, @@ -610,6 +1006,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 @@ -623,12 +1021,13 @@ _dbus_listen_unix_socket (const char *path, int listen_fd; struct sockaddr_un addr; size_t path_len; + unsigned int reuseaddr; _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_verbose ("listening on unix socket %s abstract=%d\n", path, abstract); - + if (!_dbus_open_unix_socket (&listen_fd, error)) { _DBUS_ASSERT_ERROR_IS_SET(error); @@ -639,7 +1038,7 @@ _dbus_listen_unix_socket (const char *path, _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; path_len = strlen (path); - + if (abstract) { #ifdef HAVE_ABSTRACT_SOCKETS @@ -656,7 +1055,7 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + strncpy (&addr.sun_path[1], path, path_len); /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ #else /* HAVE_ABSTRACT_SOCKETS */ @@ -693,10 +1092,17 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + strncpy (addr.sun_path, path, path_len); } - + + reuseaddr = 1; + if (setsockopt (listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr))==-1) + { + _dbus_warn ("Failed to set socket option\"%s\": %s", + path, _dbus_strerror (errno)); + } + if (bind (listen_fd, (struct sockaddr*) &addr, _DBUS_STRUCT_OFFSET (struct sockaddr_un, sun_path) + path_len) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), @@ -730,22 +1136,123 @@ _dbus_listen_unix_socket (const char *path, _dbus_close (listen_fd, NULL); return -1; } - + /* Try opening up the permissions, but if we can't, just go ahead * and continue, maybe it will be good enough. */ if (!abstract && chmod (path, 0777) < 0) _dbus_warn ("Could not set mode 0777 on socket %s\n", path); - + return listen_fd; } /** - * Creates a socket and connects to a socket at the given host + * Acquires one or more sockets passed in from systemd. The sockets + * are set to be nonblocking. + * + * This will set FD_CLOEXEC for the sockets returned. + * + * @param fds the file descriptors + * @param error return location for errors + * @returns the number of file descriptors + */ +int +_dbus_listen_systemd_sockets (int **fds, + DBusError *error) +{ + int r, n; + unsigned fd; + int *new_fds; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + n = sd_listen_fds (TRUE); + if (n < 0) + { + dbus_set_error (error, _dbus_error_from_errno (-n), + "Failed to acquire systemd socket: %s", + _dbus_strerror (-n)); + return -1; + } + + if (n <= 0) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "No socket received."); + return -1; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + r = sd_is_socket (fd, AF_UNSPEC, SOCK_STREAM, 1); + if (r < 0) + { + dbus_set_error (error, _dbus_error_from_errno (-r), + "Failed to verify systemd socket type: %s", + _dbus_strerror (-r)); + return -1; + } + + if (!r) + { + dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS, + "Passed socket has wrong type."); + return -1; + } + } + + /* OK, the file descriptors are all good, so let's take posession of + them then. */ + + new_fds = dbus_new (int, n); + if (!new_fds) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, + "Failed to allocate file handle array."); + goto fail; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + if (!_dbus_set_local_creds (fd, TRUE)) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to enable LOCAL_CREDS on systemd socket: %s", + _dbus_strerror (errno)); + goto fail; + } + + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto fail; + } + + new_fds[fd - SD_LISTEN_FDS_START] = fd; + } + + *fds = new_fds; + return n; + + fail: + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) + { + _dbus_close (fd, NULL); + } + + dbus_free (new_fds); + return -1; +} + +/** + * Creates a socket and connects to a socket at the given host * 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 @@ -758,19 +1265,21 @@ _dbus_connect_tcp_socket (const char *host, const char *family, DBusError *error) { + return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error); +} + +int +_dbus_connect_tcp_socket_with_nonce (const char *host, + const char *port, + const char *family, + const char *noncefile, + DBusError *error) +{ int saved_errno = 0; int fd = -1, res; struct addrinfo hints; struct addrinfo *ai, *tmp; - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - if (!_dbus_open_tcp_socket (&fd, error)) - { - _DBUS_ASSERT_ERROR_IS_SET(error); - return -1; - } - _DBUS_ASSERT_ERROR_IS_CLEAR(error); _DBUS_ZERO (hints); @@ -798,7 +1307,6 @@ _dbus_connect_tcp_socket (const char *host, _dbus_error_from_errno (errno), "Failed to lookup host/port: \"%s:%s\": %s (%d)", host, port, gai_strerror(res), res); - _dbus_close (fd, NULL); return -1; } @@ -835,12 +1343,24 @@ _dbus_connect_tcp_socket (const char *host, return -1; } + if (noncefile != NULL) + { + DBusString noncefileStr; + dbus_bool_t ret; + _dbus_string_init_const (&noncefileStr, noncefile); + ret = _dbus_send_nonce (fd, &noncefileStr, error); + _dbus_string_free (&noncefileStr); - if (!_dbus_set_fd_nonblocking (fd, error)) + if (!ret) { _dbus_close (fd, NULL); - fd = -1; + return -1; + } + } + if (!_dbus_set_fd_nonblocking (fd, error)) + { + _dbus_close (fd, NULL); return -1; } @@ -853,6 +1373,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 @@ -873,6 +1395,7 @@ _dbus_listen_tcp_socket (const char *host, int nlisten_fd = 0, *listen_fd = NULL, res, i; struct addrinfo hints; struct addrinfo *ai, *tmp; + unsigned int reuseaddr; *fds_p = NULL; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -898,13 +1421,14 @@ _dbus_listen_tcp_socket (const char *host, hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; redo_lookup_with_port: + ai = NULL; if ((res = getaddrinfo(host, port, &hints, &ai)) != 0 || !ai) { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to lookup host/port: \"%s:%s\": %s (%d)", host ? host : "*", port, gai_strerror(res), res); - return -1; + goto failed; } tmp = ai; @@ -918,6 +1442,13 @@ _dbus_listen_tcp_socket (const char *host, } _DBUS_ASSERT_ERROR_IS_CLEAR(error); + reuseaddr = 1; + if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr))==-1) + { + _dbus_warn ("Failed to set socket option \"%s:%s\": %s", + host ? host : "*", port, _dbus_strerror (errno)); + } + if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0) { saved_errno = errno; @@ -968,16 +1499,18 @@ _dbus_listen_tcp_socket (const char *host, to use the same port */ if (!port || !strcmp(port, "0")) { + int result; struct sockaddr_storage addr; socklen_t addrlen; char portbuf[50]; addrlen = sizeof(addr); - getsockname(fd, (struct sockaddr*) &addr, &addrlen); + result = getsockname(fd, (struct sockaddr*) &addr, &addrlen); - if ((res = getnameinfo((struct sockaddr*)&addr, addrlen, NULL, 0, - portbuf, sizeof(portbuf), - NI_NUMERICHOST)) != 0) + if (result == -1 || + (res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0, + portbuf, sizeof(portbuf), + NI_NUMERICHOST)) != 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to resolve port \"%s:%s\": %s (%s)", @@ -1016,7 +1549,7 @@ _dbus_listen_tcp_socket (const char *host, dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to bind socket \"%s:%s\": %s", host ? host : "*", port, _dbus_strerror (errno)); - return -1; + goto failed; } for (i = 0 ; i < nlisten_fd ; i++) @@ -1046,36 +1579,44 @@ write_credentials_byte (int server_fd, { int bytes_written; char buf[1] = { '\0' }; -#if defined(HAVE_CMSGCRED) - struct { +#if defined(HAVE_CMSGCRED) + union { struct cmsghdr hdr; - struct cmsgcred cred; + char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; } cmsg; struct iovec iov; struct msghdr msg; 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)); - cmsg.hdr.cmsg_len = sizeof (cmsg); + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred)); + _DBUS_ZERO(cmsg); + cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; #endif _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + again: -#if defined(HAVE_CMSGCRED) - bytes_written = sendmsg (server_fd, &msg, 0); +#if defined(HAVE_CMSGCRED) + bytes_written = sendmsg (server_fd, &msg, 0 +#if HAVE_DECL_MSG_NOSIGNAL + |MSG_NOSIGNAL +#endif + ); #else - bytes_written = write (server_fd, buf, 1); + bytes_written = send (server_fd, buf, 1, 0 +#if HAVE_DECL_MSG_NOSIGNAL + |MSG_NOSIGNAL +#endif + ); #endif if (bytes_written < 0 && errno == EINTR) @@ -1112,7 +1653,7 @@ write_credentials_byte (int server_fd, * we got valid credentials. On some systems, such as Linux, * reading/writing the byte isn't actually required, but we do it * anyway just to avoid multiple codepaths. - * + * * Fails if no byte is available, so you must select() first. * * The point of the byte is that on some systems we have to @@ -1134,14 +1675,11 @@ _dbus_read_credentials_socket (int client_fd, dbus_uid_t uid_read; dbus_pid_t pid_read; int bytes_read; - - uid_read = DBUS_UID_UNSET; - pid_read = DBUS_PID_UNSET; - -#ifdef HAVE_CMSGCRED - struct { + +#ifdef HAVE_CMSGCRED + union { struct cmsghdr hdr; - struct cmsgcred cred; + char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; } cmsg; #elif defined(LOCAL_CREDS) @@ -1151,8 +1689,11 @@ _dbus_read_credentials_socket (int client_fd, } cmsg; #endif + uid_read = DBUS_UID_UNSET; + pid_read = DBUS_PID_UNSET; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + /* The POSIX spec certainly doesn't promise this, but * we need these assertions to fail as soon as we're wrong about * it so we can do the porting fixups @@ -1172,14 +1713,14 @@ _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)); - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof (cmsg); + _DBUS_ZERO(cmsg); + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred)); #endif again: @@ -1194,7 +1735,7 @@ _dbus_read_credentials_socket (int client_fd, * normally only call read_credentials if the socket was ready * for reading */ - + dbus_set_error (error, _dbus_error_from_errno (errno), "Failed to read credentials byte: %s", _dbus_strerror (errno)); @@ -1217,7 +1758,8 @@ _dbus_read_credentials_socket (int client_fd, } #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) - if (cmsg.hdr.cmsg_len < sizeof (cmsg) || cmsg.hdr.cmsg_type != SCM_CREDS) + if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof (struct cmsgcred)) + || cmsg.hdr.cmsg_type != SCM_CREDS) { dbus_set_error (error, DBUS_ERROR_FAILED, "Message from recvmsg() was not SCM_CREDS"); @@ -1229,9 +1771,13 @@ _dbus_read_credentials_socket (int client_fd, { #ifdef SO_PEERCRED - struct ucred cr; +#ifdef __OpenBSD__ + struct sockpeercred cr; +#else + struct ucred cr; +#endif int cr_len = sizeof (cr); - + if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && cr_len == sizeof (cr)) { @@ -1244,8 +1790,11 @@ _dbus_read_credentials_socket (int client_fd, cr_len, (int) sizeof (cr), _dbus_strerror (errno)); } #elif defined(HAVE_CMSGCRED) - pid_read = cmsg.cred.cmcred_pid; - uid_read = cmsg.cred.cmcred_euid; + struct cmsgcred *cred; + + cred = (struct cmsgcred *) CMSG_DATA (&cmsg.hdr); + pid_read = cred->cmcred_pid; + uid_read = cred->cmcred_euid; #elif defined(LOCAL_CREDS) pid_read = DBUS_PID_UNSET; uid_read = cmsg.cred.sc_uid; @@ -1278,9 +1827,9 @@ _dbus_read_credentials_socket (int client_fd, { _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno)); } - else + else { - if (adt_set_from_ucred (adth, ucred, ADT_NEW)) + if (adt_set_from_ucred (adth, ucred, ADT_NEW)) { _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno)); } @@ -1321,7 +1870,7 @@ _dbus_read_credentials_socket (int client_fd, if (pid_read != DBUS_PID_UNSET) { - if (!_dbus_credentials_add_unix_pid (credentials, pid_read)) + if (!_dbus_credentials_add_pid (credentials, pid_read)) { _DBUS_SET_OOM (error); return FALSE; @@ -1336,7 +1885,7 @@ _dbus_read_credentials_socket (int client_fd, return FALSE; } } - + return TRUE; } @@ -1362,7 +1911,7 @@ _dbus_send_credentials_socket (int server_fd, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + if (write_credentials_byte (server_fd, error)) return TRUE; else @@ -1373,6 +1922,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 */ @@ -1382,12 +1933,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) @@ -1395,13 +1959,20 @@ _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; } /** - * Checks to make sure the given directory is - * private to the user + * Checks to make sure the given directory is + * private to the user * * @param dir the name of the directory * @param error error return @@ -1412,19 +1983,19 @@ _dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) { const char *directory; struct stat sb; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + directory = _dbus_string_get_const_data (dir); - + if (stat (directory, &sb) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), "%s", _dbus_strerror (errno)); - + return FALSE; } - + if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) || (S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode)) { @@ -1432,7 +2003,7 @@ _dbus_check_dir_is_private_to_user (DBusString *dir, DBusError *error) "%s directory is not private to the user", directory); return FALSE; } - + return TRUE; } @@ -1443,12 +2014,12 @@ fill_user_info_from_passwd (struct passwd *p, { _dbus_assert (p->pw_name != NULL); _dbus_assert (p->pw_dir != NULL); - + info->uid = p->pw_uid; info->primary_gid = p->pw_gid; info->username = _dbus_strdup (p->pw_name); info->homedir = _dbus_strdup (p->pw_dir); - + if (info->username == NULL || info->homedir == NULL) { @@ -1466,7 +2037,7 @@ fill_user_info (DBusUserInfo *info, DBusError *error) { const char *username_c; - + /* exactly one of username/uid provided */ _dbus_assert (username != NULL || uid != DBUS_UID_UNSET); _dbus_assert (username == NULL || uid == DBUS_UID_UNSET); @@ -1477,7 +2048,7 @@ fill_user_info (DBusUserInfo *info, info->n_group_ids = 0; info->username = NULL; info->homedir = NULL; - + if (username != NULL) username_c = _dbus_string_get_const_data (username); else @@ -1487,7 +2058,7 @@ fill_user_info (DBusUserInfo *info, * are always symmetrical, if not we have to add more configure * checks */ - + #if defined (HAVE_POSIX_GETPWNAM_R) || defined (HAVE_NONPOSIX_GETPWNAM_R) { struct passwd *p; @@ -1591,7 +2162,7 @@ fill_user_info (DBusUserInfo *info, /* Fill this in so we can use it to get groups */ username_c = info->username; - + #ifdef HAVE_GETGROUPLIST { gid_t *buf; @@ -1607,7 +2178,7 @@ fill_user_info (DBusUserInfo *info, dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto failed; } - + if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0) @@ -1624,10 +2195,10 @@ fill_user_info (DBusUserInfo *info, for the "id" command, and it turns out that they use an undocumented library function getgrouplist_2 (!) which is not declared in any header in /usr/include (!!). That did not seem - like the way to go here. + like the way to go here. */ - if (buf_count == initial_buf_count) - { + if (buf_count == initial_buf_count) + { buf_count *= 16; /* Retry with an arbitrarily scaled-up array */ } new = dbus_realloc (buf, buf_count * sizeof (buf[0])); @@ -1637,7 +2208,7 @@ fill_user_info (DBusUserInfo *info, dbus_free (buf); goto failed; } - + buf = new; errno = 0; @@ -1647,7 +2218,7 @@ fill_user_info (DBusUserInfo *info, { _dbus_warn ("It appears that username \"%s\" is in more than %d groups.\nProceeding with just the first %d groups.", username_c, buf_count, buf_count); - } + } else { dbus_set_error (error, @@ -1669,12 +2240,12 @@ fill_user_info (DBusUserInfo *info, dbus_free (buf); goto failed; } - + for (i = 0; i < buf_count; ++i) info->group_ids[i] = buf[i]; info->n_group_ids = buf_count; - + dbus_free (buf); } #else /* HAVE_GETGROUPLIST */ @@ -1694,9 +2265,9 @@ fill_user_info (DBusUserInfo *info, #endif /* HAVE_GETGROUPLIST */ _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + return TRUE; - + failed: _DBUS_ASSERT_ERROR_IS_SET (error); return FALSE; @@ -1754,7 +2325,7 @@ _dbus_credentials_add_from_current_process (DBusCredentials *credentials) _dbus_assert (sizeof (uid_t) <= sizeof (dbus_uid_t)); _dbus_assert (sizeof (gid_t) <= sizeof (dbus_gid_t)); - if (!_dbus_credentials_add_unix_pid(credentials, _dbus_getpid())) + if (!_dbus_credentials_add_pid(credentials, _dbus_getpid())) return FALSE; if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid())) return FALSE; @@ -1769,7 +2340,7 @@ _dbus_credentials_add_from_current_process (DBusCredentials *credentials) * is required, that is done in dbus-auth.c. The username here * need not be anything human-readable, it can be the machine-readable * form i.e. a user id. - * + * * @param str the string to append to * @returns #FALSE on no memory */ @@ -1811,7 +2382,7 @@ _dbus_geteuid (void) /** * The only reason this is separate from _dbus_getpid() is to allow it * on Windows for logging but not for other purposes. - * + * * @returns process ID to put in log messages */ unsigned long @@ -1833,7 +2404,7 @@ _dbus_parse_uid (const DBusString *uid_str, { int end; long val; - + if (_dbus_string_get_length (uid_str) == 0) { _dbus_verbose ("UID string was zero length\n"); @@ -1848,7 +2419,7 @@ _dbus_parse_uid (const DBusString *uid_str, _dbus_verbose ("could not parse string as a UID\n"); return FALSE; } - + if (end != _dbus_string_get_length (uid_str)) { _dbus_verbose ("string contained trailing stuff after UID\n"); @@ -1860,23 +2431,13 @@ _dbus_parse_uid (const DBusString *uid_str, return TRUE; } - -_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; -} +#if !DBUS_USE_SYNC +/* To be thread-safe by default on platforms that don't necessarily have + * atomic operations (notably Debian armel, which is armv4t), we must + * use a mutex that can be initialized statically, like this. + * GLib >= 2.32 uses a similar system. + */ +static pthread_mutex_t atomic_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /** @@ -1884,20 +2445,20 @@ 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); + + pthread_mutex_lock (&atomic_mutex); res = atomic->value; atomic->value += 1; - _DBUS_UNLOCK (atomic); + pthread_mutex_unlock (&atomic_mutex); + return res; #endif } @@ -1907,35 +2468,47 @@ _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; - - _DBUS_LOCK (atomic); + + pthread_mutex_lock (&atomic_mutex); res = atomic->value; atomic->value -= 1; - _DBUS_UNLOCK (atomic); + pthread_mutex_unlock (&atomic_mutex); + return res; #endif } -#ifdef DBUS_BUILD_TESTS -/** Gets our GID - * @returns process GID +/** + * Atomically get the value of an integer. It may change at any time + * thereafter, so this is mostly only useful for assertions. + * + * @param atomic pointer to the integer to get + * @returns the value at this moment */ -dbus_gid_t -_dbus_getgid (void) +dbus_int32_t +_dbus_atomic_get (DBusAtomic *atomic) { - return getgid (); -} +#if DBUS_USE_SYNC + __sync_synchronize (); + return atomic->value; +#else + dbus_int32_t res; + + pthread_mutex_lock (&atomic_mutex); + res = atomic->value; + pthread_mutex_unlock (&atomic_mutex); + + return res; #endif +} /** * Wrapper for poll(). @@ -1970,7 +2543,7 @@ _dbus_poll (DBusPollFD *fds, _DBUS_STRUCT_OFFSET (struct pollfd, revents)) { return poll ((struct pollfd*) fds, - n_fds, + n_fds, timeout_milliseconds); } else @@ -1988,7 +2561,7 @@ _dbus_poll (DBusPollFD *fds, int i; struct timeval tv; int ready; - + FD_ZERO (&read_set); FD_ZERO (&write_set); FD_ZERO (&err_set); @@ -2007,7 +2580,7 @@ _dbus_poll (DBusPollFD *fds, max_fd = MAX (max_fd, fdp->fd); } - + tv.tv_sec = timeout_milliseconds / 1000; tv.tv_usec = (timeout_milliseconds % 1000) * 1000; @@ -2038,15 +2611,25 @@ _dbus_poll (DBusPollFD *fds, } /** - * Get current time, as in gettimeofday(). + * Get current time, as in gettimeofday(). Use the monotonic clock if + * available, to avoid problems when the system time changes. * * @param tv_sec return location for number of seconds - * @param tv_usec return location for number of microseconds (thousandths) + * @param tv_usec return location for number of microseconds */ void -_dbus_get_current_time (long *tv_sec, - long *tv_usec) +_dbus_get_monotonic_time (long *tv_sec, + long *tv_usec) { +#ifdef HAVE_MONOTONIC_CLOCK + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + + if (tv_sec) + *tv_sec = ts.tv_sec; + if (tv_usec) + *tv_usec = ts.tv_nsec / 1000; +#else struct timeval t; gettimeofday (&t, NULL); @@ -2055,365 +2638,28 @@ _dbus_get_current_time (long *tv_sec, *tv_sec = t.tv_sec; if (tv_usec) *tv_usec = t.tv_usec; +#endif } /** - * Appends the contents of the given file to the string, - * returning error code. At the moment, won't open a file - * more than a megabyte in size. + * Get current time, as in gettimeofday(). Never uses the monotonic + * clock. * - * @param str the string to append to - * @param filename filename to load - * @param error place to set an error - * @returns #FALSE if error was set + * @param tv_sec return location for number of seconds + * @param tv_usec return location for number of microseconds */ -dbus_bool_t -_dbus_file_get_contents (DBusString *str, - const DBusString *filename, - DBusError *error) +void +_dbus_get_real_time (long *tv_sec, + long *tv_usec) { - int fd; - struct stat sb; - int orig_len; - int total; - const char *filename_c; + struct timeval t; - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - filename_c = _dbus_string_get_const_data (filename); - - /* O_BINARY useful on Cygwin */ - fd = open (filename_c, O_RDONLY | O_BINARY); - if (fd < 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to open \"%s\": %s", - filename_c, - _dbus_strerror (errno)); - return FALSE; - } - - _dbus_verbose ("file fd %d opened\n", fd); - - if (fstat (fd, &sb) < 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to stat \"%s\": %s", - filename_c, - _dbus_strerror (errno)); - - _dbus_verbose ("fstat() failed: %s", - _dbus_strerror (errno)); - - _dbus_close (fd, NULL); - - return FALSE; - } - - if (sb.st_size > _DBUS_ONE_MEGABYTE) - { - dbus_set_error (error, DBUS_ERROR_FAILED, - "File size %lu of \"%s\" is too large.", - (unsigned long) sb.st_size, filename_c); - _dbus_close (fd, NULL); - return FALSE; - } - - total = 0; - orig_len = _dbus_string_get_length (str); - if (sb.st_size > 0 && S_ISREG (sb.st_mode)) - { - int bytes_read; - - while (total < (int) sb.st_size) - { - bytes_read = _dbus_read (fd, str, - sb.st_size - total); - if (bytes_read <= 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Error reading \"%s\": %s", - filename_c, - _dbus_strerror (errno)); - - _dbus_verbose ("read() failed: %s", - _dbus_strerror (errno)); - - _dbus_close (fd, NULL); - _dbus_string_set_length (str, orig_len); - return FALSE; - } - else - total += bytes_read; - } - - _dbus_close (fd, NULL); - return TRUE; - } - else if (sb.st_size != 0) - { - _dbus_verbose ("Can only open regular files at the moment.\n"); - dbus_set_error (error, DBUS_ERROR_FAILED, - "\"%s\" is not a regular file", - filename_c); - _dbus_close (fd, NULL); - return FALSE; - } - else - { - _dbus_close (fd, NULL); - return TRUE; - } -} - -/** - * Writes a string out to a file. If the file exists, - * it will be atomically overwritten by the new data. - * - * @param str the string to write out - * @param filename the file to save string to - * @param error error to be filled in on failure - * @returns #FALSE on failure - */ -dbus_bool_t -_dbus_string_save_to_file (const DBusString *str, - const DBusString *filename, - DBusError *error) -{ - int fd; - int bytes_to_write; - const char *filename_c; - DBusString tmp_filename; - const char *tmp_filename_c; - int total; - dbus_bool_t need_unlink; - dbus_bool_t retval; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - fd = -1; - retval = FALSE; - need_unlink = FALSE; - - if (!_dbus_string_init (&tmp_filename)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - return FALSE; - } - - if (!_dbus_string_copy (filename, 0, &tmp_filename, 0)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - _dbus_string_free (&tmp_filename); - return FALSE; - } - - if (!_dbus_string_append (&tmp_filename, ".")) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - _dbus_string_free (&tmp_filename); - return FALSE; - } - -#define N_TMP_FILENAME_RANDOM_BYTES 8 - if (!_dbus_generate_random_ascii (&tmp_filename, N_TMP_FILENAME_RANDOM_BYTES)) - { - dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); - _dbus_string_free (&tmp_filename); - return FALSE; - } - - filename_c = _dbus_string_get_const_data (filename); - tmp_filename_c = _dbus_string_get_const_data (&tmp_filename); - - fd = open (tmp_filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, - 0600); - if (fd < 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not create %s: %s", tmp_filename_c, - _dbus_strerror (errno)); - goto out; - } - - _dbus_verbose ("tmp file fd %d opened\n", fd); - - need_unlink = TRUE; - - total = 0; - bytes_to_write = _dbus_string_get_length (str); - - while (total < bytes_to_write) - { - int bytes_written; - - bytes_written = _dbus_write (fd, str, total, - bytes_to_write - total); - - if (bytes_written <= 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not write to %s: %s", tmp_filename_c, - _dbus_strerror (errno)); - - goto out; - } - - total += bytes_written; - } - - if (fsync(fd)) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not synchronize file %s: %s", - tmp_filename_c, _dbus_strerror (errno)); - - goto out; - } - - if (!_dbus_close (fd, NULL)) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not close file %s: %s", - tmp_filename_c, _dbus_strerror (errno)); - - goto out; - } - - fd = -1; - - if (rename (tmp_filename_c, filename_c) < 0) - { - dbus_set_error (error, _dbus_error_from_errno (errno), - "Could not rename %s to %s: %s", - tmp_filename_c, filename_c, - _dbus_strerror (errno)); - - goto out; - } - - need_unlink = FALSE; - - retval = TRUE; - - out: - /* close first, then unlink, to prevent ".nfs34234235" garbage - * files - */ - - if (fd >= 0) - _dbus_close (fd, NULL); - - if (need_unlink && unlink (tmp_filename_c) < 0) - _dbus_verbose ("Failed to unlink temp file %s: %s\n", - tmp_filename_c, _dbus_strerror (errno)); - - _dbus_string_free (&tmp_filename); - - if (!retval) - _DBUS_ASSERT_ERROR_IS_SET (error); - - return retval; -} - -/** Makes the file readable by every user in the system. - * - * @param filename the filename - * @param error error location - * @returns #TRUE if the file's permissions could be changed. - */ -dbus_bool_t -_dbus_make_file_world_readable(const DBusString *filename, - DBusError *error) -{ - const char *filename_c; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - filename_c = _dbus_string_get_const_data (filename); - if (chmod (filename_c, 0644) == -1) - { - dbus_set_error (error, - DBUS_ERROR_FAILED, - "Could not change permissions of file %s: %s\n", - filename_c, - _dbus_strerror (errno)); - return FALSE; - } - return TRUE; -} - -/** Creates the given file, failing if the file already exists. - * - * @param filename the filename - * @param error error location - * @returns #TRUE if we created the file and it didn't exist - */ -dbus_bool_t -_dbus_create_file_exclusively (const DBusString *filename, - DBusError *error) -{ - int fd; - const char *filename_c; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - filename_c = _dbus_string_get_const_data (filename); - - fd = open (filename_c, O_WRONLY | O_BINARY | O_EXCL | O_CREAT, - 0600); - if (fd < 0) - { - dbus_set_error (error, - DBUS_ERROR_FAILED, - "Could not create file %s: %s\n", - filename_c, - _dbus_strerror (errno)); - return FALSE; - } - - _dbus_verbose ("exclusive file fd %d opened\n", fd); - - if (!_dbus_close (fd, NULL)) - { - dbus_set_error (error, - DBUS_ERROR_FAILED, - "Could not close file %s: %s\n", - filename_c, - _dbus_strerror (errno)); - return FALSE; - } - - return TRUE; -} - -/** - * Deletes the given file. - * - * @param filename the filename - * @param error error location - * - * @returns #TRUE if unlink() succeeded - */ -dbus_bool_t -_dbus_delete_file (const DBusString *filename, - DBusError *error) -{ - const char *filename_c; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - filename_c = _dbus_string_get_const_data (filename); + gettimeofday (&t, NULL); - if (unlink (filename_c) < 0) - { - dbus_set_error (error, DBUS_ERROR_FAILED, - "Failed to delete file %s: %s\n", - filename_c, _dbus_strerror (errno)); - return FALSE; - } - else - return TRUE; + if (tv_sec) + *tv_sec = t.tv_sec; + if (tv_usec) + *tv_usec = t.tv_usec; } /** @@ -2431,14 +2677,14 @@ _dbus_create_directory (const DBusString *filename, const char *filename_c; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + filename_c = _dbus_string_get_const_data (filename); if (mkdir (filename_c, 0700) < 0) { if (errno == EEXIST) return TRUE; - + dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to create directory %s: %s\n", filename_c, _dbus_strerror (errno)); @@ -2468,7 +2714,7 @@ _dbus_concat_dir_and_file (DBusString *dir, if (_dbus_string_get_length (dir) == 0 || _dbus_string_get_length (next_component) == 0) return TRUE; - + dir_ends_in_slash = '/' == _dbus_string_get_byte (dir, _dbus_string_get_length (dir) - 1); @@ -2530,7 +2776,7 @@ _dbus_generate_pseudorandom_bytes (DBusString *str, { int old_len; char *p; - + old_len = _dbus_string_get_length (str); if (!_dbus_string_lengthen (str, n_bytes)) @@ -2563,7 +2809,7 @@ _dbus_generate_random_bytes (DBusString *str, * a DBusError. So we always fall back to pseudorandom * if the I/O fails. */ - + old_len = _dbus_string_get_length (str); fd = -1; @@ -2573,7 +2819,7 @@ _dbus_generate_random_bytes (DBusString *str, return _dbus_generate_pseudorandom_bytes (str, n_bytes); _dbus_verbose ("/dev/urandom fd %d opened\n", fd); - + if (_dbus_read (fd, str, n_bytes) != n_bytes) { _dbus_close (fd, NULL); @@ -2583,9 +2829,9 @@ _dbus_generate_random_bytes (DBusString *str, _dbus_verbose ("Read %d bytes from /dev/urandom\n", n_bytes); - + _dbus_close (fd, NULL); - + return TRUE; } @@ -2612,7 +2858,7 @@ const char* _dbus_strerror (int error_number) { const char *msg; - + msg = strerror (error_number); if (msg == NULL) msg = "unknown"; @@ -2637,17 +2883,17 @@ _dbus_disable_sigpipe (void) * @param fd the file descriptor */ void -_dbus_fd_set_close_on_exec (int fd) +_dbus_fd_set_close_on_exec (intptr_t fd) { int val; - + val = fcntl (fd, F_GETFD, 0); - + if (val < 0) return; val |= FD_CLOEXEC; - + fcntl (fd, F_SETFD, val); } @@ -2663,7 +2909,7 @@ _dbus_close (int fd, DBusError *error) { _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + again: if (close (fd) < 0) { @@ -2679,6 +2925,49 @@ _dbus_close (int fd, } /** + * 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 + * @param error address of error location. + * @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; + } + +#ifdef 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. * * @param fd the file descriptor. @@ -2692,7 +2981,7 @@ _dbus_set_fd_nonblocking (int fd, int val; _DBUS_ASSERT_ERROR_IS_CLEAR (error); - + val = fcntl (fd, F_GETFL, 0); if (val < 0) { @@ -2725,17 +3014,17 @@ _dbus_set_fd_nonblocking (int fd, */ void _dbus_print_backtrace (void) -{ +{ #if defined (HAVE_BACKTRACE) && defined (DBUS_BUILT_R_DYNAMIC) void *bt[500]; int bt_size; int i; char **syms; - + bt_size = backtrace (bt, 500); syms = backtrace_symbols (bt, bt_size); - + i = 0; while (i < bt_size) { @@ -2757,11 +3046,8 @@ _dbus_print_backtrace (void) * Creates a full-duplex pipe (as in socketpair()). * Sets both ends of the pipe nonblocking. * - * @todo libdbus only uses this for the debug-pipe server, so in - * principle it could be in dbus-sysdeps-util.c, except that - * dbus-sysdeps-util.c isn't in libdbus when tests are enabled and the - * debug-pipe server is used. - * + * Marks both file descriptors as close-on-exec + * * @param fd1 return location for one end * @param fd2 return location for the other end * @param blocking #TRUE if pipe should be blocking @@ -2776,36 +3062,57 @@ _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))) { dbus_set_error (error, _dbus_error_from_errno (errno), "Could not set full-duplex pipe nonblocking"); - + _dbus_close (fds[0], NULL); _dbus_close (fds[1], NULL); - + return FALSE; } - + *fd1 = fds[0]; *fd2 = fds[1]; _dbus_verbose ("full-duplex pipe %d <-> %d\n", *fd1, *fd2); - - return TRUE; + + return TRUE; #else _dbus_warn ("_dbus_full_duplex_pipe() not implemented on this OS\n"); dbus_set_error (error, DBUS_ERROR_FAILED, @@ -2820,28 +3127,88 @@ _dbus_full_duplex_pipe (int *fd1, * * @param format a printf-style format string * @param args arguments for the format string - * @returns length of the given format string and args + * @returns length of the given format string and args, or -1 if no memory */ int _dbus_printf_string_upper_bound (const char *format, va_list args) { - char c; - return vsnprintf (&c, 1, format, args); -} + char static_buf[1024]; + int bufsize = sizeof (static_buf); + int len; + va_list args_copy; -/** - * Gets the temporary files directory by inspecting the environment variables - * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned - * - * @returns location of temp directory - */ -const char* -_dbus_get_tmpdir(void) -{ - static const char* tmpdir = NULL; + DBUS_VA_COPY (args_copy, args); + len = vsnprintf (static_buf, bufsize, format, args_copy); + va_end (args_copy); - if (tmpdir == NULL) + /* If vsnprintf() returned non-negative, then either the string fits in + * static_buf, or this OS has the POSIX and C99 behaviour where vsnprintf + * returns the number of characters that were needed, or this OS returns the + * truncated length. + * + * We ignore the possibility that snprintf might just ignore the length and + * overrun the buffer (64-bit Solaris 7), because that's pathological. + * If your libc is really that bad, come back when you have a better one. */ + if (len == bufsize) + { + /* This could be the truncated length (Tru64 and IRIX have this bug), + * or the real length could be coincidentally the same. Which is it? + * If vsnprintf returns the truncated length, we'll go to the slow + * path. */ + DBUS_VA_COPY (args_copy, args); + + if (vsnprintf (static_buf, 1, format, args_copy) == 1) + len = -1; + + va_end (args_copy); + } + + /* If vsnprintf() returned negative, we have to do more work. + * HP-UX returns negative. */ + while (len < 0) + { + char *buf; + + bufsize *= 2; + + buf = dbus_malloc (bufsize); + + if (buf == NULL) + return -1; + + DBUS_VA_COPY (args_copy, args); + len = vsnprintf (buf, bufsize, format, args_copy); + va_end (args_copy); + + dbus_free (buf); + + /* If the reported length is exactly the buffer size, round up to the + * next size, in case vsnprintf has been returning the truncated + * length */ + if (len == bufsize) + len = -1; + } + + return len; +} + +/** + * Gets the temporary files directory by inspecting the environment variables + * TMPDIR, TMP, and TEMP in that order. If none of those are set "/tmp" is returned + * + * @returns location of temp directory, or #NULL if no memory for locking + */ +const char* +_dbus_get_tmpdir(void) +{ + /* Protected by _DBUS_LOCK_sysdeps */ + static const char* tmpdir = NULL; + + if (!_DBUS_LOCK (sysdeps)) + return NULL; + + if (tmpdir == NULL) { /* TMPDIR is what glibc uses, then * glibc falls back to the P_tmpdir macro which @@ -2862,49 +3229,54 @@ _dbus_get_tmpdir(void) if (tmpdir == NULL) tmpdir = "/tmp"; } - + + _DBUS_UNLOCK (sysdeps); + _dbus_assert(tmpdir != NULL); - + return tmpdir; } +#if defined(DBUS_ENABLE_X11_AUTOLAUNCH) || defined(DBUS_ENABLE_LAUNCHD) /** - * Determines the address of the session bus by querying a - * platform-specific method. + * Execute a subprocess, returning up to 1024 bytes of output + * into @p result. * - * If successful, returns #TRUE and appends the address to @p - * address. If a failure happens, returns #FALSE and + * If successful, returns #TRUE and appends the output to @p + * result. If a failure happens, returns #FALSE and * sets an error in @p error. * - * @param address a DBusString where the address can be stored + * @note It's not an error if the subprocess terminates normally + * without writing any data to stdout. Verify the @p result length + * before and after this function call to cover this case. + * + * @param progname initial path to exec (may or may not be absolute) + * @param path_fallback if %TRUE, search PATH for executable + * @param argv NULL-terminated list of arguments + * @param result a DBusString where the output can be append * @param error a DBusError to store the error in case of failure * @returns #TRUE on success, #FALSE if an error happened */ -dbus_bool_t -_dbus_get_autolaunch_address (DBusString *address, - DBusError *error) +static dbus_bool_t +_read_subprocess_line_argv (const char *progpath, + dbus_bool_t path_fallback, + char * const *argv, + DBusString *result, + DBusError *error) { - static char *argv[6]; - int address_pipe[2] = { -1, -1 }; + int result_pipe[2] = { -1, -1 }; int errors_pipe[2] = { -1, -1 }; pid_t pid; int ret; int status; int orig_len; - int i; - DBusString uuid; + dbus_bool_t retval; sigset_t new_set, old_set; - + _DBUS_ASSERT_ERROR_IS_CLEAR (error); retval = FALSE; - if (!_dbus_string_init (&uuid)) - { - _DBUS_SET_OOM (error); - return FALSE; - } - /* We need to block any existing handlers for SIGCHLD temporarily; they * will cause waitpid() below to fail. * https://bugs.freedesktop.org/show_bug.cgi?id=21347 @@ -2912,49 +3284,27 @@ _dbus_get_autolaunch_address (DBusString *address, sigemptyset (&new_set); sigaddset (&new_set, SIGCHLD); sigprocmask (SIG_BLOCK, &new_set, &old_set); - - if (!_dbus_get_local_machine_uuid_encoded (&uuid)) - { - _DBUS_SET_OOM (error); - goto out; - } - - i = 0; - argv[i] = "dbus-launch"; - ++i; - argv[i] = "--autolaunch"; - ++i; - argv[i] = _dbus_string_get_data (&uuid); - ++i; - argv[i] = "--binary-syntax"; - ++i; - argv[i] = "--close-stderr"; - ++i; - argv[i] = NULL; - ++i; - _dbus_assert (i == _DBUS_N_ELEMENTS (argv)); - - orig_len = _dbus_string_get_length (address); - + orig_len = _dbus_string_get_length (result); + #define READ_END 0 #define WRITE_END 1 - if (pipe (address_pipe) < 0) + if (pipe (result_pipe) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to create a pipe: %s", - _dbus_strerror (errno)); - _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n", - _dbus_strerror (errno)); + "Failed to create a pipe to call %s: %s", + progpath, _dbus_strerror (errno)); + _dbus_verbose ("Failed to create a pipe to call %s: %s\n", + progpath, _dbus_strerror (errno)); goto out; } if (pipe (errors_pipe) < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to create a pipe: %s", - _dbus_strerror (errno)); - _dbus_verbose ("Failed to create a pipe to call dbus-launch: %s\n", - _dbus_strerror (errno)); + "Failed to create a pipe to call %s: %s", + progpath, _dbus_strerror (errno)); + _dbus_verbose ("Failed to create a pipe to call %s: %s\n", + progpath, _dbus_strerror (errno)); goto out; } @@ -2962,17 +3312,16 @@ _dbus_get_autolaunch_address (DBusString *address, if (pid < 0) { dbus_set_error (error, _dbus_error_from_errno (errno), - "Failed to fork(): %s", - _dbus_strerror (errno)); - _dbus_verbose ("Failed to fork() to call dbus-launch: %s\n", - _dbus_strerror (errno)); + "Failed to fork() to call %s: %s", + progpath, _dbus_strerror (errno)); + _dbus_verbose ("Failed to fork() to call %s: %s\n", + progpath, _dbus_strerror (errno)); goto out; } if (pid == 0) { /* child process */ - int maxfds; int fd; fd = open ("/dev/null", O_RDWR); @@ -2981,52 +3330,53 @@ _dbus_get_autolaunch_address (DBusString *address, _exit (1); _dbus_verbose ("/dev/null fd %d opened\n", fd); - + /* set-up stdXXX */ - close (address_pipe[READ_END]); + close (result_pipe[READ_END]); close (errors_pipe[READ_END]); - close (0); /* close stdin */ - close (1); /* close stdout */ - close (2); /* close stderr */ - if (dup2 (fd, 0) == -1) + if (dup2 (fd, 0) == -1) /* setup stdin */ _exit (1); - if (dup2 (address_pipe[WRITE_END], 1) == -1) + if (dup2 (result_pipe[WRITE_END], 1) == -1) /* setup stdout */ _exit (1); - if (dup2 (errors_pipe[WRITE_END], 2) == -1) + if (dup2 (errors_pipe[WRITE_END], 2) == -1) /* setup stderr */ _exit (1); - maxfds = sysconf (_SC_OPEN_MAX); - /* Pick something reasonable if for some reason sysconf - * says unlimited. - */ - if (maxfds < 0) - maxfds = 1024; - /* close all inherited fds */ - for (i = 3; i < maxfds; i++) - close (i); - - sigprocmask(SIG_SETMASK, &old_set, NULL); + _dbus_close_all (); - execv (DBUS_BINDIR "/dbus-launch", argv); + sigprocmask (SIG_SETMASK, &old_set, NULL); - /* failed, try searching PATH */ - execvp ("dbus-launch", argv); + /* If it looks fully-qualified, try execv first */ + if (progpath[0] == '/') + { + execv (progpath, argv); + /* Ok, that failed. Now if path_fallback is given, let's + * try unqualified. This is mostly a hack to work + * around systems which ship dbus-launch in /usr/bin + * but everything else in /bin (because dbus-launch + * depends on X11). + */ + if (path_fallback) + /* We must have a slash, because we checked above */ + execvp (strrchr (progpath, '/')+1, argv); + } + else + execvp (progpath, argv); /* still nothing, we failed */ _exit (1); } /* parent process */ - close (address_pipe[WRITE_END]); + close (result_pipe[WRITE_END]); close (errors_pipe[WRITE_END]); - address_pipe[WRITE_END] = -1; + result_pipe[WRITE_END] = -1; errors_pipe[WRITE_END] = -1; ret = 0; - do + do { - ret = _dbus_read (address_pipe[READ_END], address, 1024); + ret = _dbus_read (result_pipe[READ_END], result, 1024); } while (ret > 0); @@ -3039,32 +3389,37 @@ _dbus_get_autolaunch_address (DBusString *address, /* We succeeded if the process exited with status 0 and anything was read */ - if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 || - _dbus_string_get_length (address) == orig_len) + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 ) { /* The process ended with error */ DBusString error_message; - _dbus_string_init (&error_message); + if (!_dbus_string_init (&error_message)) + { + _DBUS_SET_OOM (error); + goto out; + } + ret = 0; do - { - ret = _dbus_read (errors_pipe[READ_END], &error_message, 1024); - } + { + ret = _dbus_read (errors_pipe[READ_END], &error_message, 1024); + } while (ret > 0); - _dbus_string_set_length (address, orig_len); + _dbus_string_set_length (result, orig_len); if (_dbus_string_get_length (&error_message) > 0) - dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, - "dbus-launch failed to autolaunch D-Bus session: %s", - _dbus_string_get_data (&error_message)); + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "%s terminated abnormally with the following error: %s", + progpath, _dbus_string_get_data (&error_message)); else - dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, - "Failed to execute dbus-launch to autolaunch D-Bus session"); + dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, + "%s terminated abnormally without any error message", + progpath); goto out; } retval = TRUE; - + out: sigprocmask (SIG_SETMASK, &old_set, NULL); @@ -3073,17 +3428,109 @@ _dbus_get_autolaunch_address (DBusString *address, else _DBUS_ASSERT_ERROR_IS_SET (error); - if (address_pipe[0] != -1) - close (address_pipe[0]); - if (address_pipe[1] != -1) - close (address_pipe[1]); + if (result_pipe[0] != -1) + close (result_pipe[0]); + if (result_pipe[1] != -1) + close (result_pipe[1]); if (errors_pipe[0] != -1) close (errors_pipe[0]); if (errors_pipe[1] != -1) close (errors_pipe[1]); + return retval; +} +#endif + +/** + * Returns the address of a new session bus. + * + * If successful, returns #TRUE and appends the address to @p + * address. If a failure happens, returns #FALSE and + * sets an error in @p error. + * + * @param scope scope of autolaunch (Windows only) + * @param address a DBusString where the address can be stored + * @param error a DBusError to store the error in case of failure + * @returns #TRUE on success, #FALSE if an error happened + */ +dbus_bool_t +_dbus_get_autolaunch_address (const char *scope, + DBusString *address, + DBusError *error) +{ +#ifdef DBUS_ENABLE_X11_AUTOLAUNCH + /* Perform X11-based autolaunch. (We also support launchd-based autolaunch, + * but that's done elsewhere, and if it worked, this function wouldn't + * be called.) */ + const char *display; + char *argv[6]; + int i; + DBusString uuid; + dbus_bool_t retval; + + if (_dbus_check_setuid ()) + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to autolaunch when setuid"); + return FALSE; + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + retval = FALSE; + + /* fd.o #19997: if $DISPLAY isn't set to something useful, then + * dbus-launch-x11 is just going to fail. Rather than trying to + * run it, we might as well bail out early with a nice error. */ + display = _dbus_getenv ("DISPLAY"); + + if (display == NULL || display[0] == '\0') + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to autolaunch a dbus-daemon without a $DISPLAY for X11"); + return FALSE; + } + + if (!_dbus_string_init (&uuid)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!_dbus_get_local_machine_uuid_encoded (&uuid)) + { + _DBUS_SET_OOM (error); + goto out; + } + + i = 0; + argv[i] = "dbus-launch"; + ++i; + argv[i] = "--autolaunch"; + ++i; + argv[i] = _dbus_string_get_data (&uuid); + ++i; + argv[i] = "--binary-syntax"; + ++i; + argv[i] = "--close-stderr"; + ++i; + argv[i] = NULL; + ++i; + + _dbus_assert (i == _DBUS_N_ELEMENTS (argv)); + + retval = _read_subprocess_line_argv (DBUS_BINDIR "/dbus-launch", + TRUE, + argv, address, error); + + out: _dbus_string_free (&uuid); return retval; +#else + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Using X11 for dbus-daemon autolaunch was disabled at compile time, " + "set your DBUS_SESSION_BUS_ADDRESS instead"); + return FALSE; +#endif } /** @@ -3110,222 +3557,169 @@ _dbus_read_local_machine_uuid (DBusGUID *machine_id, DBusError *error) { DBusString filename; + dbus_bool_t b; + _dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE); - return _dbus_read_uuid_file (&filename, machine_id, create_if_not_found, error); -} -#define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" -#define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" + b = _dbus_read_uuid_file (&filename, machine_id, create_if_not_found, error); + if (b) + return TRUE; -/** - * Determines the address of the session bus by querying a - * platform-specific method. - * - * The first parameter will be a boolean specifying whether - * or not a dynamic session lookup is supported on this platform. - * - * If supported is TRUE and the return value is #TRUE, the - * address will be appended to @p address. - * If a failure happens, returns #FALSE and sets an error in - * @p error. - * - * If supported is FALSE, ignore the return value. - * - * @param supported returns whether this method is supported - * @param address a DBusString where the address can be stored - * @param error a DBusError to store the error in case of failure - * @returns #TRUE on success, #FALSE if an error happened - */ -dbus_bool_t -_dbus_lookup_session_address (dbus_bool_t *supported, - DBusString *address, - DBusError *error) -{ - /* On non-Mac Unix platforms, if the session address isn't already - * set in DBUS_SESSION_BUS_ADDRESS environment variable, we punt and - * fall back to the autolaunch: global default; see - * init_session_address in dbus/dbus-bus.c. */ - *supported = FALSE; - return TRUE; + dbus_error_free (error); + + /* Fallback to the system machine ID */ + _dbus_string_init_const (&filename, "/etc/machine-id"); + return _dbus_read_uuid_file (&filename, machine_id, FALSE, error); } /** - * Returns the standard directories for a session bus to look for service - * activation files - * - * On UNIX this should be the standard xdg freedesktop.org data directories: - * - * XDG_DATA_HOME=${XDG_DATA_HOME-$HOME/.local/share} - * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share} - * - * and - * - * DBUS_DATADIR - * - * @param dirs the directory list we are returning - * @returns #FALSE on OOM + * quries launchd for a specific env var which holds the socket path. + * @param socket_path append the socket path to this DBusString + * @param launchd_env_var the env var to look up + * @param error a DBusError to store the error in case of failure + * @return the value of the env var */ - -dbus_bool_t -_dbus_get_standard_session_servicedirs (DBusList **dirs) +dbus_bool_t +_dbus_lookup_launchd_socket (DBusString *socket_path, + const char *launchd_env_var, + DBusError *error) { - const char *xdg_data_home; - const char *xdg_data_dirs; - DBusString servicedir_path; - - if (!_dbus_string_init (&servicedir_path)) - return FALSE; +#ifdef DBUS_ENABLE_LAUNCHD + char *argv[4]; + int i; - xdg_data_home = _dbus_getenv ("XDG_DATA_HOME"); - xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS"); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - if (xdg_data_dirs != NULL) + if (_dbus_check_setuid ()) { - if (!_dbus_string_append (&servicedir_path, xdg_data_dirs)) - goto oom; - - if (!_dbus_string_append (&servicedir_path, ":")) - goto oom; - } - else - { - if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:")) - goto oom; + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to find launchd socket when setuid"); + return FALSE; } - /* - * add configured datadir to defaults - * this may be the same as an xdg dir - * however the config parser should take - * care of duplicates - */ - if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) - goto oom; + i = 0; + argv[i] = "launchctl"; + ++i; + argv[i] = "getenv"; + ++i; + argv[i] = (char*)launchd_env_var; + ++i; + argv[i] = NULL; + ++i; + + _dbus_assert (i == _DBUS_N_ELEMENTS (argv)); - if (xdg_data_home != NULL) + if (!_read_subprocess_line_argv(argv[0], TRUE, argv, socket_path, error)) { - if (!_dbus_string_append (&servicedir_path, xdg_data_home)) - goto oom; + return FALSE; } - else - { - const DBusString *homedir; - DBusString local_share; - - if (!_dbus_homedir_from_current_process (&homedir)) - goto oom; - - if (!_dbus_string_append (&servicedir_path, _dbus_string_get_const_data (homedir))) - goto oom; - _dbus_string_init_const (&local_share, "/.local/share"); - if (!_dbus_concat_dir_and_file (&servicedir_path, &local_share)) - goto oom; + /* no error, but no result either */ + if (_dbus_string_get_length(socket_path) == 0) + { + return FALSE; } - if (!_dbus_split_paths_and_append (&servicedir_path, - DBUS_UNIX_STANDARD_SESSION_SERVICEDIR, - dirs)) - goto oom; - - _dbus_string_free (&servicedir_path); + /* strip the carriage-return */ + _dbus_string_shorten(socket_path, 1); return TRUE; - - oom: - _dbus_string_free (&servicedir_path); +#else /* DBUS_ENABLE_LAUNCHD */ + dbus_set_error(error, DBUS_ERROR_NOT_SUPPORTED, + "can't lookup socket from launchd; launchd support not compiled in"); return FALSE; +#endif } - -/** - * Returns the standard directories for a system bus to look for service - * activation files - * - * On UNIX this should be the standard xdg freedesktop.org data directories: - * - * XDG_DATA_DIRS=${XDG_DATA_DIRS-/usr/local/share:/usr/share} - * - * and - * - * DBUS_DATADIR - * - * On Windows there is no system bus and this function can return nothing. - * - * @param dirs the directory list we are returning - * @returns #FALSE on OOM - */ - -dbus_bool_t -_dbus_get_standard_system_servicedirs (DBusList **dirs) +#ifdef DBUS_ENABLE_LAUNCHD +static dbus_bool_t +_dbus_lookup_session_address_launchd (DBusString *address, DBusError *error) { - const char *xdg_data_dirs; - DBusString servicedir_path; + dbus_bool_t valid_socket; + DBusString socket_path; - if (!_dbus_string_init (&servicedir_path)) - return FALSE; + if (_dbus_check_setuid ()) + { + dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED, + "Unable to find launchd socket when setuid"); + return FALSE; + } - xdg_data_dirs = _dbus_getenv ("XDG_DATA_DIRS"); + if (!_dbus_string_init (&socket_path)) + { + _DBUS_SET_OOM (error); + return FALSE; + } - if (xdg_data_dirs != NULL) + valid_socket = _dbus_lookup_launchd_socket (&socket_path, "DBUS_LAUNCHD_SESSION_BUS_SOCKET", error); + + if (dbus_error_is_set(error)) { - if (!_dbus_string_append (&servicedir_path, xdg_data_dirs)) - goto oom; + _dbus_string_free(&socket_path); + return FALSE; + } - if (!_dbus_string_append (&servicedir_path, ":")) - goto oom; + if (!valid_socket) + { + dbus_set_error(error, "no socket path", + "launchd did not provide a socket path, " + "verify that org.freedesktop.dbus-session.plist is loaded!"); + _dbus_string_free(&socket_path); + return FALSE; } - else + if (!_dbus_string_append (address, "unix:path=")) { - if (!_dbus_string_append (&servicedir_path, "/usr/local/share:/usr/share:")) - goto oom; + _DBUS_SET_OOM (error); + _dbus_string_free(&socket_path); + return FALSE; + } + if (!_dbus_string_copy (&socket_path, 0, address, + _dbus_string_get_length (address))) + { + _DBUS_SET_OOM (error); + _dbus_string_free(&socket_path); + return FALSE; } - /* - * add configured datadir to defaults - * this may be the same as an xdg dir - * however the config parser should take - * care of duplicates - */ - if (!_dbus_string_append (&servicedir_path, DBUS_DATADIR":")) - goto oom; - - if (!_dbus_split_paths_and_append (&servicedir_path, - DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR, - dirs)) - goto oom; - - _dbus_string_free (&servicedir_path); + _dbus_string_free(&socket_path); return TRUE; - - oom: - _dbus_string_free (&servicedir_path); - return FALSE; -} - -/** - * Append the absolute path of the system.conf file - * (there is no system bus on Windows so this can just - * return FALSE and print a warning or something) - * - * @param str the string to append to - * @returns #FALSE if no memory - */ -dbus_bool_t -_dbus_append_system_config_file (DBusString *str) -{ - return _dbus_string_append (str, DBUS_SYSTEM_CONFIG_FILE); } +#endif /** - * Append the absolute path of the session.conf file. - * - * @param str the string to append to - * @returns #FALSE if no memory + * Determines the address of the session bus by querying a + * platform-specific method. + * + * The first parameter will be a boolean specifying whether + * or not a dynamic session lookup is supported on this platform. + * + * If supported is TRUE and the return value is #TRUE, the + * address will be appended to @p address. + * If a failure happens, returns #FALSE and sets an error in + * @p error. + * + * If supported is FALSE, ignore the return value. + * + * @param supported returns whether this method is supported + * @param address a DBusString where the address can be stored + * @param error a DBusError to store the error in case of failure + * @returns #TRUE on success, #FALSE if an error happened */ dbus_bool_t -_dbus_append_session_config_file (DBusString *str) +_dbus_lookup_session_address (dbus_bool_t *supported, + DBusString *address, + DBusError *error) { - return _dbus_string_append (str, DBUS_SESSION_CONFIG_FILE); +#ifdef DBUS_ENABLE_LAUNCHD + *supported = TRUE; + return _dbus_lookup_session_address_launchd (address, error); +#else + /* On non-Mac Unix platforms, if the session address isn't already + * set in DBUS_SESSION_BUS_ADDRESS environment variable, we punt and + * fall back to the autolaunch: global default; see + * init_session_address in dbus/dbus-bus.c. */ + *supported = FALSE; + return TRUE; +#endif } /** @@ -3333,7 +3727,7 @@ _dbus_append_session_config_file (DBusString *str) * caches should be nuked. Of course any caches that need explicit reload * are probably broken, but c'est la vie. * - * + * */ void _dbus_flush_caches (void) @@ -3348,10 +3742,10 @@ _dbus_flush_caches (void) * * On UNIX the directory is ~/.dbus-keyrings while on Windows it should probably * be something else, since the dotfile convention is not normal on Windows. - * + * * @param directory string to append directory to * @param credentials credentials the directory should be for - * + * * @returns #FALSE on no memory */ dbus_bool_t @@ -3361,10 +3755,10 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, DBusString homedir; DBusString dotdir; dbus_uid_t uid; - + _dbus_assert (credentials != NULL); _dbus_assert (!_dbus_credentials_are_anonymous (credentials)); - + if (!_dbus_string_init (&homedir)) return FALSE; @@ -3373,11 +3767,11 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, if (!_dbus_homedir_from_uid (uid, &homedir)) goto failed; - -#ifdef DBUS_BUILD_TESTS + +#ifdef DBUS_ENABLE_EMBEDDED_TESTS { const char *override; - + override = _dbus_getenv ("DBUS_TEST_HOMEDIR"); if (override != NULL && *override != '\0') { @@ -3390,6 +3784,8 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, } else { + /* Not strictly thread-safe, but if we fail at thread-safety here, + * the worst that will happen is some extra warnings. */ static dbus_bool_t already_warned = FALSE; if (!already_warned) { @@ -3404,7 +3800,7 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, if (!_dbus_concat_dir_and_file (&homedir, &dotdir)) goto failed; - + if (!_dbus_string_copy (&homedir, 0, directory, _dbus_string_get_length (directory))) { goto failed; @@ -3412,12 +3808,26 @@ _dbus_append_keyring_directory_for_credentials (DBusString *directory, _dbus_string_free (&homedir); return TRUE; - - failed: + + failed: _dbus_string_free (&homedir); return FALSE; } +//PENDING(kdab) docs +dbus_bool_t +_dbus_daemon_publish_session_bus_address (const char* addr, + const char *scope) +{ + return TRUE; +} + +//PENDING(kdab) docs +void +_dbus_daemon_unpublish_session_bus_address (void) +{ + +} /** * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently @@ -3431,4 +3841,251 @@ _dbus_get_is_errno_eagain_or_ewouldblock (void) return errno == EAGAIN || errno == EWOULDBLOCK; } +/** + * Removes a directory; Directory must be empty + * + * @param filename directory filename + * @param error initialized error object + * @returns #TRUE on success + */ +dbus_bool_t +_dbus_delete_directory (const DBusString *filename, + DBusError *error) +{ + const char *filename_c; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + filename_c = _dbus_string_get_const_data (filename); + + if (rmdir (filename_c) != 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to remove directory %s: %s\n", + filename_c, _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +/** + * 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 +} + +/** + * Closes all file descriptors except the first three (i.e. stdin, + * stdout, stderr). + */ +void +_dbus_close_all (void) +{ + int maxfds, i; + +#ifdef __linux__ + DIR *d; + + /* On Linux we can optimize this a bit if /proc is available. If it + isn't available, fall back to the brute force way. */ + + d = opendir ("/proc/self/fd"); + if (d) + { + for (;;) + { + struct dirent buf, *de; + int k, fd; + long l; + char *e = NULL; + + k = readdir_r (d, &buf, &de); + if (k != 0 || !de) + break; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol (de->d_name, &e, 10); + if (errno != 0 || e == NULL || *e != '\0') + continue; + + fd = (int) l; + if (fd < 3) + continue; + + if (fd == dirfd (d)) + continue; + + close (fd); + } + + closedir (d); + return; + } +#endif + + maxfds = sysconf (_SC_OPEN_MAX); + + /* Pick something reasonable if for some reason sysconf says + * unlimited. + */ + if (maxfds < 0) + maxfds = 1024; + + /* close all inherited fds */ + for (i = 3; i < maxfds; i++) + close (i); +} + +/** + * **NOTE**: If you modify this function, please also consider making + * the corresponding change in GLib. See + * glib/gutils.c:g_check_setuid(). + * + * Returns TRUE if the current process was executed as setuid (or an + * equivalent __libc_enable_secure is available). See: + * http://osdir.com/ml/linux.lfs.hardened/2007-04/msg00032.html + */ +dbus_bool_t +_dbus_check_setuid (void) +{ + /* TODO: get __libc_enable_secure exported from glibc. + * See http://www.openwall.com/lists/owl-dev/2012/08/14/1 + */ +#if 0 && defined(HAVE_LIBC_ENABLE_SECURE) + { + /* See glibc/include/unistd.h */ + extern int __libc_enable_secure; + return __libc_enable_secure; + } +#elif defined(HAVE_ISSETUGID) + /* BSD: http://www.freebsd.org/cgi/man.cgi?query=issetugid&sektion=2 */ + return issetugid (); +#else + uid_t ruid, euid, suid; /* Real, effective and saved user ID's */ + gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */ + + /* We call into this function from _dbus_threads_init_platform_specific() + * to make sure these are initialized before we start threading. */ + static dbus_bool_t check_setuid_initialised; + static dbus_bool_t is_setuid; + + if (_DBUS_UNLIKELY (!check_setuid_initialised)) + { +#ifdef HAVE_GETRESUID + if (getresuid (&ruid, &euid, &suid) != 0 || + getresgid (&rgid, &egid, &sgid) != 0) +#endif /* HAVE_GETRESUID */ + { + suid = ruid = getuid (); + sgid = rgid = getgid (); + euid = geteuid (); + egid = getegid (); + } + + check_setuid_initialised = TRUE; + is_setuid = (ruid != euid || ruid != suid || + rgid != egid || rgid != sgid); + + } + return is_setuid; +#endif +} + +/** + * Read the address from the socket and append it to the string + * + * @param fd the socket + * @param address + * @param error return location for error code + */ +dbus_bool_t +_dbus_append_address_from_socket (int fd, + DBusString *address, + DBusError *error) +{ + union { + struct sockaddr sa; + struct sockaddr_storage storage; + struct sockaddr_un un; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + } socket; + char hostip[INET6_ADDRSTRLEN]; + int size = sizeof (socket); + + if (getsockname (fd, &socket.sa, &size)) + goto err; + + switch (socket.sa.sa_family) + { + case AF_UNIX: + if (socket.un.sun_path[0]=='\0') + { + if (_dbus_string_append_printf (address, "unix:abstract=%s", &(socket.un.sun_path[1]))) + return TRUE; + } + else + { + if (_dbus_string_append_printf (address, "unix:path=%s", socket.un.sun_path)) + return TRUE; + } + break; + case AF_INET: + if (inet_ntop (AF_INET, &socket.ipv4.sin_addr, hostip, sizeof (hostip))) + if (_dbus_string_append_printf (address, "tcp:family=ipv4,host=%s,port=%u", + hostip, ntohs (socket.ipv4.sin_port))) + return TRUE; + break; +#ifdef AF_INET6 + case AF_INET6: + if (inet_ntop (AF_INET6, &socket.ipv6.sin6_addr, hostip, sizeof (hostip))) + if (_dbus_string_append_printf (address, "tcp:family=ipv6,host=%s,port=%u", + hostip, ntohs (socket.ipv6.sin6_port))) + return TRUE; + break; +#endif + default: + dbus_set_error (error, + _dbus_error_from_errno (EINVAL), + "Failed to read address from socket: Unknown socket type."); + return FALSE; + } + err: + dbus_set_error (error, + _dbus_error_from_errno (errno), + "Failed to open socket: %s", + _dbus_strerror (errno)); + return FALSE; +} + /* tests in dbus-sysdeps-util.c */