diff options
Diffstat (limited to 'dbus/dbus-sysdeps-unix.c')
-rw-r--r-- | dbus/dbus-sysdeps-unix.c | 332 |
1 files changed, 230 insertions, 102 deletions
diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 42aa9674..f0f1e706 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -18,7 +18,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ @@ -1373,9 +1373,9 @@ write_credentials_byte (int server_fd, int bytes_written; char buf[1] = { '\0' }; #if defined(HAVE_CMSGCRED) - struct { + union { struct cmsghdr hdr; - struct cmsgcred cred; + char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; } cmsg; struct iovec iov; struct msghdr msg; @@ -1386,10 +1386,10 @@ write_credentials_byte (int server_fd, msg.msg_iov = &iov; msg.msg_iovlen = 1; - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof (cmsg); + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred)); _DBUS_ZERO(cmsg); - cmsg.hdr.cmsg_len = sizeof (cmsg); + cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred)); cmsg.hdr.cmsg_level = SOL_SOCKET; cmsg.hdr.cmsg_type = SCM_CREDS; #endif @@ -1461,13 +1461,10 @@ _dbus_read_credentials_socket (int client_fd, dbus_pid_t pid_read; int bytes_read; - uid_read = DBUS_UID_UNSET; - pid_read = DBUS_PID_UNSET; - #ifdef HAVE_CMSGCRED - struct { + union { struct cmsghdr hdr; - struct cmsgcred cred; + char cred[CMSG_SPACE (sizeof (struct cmsgcred))]; } cmsg; #elif defined(LOCAL_CREDS) @@ -1477,6 +1474,9 @@ _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 @@ -1504,8 +1504,8 @@ _dbus_read_credentials_socket (int client_fd, #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS) _DBUS_ZERO(cmsg); - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof (cmsg); + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred)); #endif again: @@ -1543,7 +1543,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"); @@ -1570,8 +1571,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); + 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; @@ -1945,8 +1949,10 @@ fill_user_info (DBusUserInfo *info, gid_t *buf; int buf_count; int i; - - buf_count = 17; + int initial_buf_count; + + initial_buf_count = 17; + buf_count = initial_buf_count; buf = dbus_new (gid_t, buf_count); if (buf == NULL) { @@ -1958,7 +1964,25 @@ fill_user_info (DBusUserInfo *info, info->primary_gid, buf, &buf_count) < 0) { - gid_t *new = dbus_realloc (buf, buf_count * sizeof (buf[0])); + gid_t *new; + /* Presumed cause of negative return code: buf has insufficient + entries to hold the entire group list. The Linux behavior in this + case is to pass back the actual number of groups in buf_count, but + on Mac OS X 10.5, buf_count is unhelpfully left alone. + So as a hack, try to help out a bit by guessing a larger + number of groups, within reason.. might still fail, of course, + but we can at least print a more informative message. I looked up + the "right way" to do this by downloading Apple's own source code + 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. + */ + 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])); if (new == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); @@ -1971,14 +1995,22 @@ fill_user_info (DBusUserInfo *info, errno = 0; if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0) { - dbus_set_error (error, - _dbus_error_from_errno (errno), - "Failed to get groups for username \"%s\" primary GID " - DBUS_GID_FORMAT ": %s\n", - username_c, info->primary_gid, - _dbus_strerror (errno)); - dbus_free (buf); - goto failed; + if (errno == 0) + { + _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, + _dbus_error_from_errno (errno), + "Failed to get groups for username \"%s\" primary GID " + DBUS_GID_FORMAT ": %s\n", + username_c, info->primary_gid, + _dbus_strerror (errno)); + dbus_free (buf); + goto failed; + } } } @@ -3235,83 +3267,73 @@ _dbus_get_tmpdir(void) } /** - * 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; - } - - 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)); + /* 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 + */ + sigemptyset (&new_set); + sigaddset (&new_set, SIGCHLD); + sigprocmask (SIG_BLOCK, &new_set, &old_set); - 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; } @@ -3319,10 +3341,10 @@ _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; } @@ -3340,7 +3362,7 @@ _dbus_get_autolaunch_address (DBusString *address, _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 */ @@ -3348,7 +3370,7 @@ _dbus_get_autolaunch_address (DBusString *address, if (dup2 (fd, 0) == -1) _exit (1); - if (dup2 (address_pipe[WRITE_END], 1) == -1) + if (dup2 (result_pipe[WRITE_END], 1) == -1) _exit (1); if (dup2 (errors_pipe[WRITE_END], 2) == -1) _exit (1); @@ -3363,25 +3385,39 @@ _dbus_get_autolaunch_address (DBusString *address, for (i = 3; i < maxfds; i++) close (i); - 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 { - ret = _dbus_read (address_pipe[READ_END], address, 1024); + ret = _dbus_read (result_pipe[READ_END], result, 1024); } while (ret > 0); @@ -3394,47 +3430,108 @@ _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); 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); + if (retval) _DBUS_ASSERT_ERROR_IS_CLEAR (error); 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; +} + +/** + * 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 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 (DBusString *address, + DBusError *error) +{ + static char *argv[6]; + int i; + DBusString uuid; + dbus_bool_t retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + retval = 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; } @@ -3470,6 +3567,37 @@ _dbus_read_local_machine_uuid (DBusGUID *machine_id, #define DBUS_UNIX_STANDARD_SESSION_SERVICEDIR "/dbus-1/services" #define DBUS_UNIX_STANDARD_SYSTEM_SERVICEDIR "/dbus-1/system-services" +/** + * 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; +} /** * Returns the standard directories for a session bus to look for service |