dbus-sysdeps-unix.c
/* -*- 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
*
* SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
*
* 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
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* 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
*
*/
#include <config.h>
#include "dbus-internals.h"
#include "dbus-sysdeps.h"
#include "dbus-sysdeps-unix.h"
#include "dbus-threads.h"
#include "dbus-protocol.h"
#include "dbus-file.h"
#include "dbus-transport.h"
#include "dbus-string.h"
#include "dbus-userdb.h"
#include "dbus-list.h"
#include "dbus-credentials.h"
#include "dbus-nonce.h"
#include <limits.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <dirent.h>
#include <sys/un.h>
#include <pwd.h>
#include <time.h>
#include <locale.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <grp.h>
#include <arpa/inet.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LINUX_CLOSE_RANGE_H
#include <linux/close_range.h>
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_WRITEV
#include <sys/uio.h>
#endif
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#ifdef HAVE_GETPEERUCRED
#include <ucred.h>
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#ifdef HAVE_SYS_RANDOM_H
#include <sys/random.h>
#endif
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
#ifdef HAVE_ADT
#include <bsm/adt.h>
#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#if !defined(HAVE_STDATOMIC_H) && !DBUS_USE_SYNC
#include <pthread.h>
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0
#endif
#ifndef HAVE_SOCKLEN_T
#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 */
#if defined(__linux__) && defined(__NR_close_range) && !defined(HAVE_CLOSE_RANGE)
/* The kernel headers are new enough to have the close_range syscall,
* but glibc isn't new enough to have the syscall wrapper, so call the
* syscall directly. */
static inline int
close_range (unsigned int first,
unsigned int last,
int flags)
{
return syscall (__NR_close_range, first, last, flags);
}
/* Now we can call that inline wrapper as though it was provided by glibc. */
#define HAVE_CLOSE_RANGE
#endif
dbus_bool_t
_dbus_ensure_standard_fds (DBusEnsureStandardFdsFlags flags,
const char **error_str_p)
{
static int const relevant_flag[] = { DBUS_FORCE_STDIN_NULL,
DBUS_FORCE_STDOUT_NULL,
DBUS_FORCE_STDERR_NULL };
/* Should always get replaced with the real error before use */
const char *error_str = "Failed mysteriously";
int devnull = -1;
int saved_errno;
/* This function relies on the standard fds having their POSIX values. */
_DBUS_STATIC_ASSERT (STDIN_FILENO == 0);
_DBUS_STATIC_ASSERT (STDOUT_FILENO == 1);
_DBUS_STATIC_ASSERT (STDERR_FILENO == 2);
int i;
for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
{
/* Because we rely on being single-threaded, and we want the
* standard fds to not be close-on-exec, we don't set it
* close-on-exec. */
if (devnull < i)
devnull = open ("/dev/null", O_RDWR);
if (devnull < 0)
{
error_str = "Failed to open /dev/null";
goto out;
}
/* We already opened all fds < i, so the only way this assertion
* could fail is if another thread closed one, and we document
* this function as not safe for multi-threading. */
_dbus_assert (devnull >= i);
if (devnull != i && (flags & relevant_flag[i]) != 0)
{
if (dup2 (devnull, i) < 0)
{
error_str = "Failed to dup2 /dev/null onto a standard fd";
goto out;
}
}
}
error_str = NULL;
out:
saved_errno = errno;
if (devnull > STDERR_FILENO)
close (devnull);
if (error_str_p != NULL)
*error_str_p = error_str;
errno = saved_errno;
return (error_str == NULL);
}
static dbus_bool_t _dbus_set_fd_nonblocking (int fd,
DBusError *error);
static dbus_bool_t
_dbus_open_socket (int *fd_p,
int domain,
int type,
int protocol,
DBusError *error)
{
#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 || errno == EPROTOTYPE))
#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;
}
else
{
dbus_set_error(error,
_dbus_error_from_errno (errno),
"Failed to open socket: %s",
_dbus_strerror (errno));
return FALSE;
}
}
static dbus_bool_t
_dbus_open_unix_socket (int *fd,
DBusError *error)
{
return _dbus_open_socket(fd, AF_UNIX, SOCK_STREAM, 0, error);
}
dbus_bool_t
_dbus_close_socket (DBusSocket *fd,
DBusError *error)
{
dbus_bool_t rv;
_dbus_assert (fd != NULL);
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
rv = _dbus_close (fd->fd, error);
_dbus_socket_invalidate (fd);
return rv;
}
int
_dbus_read_socket (DBusSocket fd,
DBusString *buffer,
int count)
{
return _dbus_read (fd.fd, buffer, count);
}
int
_dbus_write_socket (DBusSocket fd,
const DBusString *buffer,
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.fd, data, len, MSG_NOSIGNAL);
if (bytes_written < 0 && errno == EINTR)
goto again;
return bytes_written;
#else
return _dbus_write (fd.fd, buffer, start, len);
#endif
}
int
_dbus_read_socket_with_unix_fds (DBusSocket fd,
DBusString *buffer,
int count,
int *fds,
unsigned 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 <= DBUS_MAXIMUM_MESSAGE_UNIX_FDS);
start = _dbus_string_get_length (buffer);
if (!_dbus_string_lengthen (buffer, count))
{
errno = ENOMEM;
return -1;
}
_DBUS_ZERO(iov);
iov.iov_base = _dbus_string_get_data_len (buffer, start, count);
iov.iov_len = count;
_DBUS_ZERO(m);
m.msg_iov = &iov;
m.msg_iovlen = 1;
/* Hmm, we have no clue how long the control data will actually be
that is queued for us. The least we can do is assume that the
caller knows. Hence let's make space for the number of fds that
we shall read at max plus the cmsg header. */
m.msg_controllen = CMSG_SPACE(*n_fds * sizeof(int));
/* It's probably safe to assume that systems with SCM_RIGHTS also
know alloca() */
m.msg_control = alloca(m.msg_controllen);
memset(m.msg_control, 0, m.msg_controllen);
/* Do not include the padding at the end when we tell the kernel
* how much we're willing to receive. This avoids getting
* the padding filled with additional fds that we weren't expecting,
* if a (potentially malicious) sender included them. (fd.o #83622) */
m.msg_controllen = CMSG_LEN (*n_fds * sizeof(int));
again:
bytes_read = recvmsg (fd.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;
for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm))
if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS)
{
size_t i;
int *payload = (int *) (void *) CMSG_DATA (cm);
size_t payload_len_bytes = (cm->cmsg_len - CMSG_LEN (0));
size_t payload_len_fds;
size_t fds_to_use;
/* Every unsigned int fits in a size_t without truncation, so
* casting (size_t) *n_fds is OK */
_DBUS_STATIC_ASSERT (sizeof (size_t) >= sizeof (unsigned int));
if ((m.msg_flags & MSG_CTRUNC) && CMSG_NXTHDR(&m, cm) == NULL &&
(char *) payload + payload_len_bytes >
(char *) m.msg_control + m.msg_controllen)
{
/* This is the last cmsg in a truncated message and using
* cmsg_len would apparently overrun the allocated buffer.
* Some operating systems (illumos and Solaris are known) do
* not adjust cmsg_len in the last cmsg when truncation occurs.
* Adjust the payload length here. The calculation for
* payload_len_fds below will discard any trailing bytes that
* belong to an incomplete file descriptor - the kernel will
* have already closed that (at least for illumos and Solaris)
*/
payload_len_bytes = m.msg_controllen -
((char *) payload - (char *) m.msg_control);
}
payload_len_fds = payload_len_bytes / sizeof (int);
if (_DBUS_LIKELY (payload_len_fds <= (size_t) *n_fds))
{
/* The fds in the payload will fit in our buffer */
fds_to_use = payload_len_fds;
}
else
{
/* Too many fds in the payload. This shouldn't happen
* any more because we're setting m.msg_controllen to
* the exact number we can accept, but be safe and
* truncate. */
fds_to_use = (size_t) *n_fds;
/* Close the excess fds to avoid DoS: if they stayed open,
* someone could send us an extra fd per message
* and we'd eventually run out. */
for (i = fds_to_use; i < payload_len_fds; i++)
{
close (payload[i]);
}
}
memcpy (fds, payload, fds_to_use * sizeof (int));
found = TRUE;
/* This narrowing cast from size_t to unsigned int cannot
* overflow because we have chosen fds_to_use
* to be <= *n_fds */
*n_fds = (unsigned int) fds_to_use;
/* 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 < fds_to_use; i++)
_dbus_fd_set_close_on_exec(fds[i]);
break;
}
if (!found)
*n_fds = 0;
if (m.msg_flags & MSG_CTRUNC)
{
unsigned int i;
/* 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. */
/* We still need to close whatever fds we *did* receive,
* otherwise they'll never get closed. (CVE-2020-12049) */
for (i = 0; i < *n_fds; i++)
close (fds[i]);
*n_fds = 0;
errno = ENOSPC;
_dbus_string_set_length (buffer, start);
return -1;
}
/* put length back (doesn't actually realloc) */
_dbus_string_set_length (buffer, start + bytes_read);
#if 0
if (bytes_read > 0)
_dbus_verbose_bytes_of_string (buffer, start, bytes_read);
#endif
return bytes_read;
}
#endif
}
int
_dbus_write_socket_with_unix_fds(DBusSocket 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(DBusSocket fd,
const DBusString *buffer1,
int start1,
int len1,
const DBusString *buffer2,
int start2,
int len2,
const int *fds,
int n_fds) {
#ifndef HAVE_UNIX_FD_PASSING
if (n_fds > 0) {
errno = ENOTSUP;
return -1;
}
return _dbus_write_socket_two(fd,
buffer1, start1, len1,
buffer2, start2, len2);
#else
struct msghdr m;
struct cmsghdr *cm;
struct iovec iov[2];
int bytes_written;
_dbus_assert (len1 >= 0);
_dbus_assert (len2 >= 0);
_dbus_assert (n_fds >= 0);
_DBUS_ZERO(iov);
iov[0].iov_base = (char*) _dbus_string_get_const_data_len (buffer1, start1, len1);
iov[0].iov_len = len1;
if (buffer2)
{
iov[1].iov_base = (char*) _dbus_string_get_const_data_len (buffer2, start2, len2);
iov[1].iov_len = len2;
}
_DBUS_ZERO(m);
m.msg_iov = iov;
m.msg_iovlen = buffer2 ? 2 : 1;
if (n_fds > 0)
{
m.msg_controllen = CMSG_SPACE(n_fds * sizeof(int));
m.msg_control = alloca(m.msg_controllen);
memset(m.msg_control, 0, m.msg_controllen);
cm = CMSG_FIRSTHDR(&m);
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
cm->cmsg_len = CMSG_LEN(n_fds * sizeof(int));
memcpy(CMSG_DATA(cm), fds, n_fds * sizeof(int));
}
again:
bytes_written = sendmsg (fd.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
}
int
_dbus_write_socket_two (DBusSocket fd,
const DBusString *buffer1,
int start1,
int len1,
const DBusString *buffer2,
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.fd, &m, MSG_NOSIGNAL);
if (bytes_written < 0 && errno == EINTR)
goto again;
return bytes_written;
#else
return _dbus_write_two (fd.fd, buffer1, start1, len1,
buffer2, start2, len2);
#endif
}
int
_dbus_read (int fd,
DBusString *buffer,
int count)
{
int bytes_read;
int start;
char *data;
_dbus_assert (count >= 0);
start = _dbus_string_get_length (buffer);
if (!_dbus_string_lengthen (buffer, count))
{
errno = ENOMEM;
return -1;
}
data = _dbus_string_get_data_len (buffer, start, count);
again:
bytes_read = read (fd, data, count);
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
{
/* 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;
}
}
int
_dbus_write (int fd,
const DBusString *buffer,
int start,
int len)
{
const char *data;
int bytes_written;
data = _dbus_string_get_const_data_len (buffer, start, len);
again:
bytes_written = write (fd, data, len);
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;
}
int
_dbus_write_two (int fd,
const DBusString *buffer1,
int start1,
int len1,
const DBusString *buffer2,
int start2,
int len2)
{
_dbus_assert (buffer1 != NULL);
_dbus_assert (start1 >= 0);
_dbus_assert (start2 >= 0);
_dbus_assert (len1 >= 0);
_dbus_assert (len2 >= 0);
#ifdef HAVE_WRITEV
{
struct iovec vectors[2];
const char *data1;
const char *data2;
int bytes_written;
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;
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, ret2;
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 */
}
DBusSocket
_dbus_connect_unix_socket (const char *path,
dbus_bool_t abstract,
DBusError *error)
{
DBusSocket fd = DBUS_SOCKET_INIT;
size_t path_len;
struct sockaddr_un addr;
_DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_verbose ("connecting to unix socket %s abstract=%d\n",
path, abstract);
if (!_dbus_open_unix_socket (&fd.fd, error))
{
_DBUS_ASSERT_ERROR_IS_SET(error);
return fd;
}
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
_DBUS_ZERO (addr);
addr.sun_family = AF_UNIX;
path_len = strlen (path);
if (abstract)
{
#ifdef __linux__
addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
path_len++; /* Account for the extra nul byte added to the start of sun_path */
if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
{
dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
"Abstract socket name too long\n");
_dbus_close_socket (&fd, NULL);
return fd;
}
strncpy (&addr.sun_path[1], path, sizeof (addr.sun_path) - 2);
/* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
#else /* !__linux__ */
dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
"Operating system does not support abstract socket namespace\n");
_dbus_close_socket (&fd, NULL);
return fd;
#endif /* !__linux__ */
}
else
{
if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
{
dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
"Socket name too long\n");
_dbus_close_socket (&fd, NULL);
return fd;
}
strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
}
if (connect (fd.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_socket (&fd, NULL);
return fd;
}
if (!_dbus_set_fd_nonblocking (fd.fd, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
_dbus_close_socket (&fd, NULL);
return fd;
}
return fd;
}
DBusSocket
_dbus_connect_exec (const char *path,
char *const argv[],
DBusError *error)
{
DBusSocket s = DBUS_SOCKET_INIT;
int fds[2];
pid_t pid;
int retval;
dbus_bool_t cloexec_done = 0;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_verbose ("connecting to process %s\n", path);
#ifdef SOCK_CLOEXEC
retval = socketpair (AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds);
cloexec_done = (retval >= 0);
if (retval < 0 && (errno == EINVAL || errno == EPROTOTYPE))
#endif
{
retval = socketpair (AF_UNIX, SOCK_STREAM, 0, fds);
}
if (retval < 0)
{
dbus_set_error (error,
_dbus_error_from_errno (errno),
"Failed to create socket pair: %s",
_dbus_strerror (errno));
_dbus_assert (!_dbus_socket_is_valid (s));
return s;
}
if (!cloexec_done)
{
_dbus_fd_set_close_on_exec (fds[0]);
_dbus_fd_set_close_on_exec (fds[1]);
}
/* Make sure our output buffers aren't redundantly printed by both the
* parent and the child */
fflush (stdout);
fflush (stderr);
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]);
_dbus_assert (!_dbus_socket_is_valid (s));
return s;
}
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, (char * const *) 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]);
_dbus_assert (!_dbus_socket_is_valid (s));
return s;
}
s.fd = fds[0];
return s;
}
DBusSocket
_dbus_listen_unix_socket (const char *path,
dbus_bool_t abstract,
DBusError *error)
{
DBusSocket s = DBUS_SOCKET_INIT;
int listen_fd;
struct sockaddr_un addr;
size_t path_len;
_DBUS_STATIC_ASSERT (sizeof (addr.sun_path) > _DBUS_MAX_SUN_PATH_LENGTH);
_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);
return s;
}
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
_DBUS_ZERO (addr);
addr.sun_family = AF_UNIX;
path_len = strlen (path);
if (abstract)
{
#ifdef __linux__
/* remember that abstract names aren't nul-terminated so we rely
* on sun_path being filled in with zeroes above.
*/
addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
path_len++; /* Account for the extra nul byte added to the start of sun_path */
if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
{
dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
"Abstract socket name too long\n");
_dbus_close (listen_fd, NULL);
return s;
}
strncpy (&addr.sun_path[1], path, sizeof (addr.sun_path) - 2);
/* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
#else /* !__linux__ */
dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED,
"Operating system does not support abstract socket namespace\n");
_dbus_close (listen_fd, NULL);
return s;
#endif /* !__linux__ */
}
else
{
/* Discussed security implications of this with Nalin,
* and we couldn't think of where it would kick our ass, but
* it still seems a bit sucky. It also has non-security suckage;
* really we'd prefer to exit if the socket is already in use.
* But there doesn't seem to be a good way to do this.
*
* Just to be extra careful, I threw in the stat() - clearly
* the stat() can't *fix* any security issue, but it at least
* avoids inadvertent/accidental data loss.
*/
{
struct stat sb;
if (stat (path, &sb) == 0 &&
S_ISSOCK (sb.st_mode))
unlink (path);
}
if (path_len > _DBUS_MAX_SUN_PATH_LENGTH)
{
dbus_set_error (error, DBUS_ERROR_BAD_ADDRESS,
"Socket name too long\n");
_dbus_close (listen_fd, NULL);
return s;
}
strncpy (addr.sun_path, path, sizeof (addr.sun_path) - 1);
}
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),
"Failed to bind socket \"%s\": %s",
path, _dbus_strerror (errno));
_dbus_close (listen_fd, NULL);
return s;
}
if (listen (listen_fd, SOMAXCONN /* backlog */) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to listen on socket \"%s\": %s",
path, _dbus_strerror (errno));
_dbus_close (listen_fd, NULL);
return s;
}
if (!_dbus_set_fd_nonblocking (listen_fd, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
_dbus_close (listen_fd, NULL);
return s;
}
/* 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", path);
s.fd = listen_fd;
return s;
}
int
_dbus_listen_systemd_sockets (DBusSocket **fds,
DBusError *error)
{
#ifdef HAVE_SYSTEMD
int r, n;
int fd;
DBusSocket *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 (DBusSocket, 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_fd_nonblocking (fd, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
goto fail;
}
new_fds[fd - SD_LISTEN_FDS_START].fd = 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;
#else
dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
"dbus was compiled without systemd support");
return -1;
#endif
}
/* Convert an error code from getaddrinfo() or getnameinfo() into
* a D-Bus error name. */
static const char *
_dbus_error_from_gai (int gai_res,
int saved_errno)
{
switch (gai_res)
{
#ifdef EAI_FAMILY
case EAI_FAMILY:
/* ai_family not supported (at all) */
return DBUS_ERROR_NOT_SUPPORTED;
#endif
#ifdef EAI_SOCKTYPE
case EAI_SOCKTYPE:
/* ai_socktype not supported (at all) */
return DBUS_ERROR_NOT_SUPPORTED;
#endif
#ifdef EAI_MEMORY
case EAI_MEMORY:
/* Out of memory */
return DBUS_ERROR_NO_MEMORY;
#endif
#ifdef EAI_SYSTEM
case EAI_SYSTEM:
/* Unspecified system error, details in errno */
return _dbus_error_from_errno (saved_errno);
#endif
case 0:
/* It succeeded, but we didn't get any addresses? */
return DBUS_ERROR_FAILED;
/* EAI_AGAIN: Transient failure */
/* EAI_BADFLAGS: invalid ai_flags (programming error) */
/* EAI_FAIL: Non-recoverable failure */
/* EAI_NODATA: host exists but has no addresses */
/* EAI_NONAME: host does not exist */
/* EAI_OVERFLOW: argument buffer overflow */
/* EAI_SERVICE: service not available for specified socket
* type (we should never see this because we use numeric
* ports) */
default:
return DBUS_ERROR_FAILED;
}
}
DBusSocket
_dbus_connect_tcp_socket (const char *host,
const char *port,
const char *family,
DBusError *error)
{
return _dbus_connect_tcp_socket_with_nonce (host, port, family, (const char*)NULL, error);
}
DBusSocket
_dbus_connect_tcp_socket_with_nonce (const char *host,
const char *port,
const char *family,
const char *noncefile,
DBusError *error)
{
int saved_errno = 0;
DBusList *connect_errors = NULL;
DBusSocket fd = DBUS_SOCKET_INIT;
int res;
struct addrinfo hints;
struct addrinfo *ai = NULL;
const struct addrinfo *tmp;
DBusError *connect_error;
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
_DBUS_ZERO (hints);
if (!family)
hints.ai_family = AF_UNSPEC;
else if (!strcmp(family, "ipv4"))
hints.ai_family = AF_INET;
else if (!strcmp(family, "ipv6"))
hints.ai_family = AF_INET6;
else
{
dbus_set_error (error,
DBUS_ERROR_BAD_ADDRESS,
"Unknown address family %s", family);
return _dbus_socket_get_invalid ();
}
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
if ((res = getaddrinfo(host, port, &hints, &ai)) != 0)
{
dbus_set_error (error,
_dbus_error_from_gai (res, errno),
"Failed to lookup host/port: \"%s:%s\": %s (%d)",
host, port, gai_strerror(res), res);
goto out;
}
tmp = ai;
while (tmp)
{
if (!_dbus_open_socket (&fd.fd, tmp->ai_family, SOCK_STREAM, 0, error))
{
_DBUS_ASSERT_ERROR_IS_SET(error);
_dbus_socket_invalidate (&fd);
goto out;
}
_DBUS_ASSERT_ERROR_IS_CLEAR(error);
if (connect (fd.fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
{
saved_errno = errno;
_dbus_close_socket (&fd, NULL);
connect_error = dbus_new0 (DBusError, 1);
if (connect_error == NULL)
{
_DBUS_SET_OOM (error);
goto out;
}
dbus_error_init (connect_error);
_dbus_set_error_with_inet_sockaddr (connect_error,
tmp->ai_addr, tmp->ai_addrlen,
"Failed to connect to socket",
saved_errno);
if (!_dbus_list_append (&connect_errors, connect_error))
{
dbus_error_free (connect_error);
dbus_free (connect_error);
_DBUS_SET_OOM (error);
goto out;
}
tmp = tmp->ai_next;
continue;
}
break;
}
if (!_dbus_socket_is_valid (fd))
{
_dbus_combine_tcp_errors (&connect_errors, "Failed to connect",
host, port, error);
goto out;
}
if (noncefile != NULL)
{
DBusString noncefileStr;
dbus_bool_t ret;
_dbus_string_init_const (&noncefileStr, noncefile);
ret = _dbus_send_nonce (fd, &noncefileStr, error);
if (!ret)
{
_dbus_close_socket (&fd, NULL);
goto out;
}
}
if (!_dbus_set_fd_nonblocking (fd.fd, error))
{
_dbus_close_socket (&fd, NULL);
goto out;
}
out:
if (ai != NULL)
freeaddrinfo (ai);
while ((connect_error = _dbus_list_pop_first (&connect_errors)))
{
dbus_error_free (connect_error);
dbus_free (connect_error);
}
return fd;
}
int
_dbus_listen_tcp_socket (const char *host,
const char *port,
const char *family,
DBusString *retport,
const char **retfamily,
DBusSocket **fds_p,
DBusError *error)
{
int saved_errno;
int nlisten_fd = 0, res, i;
DBusList *bind_errors = NULL;
DBusError *bind_error = NULL;
DBusSocket *listen_fd = NULL;
struct addrinfo hints;
struct addrinfo *ai, *tmp;
unsigned int reuseaddr;
dbus_bool_t have_ipv4 = FALSE;
dbus_bool_t have_ipv6 = FALSE;
*fds_p = NULL;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_DBUS_ZERO (hints);
if (!family)
hints.ai_family = AF_UNSPEC;
else if (!strcmp(family, "ipv4"))
hints.ai_family = AF_INET;
else if (!strcmp(family, "ipv6"))
hints.ai_family = AF_INET6;
else
{
dbus_set_error (error,
DBUS_ERROR_BAD_ADDRESS,
"Unknown address family %s", family);
return -1;
}
hints.ai_protocol = IPPROTO_TCP;
hints.ai_socktype = SOCK_STREAM;
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_gai (res, errno),
"Failed to lookup host/port: \"%s:%s\": %s (%d)",
host ? host : "*", port ? port : "0", gai_strerror(res), res);
goto failed;
}
tmp = ai;
while (tmp)
{
int fd = -1, tcp_nodelay_on;
DBusSocket *newlisten_fd;
if (!_dbus_open_socket (&fd, tmp->ai_family, SOCK_STREAM, 0, error))
{
_DBUS_ASSERT_ERROR_IS_SET(error);
goto failed;
}
_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 ? port : "0", _dbus_strerror (errno));
}
/* Nagle's algorithm imposes a huge delay on the initial messages
going over TCP. */
tcp_nodelay_on = 1;
if (setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay_on, sizeof (tcp_nodelay_on)) == -1)
{
_dbus_warn ("Failed to set TCP_NODELAY socket option \"%s:%s\": %s",
host ? host : "*", port ? port : "0", _dbus_strerror (errno));
}
if (bind (fd, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) < 0)
{
saved_errno = errno;
_dbus_close(fd, NULL);
/*
* We don't treat this as a fatal error, because there might be
* other addresses that we can listen on. In particular:
*
* - If saved_errno is EADDRINUSE after we
* "goto redo_lookup_with_port" after binding a port on one of the
* possible addresses, we will try to bind that same port on
* every address, including the same address again for a second
* time, which will fail with EADDRINUSE.
*
* - If saved_errno is EADDRINUSE, it might be because binding to
* an IPv6 address implicitly binds to a corresponding IPv4
* address or vice versa (e.g. Linux with bindv6only=0).
*
* - If saved_errno is EADDRNOTAVAIL when we asked for family
* AF_UNSPEC, it might be because IPv6 is disabled for this
* particular interface (e.g. Linux with
* /proc/sys/net/ipv6/conf/lo/disable_ipv6).
*/
bind_error = dbus_new0 (DBusError, 1);
if (bind_error == NULL)
{
_DBUS_SET_OOM (error);
goto failed;
}
dbus_error_init (bind_error);
_dbus_set_error_with_inet_sockaddr (bind_error, tmp->ai_addr, tmp->ai_addrlen,
"Failed to bind socket",
saved_errno);
if (!_dbus_list_append (&bind_errors, bind_error))
{
dbus_error_free (bind_error);
dbus_free (bind_error);
_DBUS_SET_OOM (error);
goto failed;
}
/* Try the next address, maybe it will work better */
tmp = tmp->ai_next;
continue;
}
if (listen (fd, 30 /* backlog */) < 0)
{
saved_errno = errno;
_dbus_close (fd, NULL);
_dbus_set_error_with_inet_sockaddr (error, tmp->ai_addr, tmp->ai_addrlen,
"Failed to listen on socket",
saved_errno);
goto failed;
}
newlisten_fd = dbus_realloc(listen_fd, sizeof(DBusSocket)*(nlisten_fd+1));
if (!newlisten_fd)
{
_dbus_close (fd, NULL);
dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
"Failed to allocate file handle array");
goto failed;
}
listen_fd = newlisten_fd;
listen_fd[nlisten_fd].fd = fd;
nlisten_fd++;
if (tmp->ai_addr->sa_family == AF_INET)
have_ipv4 = TRUE;
else if (tmp->ai_addr->sa_family == AF_INET6)
have_ipv6 = TRUE;
if (!_dbus_string_get_length(retport))
{
/* If the user didn't specify a port, or used 0, then
the kernel chooses a port. After the first address
is bound to, we need to force all remaining addresses
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);
result = getsockname(fd, (struct sockaddr*) &addr, &addrlen);
if (result == -1)
{
saved_errno = errno;
dbus_set_error (error, _dbus_error_from_errno (saved_errno),
"Failed to retrieve socket name for \"%s:%s\": %s",
host ? host : "*", port ? port : "0", _dbus_strerror (saved_errno));
goto failed;
}
if ((res = getnameinfo ((struct sockaddr*)&addr, addrlen, NULL, 0,
portbuf, sizeof(portbuf),
NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
{
saved_errno = errno;
dbus_set_error (error, _dbus_error_from_gai (res, saved_errno),
"Failed to resolve port \"%s:%s\": %s (%d)",
host ? host : "*", port ? port : "0", gai_strerror(res), res);
goto failed;
}
if (!_dbus_string_append(retport, portbuf))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed;
}
/* Release current address list & redo lookup */
port = _dbus_string_get_const_data(retport);
freeaddrinfo(ai);
goto redo_lookup_with_port;
}
else
{
if (!_dbus_string_append(retport, port))
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed;
}
}
}
tmp = tmp->ai_next;
}
freeaddrinfo(ai);
ai = NULL;
if (!nlisten_fd)
{
_dbus_combine_tcp_errors (&bind_errors, "Failed to bind", host,
port, error);
goto failed;
}
if (have_ipv4 && !have_ipv6)
*retfamily = "ipv4";
else if (!have_ipv4 && have_ipv6)
*retfamily = "ipv6";
for (i = 0 ; i < nlisten_fd ; i++)
{
if (!_dbus_set_fd_nonblocking (listen_fd[i].fd, error))
{
goto failed;
}
}
*fds_p = listen_fd;
/* This list might be non-empty even on success, because we might be
* ignoring EADDRINUSE or EADDRNOTAVAIL */
while ((bind_error = _dbus_list_pop_first (&bind_errors)))
{
dbus_error_free (bind_error);
dbus_free (bind_error);
}
return nlisten_fd;
failed:
if (ai)
freeaddrinfo(ai);
for (i = 0 ; i < nlisten_fd ; i++)
_dbus_close(listen_fd[i].fd, NULL);
while ((bind_error = _dbus_list_pop_first (&bind_errors)))
{
dbus_error_free (bind_error);
dbus_free (bind_error);
}
dbus_free(listen_fd);
return -1;
}
static dbus_bool_t
write_credentials_byte (int server_fd,
DBusError *error)
{
int bytes_written;
char buf[1] = { '\0' };
#if defined(HAVE_CMSGCRED)
union {
struct cmsghdr hdr;
char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
} cmsg;
struct iovec iov;
struct msghdr msg;
iov.iov_base = buf;
iov.iov_len = 1;
_DBUS_ZERO(msg);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
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 HAVE_DECL_MSG_NOSIGNAL
|MSG_NOSIGNAL
#endif
);
/* If we HAVE_CMSGCRED, the OS still might not let us sendmsg()
* with a SOL_SOCKET/SCM_CREDS message - for instance, FreeBSD
* only allows that on AF_UNIX. Try just doing a send() instead. */
if (bytes_written < 0 && errno == EINVAL)
#endif
{
bytes_written = send (server_fd, buf, 1, 0
#if HAVE_DECL_MSG_NOSIGNAL
|MSG_NOSIGNAL
#endif
);
}
if (bytes_written < 0 && errno == EINTR)
goto again;
if (bytes_written < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to write credentials byte: %s",
_dbus_strerror (errno));
return FALSE;
}
else if (bytes_written == 0)
{
dbus_set_error (error, DBUS_ERROR_IO_ERROR,
"wrote zero bytes writing credentials byte");
return FALSE;
}
else
{
_dbus_assert (bytes_written == 1);
_dbus_verbose ("wrote credentials byte\n");
return TRUE;
}
}
/* return FALSE on OOM, TRUE otherwise, even if no groups were found */
static dbus_bool_t
add_groups_to_credentials (int client_fd,
DBusCredentials *credentials,
dbus_gid_t primary)
{
#if defined(__linux__) && defined(SO_PEERGROUPS)
_DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
/* This function assumes socklen_t is unsigned, which is true on Linux */
_DBUS_STATIC_ASSERT (((socklen_t) -1) > 0);
gid_t *buf = NULL;
socklen_t len = 1024;
dbus_bool_t oom = FALSE;
/* libdbus has a different representation of group IDs just to annoy you */
dbus_gid_t *converted_gids = NULL;
dbus_bool_t need_primary = TRUE;
size_t n_gids;
size_t i;
n_gids = ((size_t) len) / sizeof (gid_t);
buf = dbus_new (gid_t, n_gids);
if (buf == NULL)
return FALSE;
while (getsockopt (client_fd, SOL_SOCKET, SO_PEERGROUPS, buf, &len) < 0)
{
int e = errno;
gid_t *replacement;
_dbus_verbose ("getsockopt failed with %s, len now %lu\n",
_dbus_strerror (e), (unsigned long) len);
if (e != ERANGE || (size_t) len <= n_gids * sizeof (gid_t))
{
_dbus_verbose ("Failed to getsockopt(SO_PEERGROUPS): %s\n",
_dbus_strerror (e));
goto out;
}
/* If not enough space, len is updated to be enough.
* Try again with a large enough buffer. */
n_gids = ((size_t) len) / sizeof (gid_t);
replacement = dbus_realloc (buf, len);
if (replacement == NULL)
{
oom = TRUE;
goto out;
}
buf = replacement;
_dbus_verbose ("will try again with %lu\n", (unsigned long) len);
}
if (len > n_gids * sizeof (gid_t))
{
_dbus_verbose ("%lu > %zu", (unsigned long) len, n_gids * sizeof (gid_t));
_dbus_assert_not_reached ("getsockopt(SO_PEERGROUPS) overflowed");
}
if (len % sizeof (gid_t) != 0)
{
_dbus_verbose ("getsockopt(SO_PEERGROUPS) did not return an "
"integer multiple of sizeof(gid_t): %lu should be "
"divisible by %zu",
(unsigned long) len, sizeof (gid_t));
goto out;
}
/* Allocate an extra space for the primary group ID */
n_gids = ((size_t) len) / sizeof (gid_t);
/* If n_gids is less than this, then (n_gids + 1) certainly doesn't
* overflow, and neither does multiplying that by sizeof(dbus_gid_t).
* This is using _DBUS_INT32_MAX as a conservative lower bound for
* the maximum size_t. */
if (n_gids >= (_DBUS_INT32_MAX / sizeof (dbus_gid_t)) - 1)
{
_dbus_verbose ("getsockopt(SO_PEERGROUPS) returned a huge number "
"of groups (%lu bytes), ignoring",
(unsigned long) len);
goto out;
}
converted_gids = dbus_new (dbus_gid_t, n_gids + 1);
if (converted_gids == NULL)
{
oom = TRUE;
goto out;
}
for (i = 0; i < n_gids; i++)
{
converted_gids[i] = (dbus_gid_t) buf[i];
if (converted_gids[i] == primary)
need_primary = FALSE;
}
if (need_primary && primary != DBUS_GID_UNSET)
{
converted_gids[n_gids] = primary;
n_gids++;
}
_dbus_credentials_take_unix_gids (credentials, converted_gids, n_gids);
out:
dbus_free (buf);
return !oom;
#else
/* no error */
return TRUE;
#endif
}
/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */
static dbus_bool_t
add_linux_security_label_to_credentials (int client_fd,
DBusCredentials *credentials)
{
#if defined(__linux__) && defined(SO_PEERSEC)
DBusString buf;
socklen_t len = 1024;
dbus_bool_t oom = FALSE;
if (!_dbus_string_init_preallocated (&buf, len) ||
!_dbus_string_set_length (&buf, len))
return FALSE;
while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC,
_dbus_string_get_data (&buf), &len) < 0)
{
int e = errno;
_dbus_verbose ("getsockopt failed with %s, len now %lu\n",
_dbus_strerror (e), (unsigned long) len);
if (e != ERANGE || len <= _dbus_string_get_length_uint (&buf))
{
_dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n",
_dbus_strerror (e));
goto out;
}
/* If not enough space, len is updated to be enough.
* Try again with a large enough buffer. */
if (!_dbus_string_set_length (&buf, len))
{
oom = TRUE;
goto out;
}
_dbus_verbose ("will try again with %lu\n", (unsigned long) len);
}
if (len <= 0)
{
_dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n",
(unsigned long) len);
goto out;
}
if (len > _dbus_string_get_length_uint (&buf))
{
_dbus_verbose ("%lu > %u", (unsigned long) len,
_dbus_string_get_length_uint (&buf));
_dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed");
}
if (_dbus_string_get_byte (&buf, len - 1) == 0)
{
/* the kernel included the trailing \0 in its count,
* but DBusString always has an extra \0 after the data anyway */
_dbus_verbose ("subtracting trailing \\0\n");
len--;
}
if (!_dbus_string_set_length (&buf, len))
{
_dbus_assert_not_reached ("shortening string should not lead to OOM");
oom = TRUE;
goto out;
}
if (strlen (_dbus_string_get_const_data (&buf)) != len)
{
/* LSM people on the linux-security-module@ mailing list say this
* should never happen: the label should be a bytestring with
* an optional trailing \0 */
_dbus_verbose ("security label from kernel had an embedded \\0, "
"ignoring it\n");
goto out;
}
_dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n",
(unsigned long) len,
_dbus_string_get_const_data (&buf));
if (!_dbus_credentials_add_linux_security_label (credentials,
_dbus_string_get_const_data (&buf)))
{
oom = TRUE;
goto out;
}
out:
_dbus_string_free (&buf);
return !oom;
#else
/* no error */
return TRUE;
#endif
}
dbus_bool_t
_dbus_read_credentials_socket (DBusSocket client_fd,
DBusCredentials *credentials,
DBusError *error)
{
struct msghdr msg;
struct iovec iov;
char buf;
dbus_uid_t uid_read;
dbus_gid_t primary_gid_read;
dbus_pid_t pid_read;
int bytes_read;
int pid_fd_read;
#ifdef HAVE_CMSGCRED
union {
struct cmsghdr hdr;
char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
} cmsg;
#endif
/* The POSIX spec certainly doesn't promise this, but
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
_DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
_DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
_DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
uid_read = DBUS_UID_UNSET;
primary_gid_read = DBUS_GID_UNSET;
pid_read = DBUS_PID_UNSET;
pid_fd_read = -1;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_credentials_clear (credentials);
iov.iov_base = &buf;
iov.iov_len = 1;
_DBUS_ZERO(msg);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
#if defined(HAVE_CMSGCRED)
_DBUS_ZERO(cmsg);
msg.msg_control = (caddr_t) &cmsg;
msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
#endif
again:
bytes_read = recvmsg (client_fd.fd, &msg, 0);
if (bytes_read < 0)
{
if (errno == EINTR)
goto again;
/* EAGAIN or EWOULDBLOCK would be unexpected here since we would
* 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));
return FALSE;
}
else if (bytes_read == 0)
{
/* this should not happen unless we are using recvmsg wrong,
* so is essentially here for paranoia
*/
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to read credentials byte (zero-length read)");
return FALSE;
}
else if (buf != '\0')
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Credentials byte was not nul");
return FALSE;
}
_dbus_verbose ("read credentials byte\n");
{
#ifdef SO_PEERCRED
/* Supported by at least Linux and OpenBSD, with minor differences.
*
* This mechanism passes the process ID through and does not require
* the peer's cooperation, so we prefer it over all others. Notably,
* Linux also supports SCM_CREDENTIALS, which is similar to FreeBSD
* SCM_CREDS; it's implemented in GIO, but we don't use it in dbus at all,
* because this is much less fragile.
*/
#ifdef __OpenBSD__
struct sockpeercred cr;
#else
struct ucred cr;
#endif
socklen_t cr_len = sizeof (cr);
if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0)
{
_dbus_verbose ("Failed to getsockopt(SO_PEERCRED): %s\n",
_dbus_strerror (errno));
}
else if (cr_len != sizeof (cr))
{
_dbus_verbose ("Failed to getsockopt(SO_PEERCRED), returned %d bytes, expected %d\n",
cr_len, (int) sizeof (cr));
}
else
{
if (cr.pid != 0)
pid_read = cr.pid;
if (cr.uid != (uid_t)-1)
uid_read = cr.uid;
#ifdef __linux__
/* Do other platforms have cr.gid? (Not that it really matters,
* because the gid is useless to us unless we know the complete
* group vector, which we only know on Linux.) */
if (cr.gid != (gid_t)-1)
primary_gid_read = cr.gid;
#endif
}
#ifdef SO_PEERPIDFD
/* If we have SO_PEERCRED we might also have SO_PEERPIDFD, which
* allows to pin the process ID, and is available on Linux since v6.5. */
cr_len = sizeof (int);
if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERPIDFD, &pid_fd_read, &cr_len) != 0)
{
_dbus_verbose ("Failed to getsockopt(SO_PEERPIDFD): %s\n",
_dbus_strerror (errno));
}
else if (cr_len != sizeof (int))
{
_dbus_verbose ("Failed to getsockopt(SO_PEERPIDFD), returned %d bytes, expected %d\n",
cr_len, (int) sizeof (int));
}
#endif
#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID)
/* Another variant of the above - used on NetBSD
*/
struct unpcbid cr;
socklen_t cr_len = sizeof (cr);
if (getsockopt (client_fd.fd, 0, LOCAL_PEEREID, &cr, &cr_len) != 0)
{
_dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID): %s\n",
_dbus_strerror (errno));
}
else if (cr_len != sizeof (cr))
{
_dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID), returned %d bytes, expected %d\n",
cr_len, (int) sizeof (cr));
}
else
{
pid_read = cr.unp_pid;
uid_read = cr.unp_euid;
}
#elif defined(HAVE_CMSGCRED)
/* We only check for HAVE_CMSGCRED, but we're really assuming that the
* presence of that struct implies SCM_CREDS. Supported by at least
* FreeBSD and DragonflyBSD.
*
* This mechanism requires the peer to help us (it has to send us a
* SCM_CREDS message) but it does pass the process ID through,
* which makes it better than getpeereid().
*/
struct cmsgcred *cred;
struct cmsghdr *cmsgp;
for (cmsgp = CMSG_FIRSTHDR (&msg);
cmsgp != NULL;
cmsgp = CMSG_NXTHDR (&msg, cmsgp))
{
if (cmsgp->cmsg_type == SCM_CREDS &&
cmsgp->cmsg_level == SOL_SOCKET &&
cmsgp->cmsg_len >= CMSG_LEN (sizeof (struct cmsgcred)))
{
cred = (struct cmsgcred *) (void *) CMSG_DATA (cmsgp);
pid_read = cred->cmcred_pid;
uid_read = cred->cmcred_euid;
break;
}
}
#elif defined(HAVE_GETPEERUCRED)
/* Supported in at least Solaris >= 10. It should probably be higher
* up this list, because it carries the pid and we use this code path
* for audit data. */
ucred_t * ucred = NULL;
if (getpeerucred (client_fd.fd, &ucred) == 0)
{
#ifdef HAVE_ADT
adt_session_data_t *adth = NULL;
#endif
pid_read = ucred_getpid (ucred);
uid_read = ucred_geteuid (ucred);
#ifdef HAVE_ADT
/* generate audit session data based on socket ucred */
if (adt_start_session (&adth, NULL, 0) || (adth == NULL))
{
_dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno));
}
else
{
if (adt_set_from_ucred (adth, ucred, ADT_NEW))
{
_dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno));
}
else
{
adt_export_data_t *data = NULL;
size_t size = adt_export_session_data (adth, &data);
if (size <= 0)
{
_dbus_verbose ("Failed to adt_export_session_data(): %s\n", _dbus_strerror (errno));
}
else
{
_dbus_credentials_add_adt_audit_data (credentials, data, size);
free (data);
}
}
(void) adt_end_session (adth);
}
#endif /* HAVE_ADT */
}
else
{
_dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
}
if (ucred != NULL)
ucred_free (ucred);
/* ----------------------------------------------------------------
* When adding new mechanisms, please add them above this point
* if they support passing the process ID through, or below if not.
* ---------------------------------------------------------------- */
#elif defined(HAVE_GETPEEREID)
/* getpeereid() originates from D.J. Bernstein and is fairly
* widely-supported. According to a web search, it might be present in
* any/all of:
*
* - AIX?
* - Blackberry?
* - Cygwin
* - FreeBSD 4.6+ (but we prefer SCM_CREDS: it carries the pid)
* - Mac OS X
* - Minix 3.1.8+
* - MirBSD?
* - NetBSD 5.0+ (but LOCAL_PEEREID would be better: it carries the pid)
* - OpenBSD 3.0+ (but we prefer SO_PEERCRED: it carries the pid)
* - QNX?
*/
uid_t euid;
gid_t egid;
if (getpeereid (client_fd.fd, &euid, &egid) == 0)
{
uid_read = euid;
}
else
{
_dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
}
#else /* no supported mechanism */
#warning Socket credentials not supported on this Unix OS
#warning Please tell https://gitlab.freedesktop.org/dbus/dbus/-/issues/new
/* Please add other operating systems known to support at least one of
* the mechanisms above to this list, keeping alphabetical order.
* Everything not in this list is best-effort.
*/
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
defined(__linux__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
# error Credentials passing not working on this OS is a regression!
#endif
_dbus_verbose ("Socket credentials not supported on this OS\n");
#endif
}
_dbus_verbose ("Credentials:"
" pid "DBUS_PID_FORMAT
" uid "DBUS_UID_FORMAT
"\n",
pid_read,
uid_read);
/* Assign this first, so we don't have to close it manually in case one of
* the next steps fails. */
if (pid_fd_read >= 0)
_dbus_credentials_take_pid_fd (credentials, pid_fd_read);
if (pid_read != DBUS_PID_UNSET)
{
if (!_dbus_credentials_add_pid (credentials, pid_read))
{
_DBUS_SET_OOM (error);
return FALSE;
}
}
if (uid_read != DBUS_UID_UNSET)
{
if (!_dbus_credentials_add_unix_uid (credentials, uid_read))
{
_DBUS_SET_OOM (error);
return FALSE;
}
}
if (!add_linux_security_label_to_credentials (client_fd.fd, credentials))
{
_DBUS_SET_OOM (error);
return FALSE;
}
/* We don't put any groups in the credentials unless we can put them
* all there. */
if (!add_groups_to_credentials (client_fd.fd, credentials, primary_gid_read))
{
_DBUS_SET_OOM (error);
return FALSE;
}
return TRUE;
}
dbus_bool_t
_dbus_send_credentials_socket (DBusSocket server_fd,
DBusError *error)
{
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (write_credentials_byte (server_fd.fd, error))
return TRUE;
else
return FALSE;
}
DBusSocket
_dbus_accept (DBusSocket listen_fd)
{
DBusSocket client_fd;
struct sockaddr addr;
socklen_t addrlen;
#ifdef HAVE_ACCEPT4
dbus_bool_t cloexec_done;
#endif
addrlen = sizeof (addr);
retry:
#ifdef HAVE_ACCEPT4
/*
* At compile-time, we assume that if accept4() is available in
* libc headers, SOCK_CLOEXEC is too. At runtime, it is still
* not necessarily true that either is supported by the running kernel.
*/
client_fd.fd = accept4 (listen_fd.fd, &addr, &addrlen, SOCK_CLOEXEC);
cloexec_done = client_fd.fd >= 0;
if (client_fd.fd < 0 && (errno == ENOSYS || errno == EINVAL))
#endif
{
client_fd.fd = accept (listen_fd.fd, &addr, &addrlen);
}
if (client_fd.fd < 0)
{
if (errno == EINTR)
goto retry;
}
_dbus_verbose ("client fd %d accepted\n", client_fd.fd);
#ifdef HAVE_ACCEPT4
if (!cloexec_done)
#endif
{
_dbus_fd_set_close_on_exec(client_fd.fd);
}
return client_fd;
}
dbus_bool_t
_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 (sb.st_uid != geteuid ())
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"%s directory is owned by user %lu, not %lu",
directory,
(unsigned long) sb.st_uid,
(unsigned long) geteuid ());
return FALSE;
}
if ((S_IROTH & sb.st_mode) || (S_IWOTH & sb.st_mode) ||
(S_IRGRP & sb.st_mode) || (S_IWGRP & sb.st_mode))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"%s directory is not private to the user", directory);
return FALSE;
}
return TRUE;
}
static dbus_bool_t
fill_user_info_from_passwd (struct passwd *p,
DBusUserInfo *info,
DBusError *error)
{
_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)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
return TRUE;
}
static dbus_bool_t
fill_user_info (DBusUserInfo *info,
dbus_uid_t uid,
const DBusString *username,
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);
info->uid = DBUS_UID_UNSET;
info->primary_gid = DBUS_GID_UNSET;
info->group_ids = NULL;
info->n_group_ids = 0;
info->username = NULL;
info->homedir = NULL;
if (username != NULL)
username_c = _dbus_string_get_const_data (username);
else
username_c = NULL;
/* For now assuming that the getpwnam() and getpwuid() flavors
* are always symmetrical, if not we have to add more configure
* checks
*/
{
struct passwd *p;
char *buf = NULL;
int result;
#ifdef HAVE_GETPWNAM_R
size_t buflen;
struct passwd p_str;
/* retrieve maximum needed size for buf */
buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
/* sysconf actually returns a long, but everything else expects size_t,
* so just recast here.
* https://bugs.freedesktop.org/show_bug.cgi?id=17061
*/
if ((long) buflen <= 0)
buflen = 1024;
result = -1;
while (1)
{
buf = dbus_malloc (buflen);
if (buf == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
return FALSE;
}
p = NULL;
if (uid != DBUS_UID_UNSET)
result = getpwuid_r (uid, &p_str, buf, buflen,
&p);
else
result = getpwnam_r (username_c, &p_str, buf, buflen,
&p);
//Try a bigger buffer if ERANGE was returned
if (result == ERANGE && buflen < 512 * 1024)
{
dbus_free (buf);
buflen *= 2;
}
else
{
break;
}
}
/* There are three possibilities:
* - an error: result is a nonzero error code, p should be NULL
* - name or uid not found: result is 0, p is NULL
* - success: result is 0, p should be &p_str
*
* Ensure that in all failure cases, p is set to NULL, matching the
* getpwuid/getpwnam interface. */
if (result != 0 || p != &p_str)
p = NULL;
#else /* ! HAVE_GETPWNAM_R */
/* I guess we're screwed on thread safety here */
#warning getpwnam_r() not available, please report this to the dbus maintainers with details of your OS
/* It is unspecified whether "failed to find" counts as an error,
* or whether it's reported as p == NULL without touching errno.
* Reset errno so we can distinguish. */
errno = 0;
if (uid != DBUS_UID_UNSET)
p = getpwuid (uid);
else
p = getpwnam (username_c);
/* Always initialized, but only meaningful if p is NULL */
result = errno;
#endif /* ! HAVE_GETPWNAM_R */
if (p != NULL)
{
if (!fill_user_info_from_passwd (p, info, error))
{
dbus_free (buf);
return FALSE;
}
dbus_free (buf);
}
else
{
DBusError local_error = DBUS_ERROR_INIT;
const char *error_str;
if (result == 0)
error_str = "not found";
else
error_str = _dbus_strerror (result);
if (uid != DBUS_UID_UNSET)
dbus_set_error (&local_error, _dbus_error_from_errno (result),
"Looking up user ID " DBUS_UID_FORMAT ": %s",
uid, error_str);
else
dbus_set_error (&local_error, _dbus_error_from_errno (result),
"Looking up user \"%s\": %s",
username_c ? username_c : "???", error_str);
_dbus_verbose ("%s", local_error.message);
dbus_move_error (&local_error, error);
dbus_free (buf);
return FALSE;
}
}
/* Fill this in so we can use it to get groups */
username_c = info->username;
#ifdef HAVE_GETGROUPLIST
{
gid_t *buf;
int buf_count;
int i;
int initial_buf_count;
initial_buf_count = 17;
buf_count = initial_buf_count;
buf = dbus_new (gid_t, buf_count);
if (buf == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed;
}
if (getgrouplist (username_c,
info->primary_gid,
buf, &buf_count) < 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);
dbus_free (buf);
goto failed;
}
buf = new;
errno = 0;
if (getgrouplist (username_c, info->primary_gid, buf, &buf_count) < 0)
{
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;
}
}
}
info->group_ids = dbus_new (dbus_gid_t, buf_count);
if (info->group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
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 */
{
/* We just get the one group ID */
info->group_ids = dbus_new (dbus_gid_t, 1);
if (info->group_ids == NULL)
{
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
goto failed;
}
info->n_group_ids = 1;
(info->group_ids)[0] = info->primary_gid;
}
#endif /* HAVE_GETGROUPLIST */
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
return TRUE;
failed:
_DBUS_ASSERT_ERROR_IS_SET (error);
return FALSE;
}
dbus_bool_t
_dbus_user_info_fill (DBusUserInfo *info,
const DBusString *username,
DBusError *error)
{
return fill_user_info (info, DBUS_UID_UNSET,
username, error);
}
dbus_bool_t
_dbus_user_info_fill_uid (DBusUserInfo *info,
dbus_uid_t uid,
DBusError *error)
{
return fill_user_info (info, uid,
NULL, error);
}
dbus_bool_t
_dbus_credentials_add_from_current_process (DBusCredentials *credentials)
{
dbus_pid_t pid = _dbus_getpid ();
/* The POSIX spec certainly doesn't promise this, but
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
_DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
_DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
_DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
#if HAVE_DECL_SYS_PIDFD_OPEN
/* Normally this syscall would have a race condition, but we can trust
* that our own process isn't going to exit, so the pid won't get reused. */
int pid_fd = (int) syscall (SYS_pidfd_open, pid, 0);
if (pid_fd >= 0)
_dbus_credentials_take_pid_fd (credentials, pid_fd);
#endif
if (!_dbus_credentials_add_pid (credentials, pid))
return FALSE;
if (!_dbus_credentials_add_unix_uid(credentials, _dbus_geteuid()))
return FALSE;
return TRUE;
}
dbus_pid_t
_dbus_resolve_pid_fd (int pid_fd)
{
#ifdef __linux__
DBusError error = DBUS_ERROR_INIT;
DBusString content = _DBUS_STRING_INIT_INVALID;
DBusString filename = _DBUS_STRING_INIT_INVALID;
dbus_pid_t result = DBUS_PID_UNSET;
int pid_index;
if (pid_fd < 0)
goto out;
if (!_dbus_string_init (&content))
goto out;
if (!_dbus_string_init (&filename))
goto out;
if (!_dbus_string_append_printf (&filename, "/proc/self/fdinfo/%d", pid_fd))
goto out;
if (!_dbus_file_get_contents (&content, &filename, &error))
{
_dbus_verbose ("Cannot read '/proc/self/fdinfo/%d', unable to resolve PID, %s: %s\n",
pid_fd, error.name, error.message);
goto out;
}
/* Ensure we are not reading PPid, either it's the first line of the file or
* there's a newline before it. */
if (!_dbus_string_find (&content, 0, "Pid:", &pid_index) ||
(pid_index > 0 && _dbus_string_get_byte (&content, pid_index - 1) != '\n'))
{
_dbus_verbose ("Cannot find 'Pid:' in '/proc/self/fdinfo/%d', unable to resolve PID\n",
pid_fd);
goto out;
}
if (!_dbus_string_parse_uint (&content, pid_index + strlen ("Pid:"), &result, NULL))
{
_dbus_verbose ("Cannot parse 'Pid:' from '/proc/self/fdinfo/%d', unable to resolve PID\n",
pid_fd);
goto out;
}
out:
_dbus_string_free (&content);
_dbus_string_free (&filename);
dbus_error_free (&error);
if (result <= 0)
return DBUS_PID_UNSET;
return result;
#else
return DBUS_PID_UNSET;
#endif
}
dbus_bool_t
_dbus_append_user_from_current_process (DBusString *str)
{
return _dbus_string_append_printf (str, DBUS_UID_FORMAT, _dbus_geteuid ());
}
dbus_pid_t
_dbus_getpid (void)
{
return getpid ();
}
dbus_uid_t
_dbus_getuid (void)
{
return getuid ();
}
dbus_uid_t
_dbus_geteuid (void)
{
return geteuid ();
}
unsigned long
_dbus_pid_for_log (void)
{
return getpid ();
}
#if !defined(HAVE_STDATOMIC_H) && !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
dbus_int32_t
_dbus_atomic_inc (DBusAtomic *atomic)
{
#ifdef HAVE_STDATOMIC_H
/* Atomic version of "old = *atomic; *atomic += 1; return old" */
return atomic_fetch_add (&atomic->value, 1);
#elif DBUS_USE_SYNC
/* Atomic version of "*atomic += 1; return *atomic - 1" */
return __sync_add_and_fetch(&atomic->value, 1)-1;
#else
dbus_int32_t res;
pthread_mutex_lock (&atomic_mutex);
res = atomic->value;
atomic->value += 1;
pthread_mutex_unlock (&atomic_mutex);
return res;
#endif
}
dbus_int32_t
_dbus_atomic_dec (DBusAtomic *atomic)
{
#ifdef HAVE_STDATOMIC_H
/* Atomic version of "old = *atomic; *atomic -= 1; return old" */
return atomic_fetch_sub (&atomic->value, 1);
#elif DBUS_USE_SYNC
/* Atomic version of "*atomic -= 1; return *atomic + 1" */
return __sync_sub_and_fetch(&atomic->value, 1)+1;
#else
dbus_int32_t res;
pthread_mutex_lock (&atomic_mutex);
res = atomic->value;
atomic->value -= 1;
pthread_mutex_unlock (&atomic_mutex);
return res;
#endif
}
dbus_int32_t
_dbus_atomic_get (DBusAtomic *atomic)
{
#ifdef HAVE_STDATOMIC_H
/* Atomic version of "return *atomic" */
return atomic_load (&atomic->value);
#elif 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
}
void
_dbus_atomic_set_zero (DBusAtomic *atomic)
{
#ifdef HAVE_STDATOMIC_H
/* Atomic version of "*atomic = 0" */
atomic_store (&atomic->value, 0);
#elif DBUS_USE_SYNC
/* Atomic version of "*atomic &= 0; return *atomic" */
__sync_and_and_fetch (&atomic->value, 0);
#else
pthread_mutex_lock (&atomic_mutex);
atomic->value = 0;
pthread_mutex_unlock (&atomic_mutex);
#endif
}
void
_dbus_atomic_set_nonzero (DBusAtomic *atomic)
{
#ifdef HAVE_STDATOMIC_H
/* Atomic version of "*atomic = 1" */
atomic_store (&atomic->value, 1);
#elif DBUS_USE_SYNC
/* Atomic version of "*atomic |= 1; return *atomic" */
__sync_or_and_fetch (&atomic->value, 1);
#else
pthread_mutex_lock (&atomic_mutex);
atomic->value = 1;
pthread_mutex_unlock (&atomic_mutex);
#endif
}
int
_dbus_poll (DBusPollFD *fds,
int n_fds,
int timeout_milliseconds)
{
#if defined(HAVE_POLL) && !defined(BROKEN_POLL)
/* DBusPollFD is a struct pollfd in this code path, so we can just poll() */
if (timeout_milliseconds < -1)
{
timeout_milliseconds = -1;
}
return poll (fds,
n_fds,
timeout_milliseconds);
#else /* ! HAVE_POLL */
/* Emulate poll() in terms of select() */
fd_set read_set, write_set, err_set;
int max_fd = 0;
int i;
struct timeval tv;
int ready;
FD_ZERO (&read_set);
FD_ZERO (&write_set);
FD_ZERO (&err_set);
for (i = 0; i < n_fds; i++)
{
DBusPollFD *fdp = &fds[i];
if (fdp->events & _DBUS_POLLIN)
FD_SET (fdp->fd, &read_set);
if (fdp->events & _DBUS_POLLOUT)
FD_SET (fdp->fd, &write_set);
FD_SET (fdp->fd, &err_set);
max_fd = MAX (max_fd, fdp->fd);
}
tv.tv_sec = timeout_milliseconds / 1000;
tv.tv_usec = (timeout_milliseconds % 1000) * 1000;
ready = select (max_fd + 1, &read_set, &write_set, &err_set,
timeout_milliseconds < 0 ? NULL : &tv);
if (ready > 0)
{
for (i = 0; i < n_fds; i++)
{
DBusPollFD *fdp = &fds[i];
fdp->revents = 0;
if (FD_ISSET (fdp->fd, &read_set))
fdp->revents |= _DBUS_POLLIN;
if (FD_ISSET (fdp->fd, &write_set))
fdp->revents |= _DBUS_POLLOUT;
if (FD_ISSET (fdp->fd, &err_set))
fdp->revents |= _DBUS_POLLERR;
}
}
return ready;
#endif
}
void
_dbus_get_monotonic_time (dbus_int64_t *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);
if (tv_sec)
*tv_sec = t.tv_sec;
if (tv_usec)
*tv_usec = t.tv_usec;
#endif
}
void
_dbus_get_real_time (dbus_int64_t *tv_sec,
long *tv_usec)
{
struct timeval t;
gettimeofday (&t, NULL);
if (tv_sec)
*tv_sec = t.tv_sec;
if (tv_usec)
*tv_usec = t.tv_usec;
}
dbus_bool_t
_dbus_ensure_directory (const DBusString *filename,
DBusError *error)
{
const char *filename_c;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
filename_c = _dbus_string_get_const_data (filename);
if (mkdir (filename_c, 0700) < 0)
{
if (errno == EEXIST)
return TRUE;
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to create directory %s: %s\n",
filename_c, _dbus_strerror (errno));
return FALSE;
}
else
return TRUE;
}
dbus_bool_t
_dbus_create_directory (const DBusString *filename,
DBusError *error)
{
const char *filename_c;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
filename_c = _dbus_string_get_const_data (filename);
if (mkdir (filename_c, 0700) < 0)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
"Failed to create directory %s: %s\n",
filename_c, _dbus_strerror (errno));
return FALSE;
}
else
return TRUE;
}
dbus_bool_t
_dbus_concat_dir_and_file (DBusString *dir,
const DBusString *next_component)
{
dbus_bool_t dir_ends_in_slash;
dbus_bool_t file_starts_with_slash;
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);
file_starts_with_slash = '/' == _dbus_string_get_byte (next_component, 0);
if (dir_ends_in_slash && file_starts_with_slash)
{
_dbus_string_shorten (dir, 1);
}
else if (!(dir_ends_in_slash || file_starts_with_slash))
{
if (!_dbus_string_append_byte (dir, '/'))
return FALSE;
}
return _dbus_string_copy (next_component, 0, dir,
_dbus_string_get_length (dir));
}
#define NANOSECONDS_PER_SECOND 1000000000
#define MICROSECONDS_PER_SECOND 1000000
#define MILLISECONDS_PER_SECOND 1000
#define NANOSECONDS_PER_MILLISECOND 1000000
#define MICROSECONDS_PER_MILLISECOND 1000
void
_dbus_sleep_milliseconds (int milliseconds)
{
#ifdef HAVE_NANOSLEEP
struct timespec req;
struct timespec rem;
req.tv_sec = milliseconds / MILLISECONDS_PER_SECOND;
req.tv_nsec = (milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND;
rem.tv_sec = 0;
rem.tv_nsec = 0;
while (nanosleep (&req, &rem) < 0 && errno == EINTR)
req = rem;
#elif defined (HAVE_USLEEP)
usleep (milliseconds * MICROSECONDS_PER_MILLISECOND);
#else /* ! HAVE_USLEEP */
sleep (MAX (milliseconds / 1000, 1));
#endif
}
dbus_bool_t
_dbus_generate_random_bytes (DBusString *str,
int n_bytes,
DBusError *error)
{
int old_len = _dbus_string_get_length (str);
int fd;
int result;
#ifdef HAVE_GETRANDOM
char *buffer;
if (!_dbus_string_lengthen (str, n_bytes))
{
_DBUS_SET_OOM (error);
return FALSE;
}
buffer = _dbus_string_get_data_len (str, old_len, n_bytes);
result = getrandom (buffer, n_bytes, GRND_NONBLOCK);
if (result == n_bytes)
return TRUE;
_dbus_string_set_length (str, old_len);
#endif
/* note, urandom on linux will fall back to pseudorandom */
fd = open ("/dev/urandom", O_RDONLY);
if (fd < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Could not open /dev/urandom: %s",
_dbus_strerror (errno));
return FALSE;
}
_dbus_verbose ("/dev/urandom fd %d opened\n", fd);
result = _dbus_read (fd, str, n_bytes);
if (result != n_bytes)
{
if (result < 0)
dbus_set_error (error, _dbus_error_from_errno (errno),
"Could not read /dev/urandom: %s",
_dbus_strerror (errno));
else
dbus_set_error (error, DBUS_ERROR_IO_ERROR,
"Short read from /dev/urandom");
_dbus_close (fd, NULL);
_dbus_string_set_length (str, old_len);
return FALSE;
}
_dbus_verbose ("Read %d bytes from /dev/urandom\n",
n_bytes);
_dbus_close (fd, NULL);
return TRUE;
}
void
_dbus_exit (int code)
{
_exit (code);
}
const char*
_dbus_strerror (int error_number)
{
const char *msg;
msg = strerror (error_number);
if (msg == NULL)
msg = "unknown";
return msg;
}
void
_dbus_disable_sigpipe (void)
{
signal (SIGPIPE, SIG_IGN);
}
void
_dbus_fd_set_close_on_exec (int fd)
{
int val;
val = fcntl (fd, F_GETFD, 0);
if (val < 0)
return;
val |= FD_CLOEXEC;
fcntl (fd, F_SETFD, val);
}
void
_dbus_fd_clear_close_on_exec (int fd)
{
int val;
val = fcntl (fd, F_GETFD, 0);
if (val < 0)
return;
val &= ~FD_CLOEXEC;
fcntl (fd, F_SETFD, val);
}
dbus_bool_t
_dbus_close (int fd,
DBusError *error)
{
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
again:
if (close (fd) < 0)
{
if (errno == EINTR)
goto again;
dbus_set_error (error, _dbus_error_from_errno (errno),
"Could not close fd %d", fd);
return FALSE;
}
return TRUE;
}
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;
}
dbus_bool_t
_dbus_set_socket_nonblocking (DBusSocket fd,
DBusError *error)
{
return _dbus_set_fd_nonblocking (fd.fd, error);
}
static dbus_bool_t
_dbus_set_fd_nonblocking (int fd,
DBusError *error)
{
int val;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
val = fcntl (fd, F_GETFL, 0);
if (val < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to get flags from file descriptor %d: %s",
fd, _dbus_strerror (errno));
_dbus_verbose ("Failed to get flags for fd %d: %s\n", fd,
_dbus_strerror (errno));
return FALSE;
}
if (fcntl (fd, F_SETFL, val | O_NONBLOCK) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (errno),
"Failed to set nonblocking flag of file descriptor %d: %s",
fd, _dbus_strerror (errno));
_dbus_verbose ("Failed to set fd %d nonblocking: %s\n",
fd, _dbus_strerror (errno));
return FALSE;
}
return TRUE;
}
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)
{
/* don't use dbus_warn since it can _dbus_abort() */
fprintf (stderr, " %s\n", syms[i]);
++i;
}
fflush (stderr);
free (syms);
#elif defined (HAVE_BACKTRACE) && ! defined (DBUS_BUILT_R_DYNAMIC)
fprintf (stderr, " D-Bus not built with -rdynamic so unable to print a backtrace\n");
#else
fprintf (stderr, " D-Bus not compiled with backtrace support so unable to print a backtrace\n");
#endif
}
dbus_bool_t
_dbus_socketpair (DBusSocket *fd1,
DBusSocket *fd2,
dbus_bool_t blocking,
DBusError *error)
{
#ifdef HAVE_SOCKETPAIR
int fds[2];
int retval;
#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 || errno == EPROTOTYPE))
#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->fd = fds[0];
fd2->fd = fds[1];
_dbus_verbose ("full-duplex pipe %d <-> %d\n",
fd1->fd, fd2->fd);
return TRUE;
#else
_dbus_warn ("_dbus_socketpair() not implemented on this OS");
dbus_set_error (error, DBUS_ERROR_FAILED,
"_dbus_socketpair() not implemented on this OS");
return FALSE;
#endif
}
int
_dbus_printf_string_upper_bound (const char *format,
va_list args)
{
char static_buf[1024];
int bufsize = sizeof (static_buf);
int len;
va_list args_copy;
va_copy (args_copy, args);
len = vsnprintf (static_buf, bufsize, format, args_copy);
va_end (args_copy);
/* 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. */
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;
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;
}
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
* just expands to "/tmp"
*/
if (tmpdir == NULL)
tmpdir = getenv("TMPDIR");
/* These two env variables are probably
* broken, but maybe some OS uses them?
*/
if (tmpdir == NULL)
tmpdir = getenv("TMP");
if (tmpdir == NULL)
tmpdir = getenv("TEMP");
/* And this is the sane fallback. */
if (tmpdir == NULL)
tmpdir = "/tmp";
}
_DBUS_UNLOCK (sysdeps);
_dbus_assert(tmpdir != NULL);
return tmpdir;
}
#if defined(DBUS_ENABLE_X11_AUTOLAUNCH) || defined(DBUS_ENABLE_LAUNCHD)
static dbus_bool_t
_read_subprocess_line_argv (const char *progpath,
dbus_bool_t path_fallback,
const char * const *argv,
DBusString *result,
DBusError *error)
{
int result_pipe[2] = { -1, -1 };
int errors_pipe[2] = { -1, -1 };
pid_t pid;
int ret;
int status;
int orig_len;
dbus_bool_t retval;
sigset_t new_set, old_set;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
retval = 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
*/
sigemptyset (&new_set);
sigaddset (&new_set, SIGCHLD);
sigprocmask (SIG_BLOCK, &new_set, &old_set);
orig_len = _dbus_string_get_length (result);
#define READ_END 0
#define WRITE_END 1
if (pipe (result_pipe) < 0)
{
dbus_set_error (error, _dbus_error_from_errno (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 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;
}
/* Make sure our output buffers aren't redundantly printed by both the
* parent and the child */
fflush (stdout);
fflush (stderr);
pid = fork ();
if (pid < 0)
{
dbus_set_error (error, _dbus_error_from_errno (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 */
const char *error_str;
if (!_dbus_ensure_standard_fds (DBUS_FORCE_STDIN_NULL, &error_str))
{
int saved_errno = errno;
/* Try to write details into the pipe, but don't bother
* trying too hard (no retry loop). */
if (write (errors_pipe[WRITE_END], error_str, strlen (error_str)) < 0 ||
write (errors_pipe[WRITE_END], ": ", 2) < 0)
{
/* ignore, not much we can do */
}
error_str = _dbus_strerror (saved_errno);
if (write (errors_pipe[WRITE_END], error_str, strlen (error_str)) < 0)
{
/* ignore, not much we can do */
}
_exit (1);
}
/* set-up stdXXX */
close (result_pipe[READ_END]);
close (errors_pipe[READ_END]);
if (dup2 (result_pipe[WRITE_END], 1) == -1) /* setup stdout */
_exit (1);
if (dup2 (errors_pipe[WRITE_END], 2) == -1) /* setup stderr */
_exit (1);
_dbus_close_all ();
sigprocmask (SIG_SETMASK, &old_set, NULL);
/* If it looks fully-qualified, try execv first */
if (progpath[0] == '/')
{
execv (progpath, (char * const *) 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, (char * const *) argv);
}
else
execvp (progpath, (char * const *) argv);
/* still nothing, we failed */
_exit (1);
}
/* parent process */
close (result_pipe[WRITE_END]);
close (errors_pipe[WRITE_END]);
result_pipe[WRITE_END] = -1;
errors_pipe[WRITE_END] = -1;
ret = 0;
do
{
ret = _dbus_read (result_pipe[READ_END], result, 1024);
}
while (ret > 0);
/* reap the child process to avoid it lingering as zombie */
do
{
ret = waitpid (pid, &status, 0);
}
while (ret == -1 && errno == EINTR);
/* We succeeded if the process exited with status 0 and
anything was read */
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0 )
{
/* The process ended with error */
DBusString 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);
}
while (ret > 0);
_dbus_string_set_length (result, orig_len);
if (_dbus_string_get_length (&error_message) > 0)
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,
"%s terminated abnormally without any error message",
progpath);
goto out;
}
retval = TRUE;
out:
sigprocmask (SIG_SETMASK, &old_set, NULL);
_DBUS_ASSERT_ERROR_XOR_BOOL (error, retval);
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
dbus_bool_t
_dbus_get_autolaunch_address (const char *scope,
DBusString *address,
DBusError *error)
{
#ifdef DBUS_ENABLE_X11_AUTOLAUNCH
static const char arg_dbus_launch[] = "dbus-launch";
static const char arg_autolaunch[] = "--autolaunch";
static const char arg_binary_syntax[] = "--binary-syntax";
static const char arg_close_stderr[] = "--close-stderr";
/* 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;
const char *progpath;
const 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.
*
* This is not strictly true in a world where the user bus exists,
* because dbus-launch --autolaunch knows how to connect to that -
* but if we were going to connect to the user bus, we'd have done
* so before trying autolaunch: in any case. */
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, error))
{
goto out;
}
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
progpath = _dbus_getenv ("DBUS_TEST_DBUS_LAUNCH");
if (progpath == NULL)
#endif
progpath = DBUS_BINDIR "/dbus-launch";
/*
* argv[0] is always dbus-launch, that's the name what we'll
* get from /proc, or ps(1), regardless what the progpath is,
* see fd.o#69716
*/
i = 0;
argv[i] = arg_dbus_launch;
++i;
argv[i] = arg_autolaunch;
++i;
argv[i] = _dbus_string_get_data (&uuid);
++i;
argv[i] = arg_binary_syntax;
++i;
argv[i] = arg_close_stderr;
++i;
argv[i] = NULL;
++i;
_dbus_assert (i == _DBUS_N_ELEMENTS (argv));
retval = _read_subprocess_line_argv (progpath,
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, "
#ifdef DBUS_ENABLE_LAUNCHD
"verify that org.freedesktop.dbus-session.plist is loaded or "
#endif
"set your DBUS_SESSION_BUS_ADDRESS instead");
return FALSE;
#endif
}
dbus_bool_t
_dbus_read_local_machine_uuid (DBusGUID *machine_id,
dbus_bool_t create_if_not_found,
DBusError *error)
{
DBusError our_error = DBUS_ERROR_INIT;
DBusError etc_error = DBUS_ERROR_INIT;
DBusString filename;
dbus_bool_t b;
_dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
b = _dbus_read_uuid_file (&filename, machine_id, FALSE, &our_error);
if (b)
return TRUE;
/* Fallback to the system machine ID */
_dbus_string_init_const (&filename, "/etc/machine-id");
b = _dbus_read_uuid_file (&filename, machine_id, FALSE, &etc_error);
if (b)
{
if (create_if_not_found)
{
/* try to copy it to the DBUS_MACHINE_UUID_FILE, but do not
* complain if that isn't possible for whatever reason */
_dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
_dbus_write_uuid_file (&filename, machine_id, NULL);
}
dbus_error_free (&our_error);
return TRUE;
}
if (!create_if_not_found)
{
dbus_set_error (error, etc_error.name,
"D-Bus library appears to be incorrectly set up: "
"see the manual page for dbus-uuidgen to correct "
"this issue. (%s; %s)",
our_error.message, etc_error.message);
dbus_error_free (&our_error);
dbus_error_free (&etc_error);
return FALSE;
}
dbus_error_free (&our_error);
dbus_error_free (&etc_error);
/* if none found, try to make a new one */
_dbus_string_init_const (&filename, DBUS_MACHINE_UUID_FILE);
if (!_dbus_generate_uuid (machine_id, error))
return FALSE;
return _dbus_write_uuid_file (&filename, machine_id, error);
}
dbus_bool_t
_dbus_lookup_launchd_socket (DBusString *socket_path,
const char *launchd_env_var,
DBusError *error)
{
#ifdef DBUS_ENABLE_LAUNCHD
char *argv[4];
int i;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
if (_dbus_check_setuid ())
{
dbus_set_error_const (error, DBUS_ERROR_NOT_SUPPORTED,
"Unable to find launchd socket when setuid");
return FALSE;
}
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 (!_read_subprocess_line_argv(argv[0], TRUE, argv, socket_path, error))
{
return FALSE;
}
/* no error, but no result either */
if (_dbus_string_get_length(socket_path) == 0)
{
return FALSE;
}
/* strip the carriage-return */
_dbus_string_shorten(socket_path, 1);
return TRUE;
#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
}
#ifdef DBUS_ENABLE_LAUNCHD
static dbus_bool_t
_dbus_lookup_session_address_launchd (dbus_bool_t *supported,
DBusString *address,
DBusError *error)
{
dbus_bool_t valid_socket;
DBusString socket_path;
if (!_dbus_string_init (&socket_path))
{
_DBUS_SET_OOM (error);
return FALSE;
}
valid_socket = _dbus_lookup_launchd_socket (&socket_path, "DBUS_LAUNCHD_SESSION_BUS_SOCKET", NULL);
if (!valid_socket)
{
_dbus_verbose ("launchd did not provide a socket path");
_dbus_string_free(&socket_path);
*supported = FALSE;
return TRUE; /* Cannot use it, but not an error */
}
if (!_dbus_string_append (address, "unix:path="))
{
_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;
}
_dbus_string_free(&socket_path);
return TRUE;
}
#endif
static dbus_bool_t
_dbus_lookup_user_bus (dbus_bool_t *supported,
DBusString *address,
DBusError *error)
{
const char *runtime_dir = _dbus_getenv ("XDG_RUNTIME_DIR");
dbus_bool_t ret = FALSE;
struct stat stbuf;
DBusString user_bus_path;
if (runtime_dir == NULL)
{
_dbus_verbose ("XDG_RUNTIME_DIR not found in environment");
*supported = FALSE;
return TRUE; /* Cannot use it, but not an error */
}
if (!_dbus_string_init (&user_bus_path))
{
_DBUS_SET_OOM (error);
return FALSE;
}
if (!_dbus_string_append_printf (&user_bus_path, "%s/bus", runtime_dir))
{
_DBUS_SET_OOM (error);
goto out;
}
if (lstat (_dbus_string_get_const_data (&user_bus_path), &stbuf) == -1)
{
_dbus_verbose ("XDG_RUNTIME_DIR/bus not available: %s",
_dbus_strerror (errno));
*supported = FALSE;
ret = TRUE; /* Cannot use it, but not an error */
goto out;
}
if (stbuf.st_uid != getuid ())
{
_dbus_verbose ("XDG_RUNTIME_DIR/bus owned by uid %ld, not our uid %ld",
(long) stbuf.st_uid, (long) getuid ());
*supported = FALSE;
ret = TRUE; /* Cannot use it, but not an error */
goto out;
}
if ((stbuf.st_mode & S_IFMT) != S_IFSOCK)
{
_dbus_verbose ("XDG_RUNTIME_DIR/bus is not a socket: st_mode = 0o%lo",
(long) stbuf.st_mode);
*supported = FALSE;
ret = TRUE; /* Cannot use it, but not an error */
goto out;
}
if (!_dbus_string_append (address, "unix:path=") ||
!_dbus_address_append_escaped (address, &user_bus_path))
{
_DBUS_SET_OOM (error);
goto out;
}
*supported = TRUE;
ret = TRUE;
out:
_dbus_string_free (&user_bus_path);
return ret;
}
dbus_bool_t
_dbus_lookup_session_address (dbus_bool_t *supported,
DBusString *address,
DBusError *error)
{
#ifdef DBUS_ENABLE_LAUNCHD
*supported = TRUE;
return _dbus_lookup_session_address_launchd (supported, address, error);
#else
*supported = FALSE;
if (!_dbus_lookup_user_bus (supported, address, error))
return FALSE;
else if (*supported)
return TRUE;
/* On non-Mac Unix platforms, if the session address isn't already
* set in DBUS_SESSION_BUS_ADDRESS environment variable and the
* $XDG_RUNTIME_DIR/bus can't be used, we punt and fall back to the
* autolaunch: global default; see init_session_address in
* dbus/dbus-bus.c. */
return TRUE;
#endif
}
void
_dbus_flush_caches (void)
{
_dbus_user_database_flush_system ();
}
dbus_bool_t
_dbus_append_keyring_directory_for_credentials (DBusString *directory,
DBusCredentials *credentials)
{
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;
uid = _dbus_credentials_get_unix_uid (credentials);
_dbus_assert (uid != DBUS_UID_UNSET);
if (!_dbus_homedir_from_uid (uid, &homedir))
goto failed;
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
{
const char *override;
override = _dbus_getenv ("DBUS_TEST_HOMEDIR");
if (override != NULL && *override != '\0')
{
_dbus_string_set_length (&homedir, 0);
if (!_dbus_string_append (&homedir, override))
goto failed;
_dbus_verbose ("Using fake homedir for testing: %s\n",
_dbus_string_get_const_data (&homedir));
}
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)
{
_dbus_warn ("Using %s for testing, set DBUS_TEST_HOMEDIR to avoid",
_dbus_string_get_const_data (&homedir));
already_warned = TRUE;
}
}
}
#endif
_dbus_string_init_const (&dotdir, ".dbus-keyrings");
if (!_dbus_concat_dir_and_file (&homedir,
&dotdir))
goto failed;
if (!_dbus_string_copy (&homedir, 0,
directory, _dbus_string_get_length (directory))) {
goto failed;
}
_dbus_string_free (&homedir);
return TRUE;
failed:
_dbus_string_free (&homedir);
return FALSE;
}
/* Documented in dbus-sysdeps-win.c, does nothing on Unix */
dbus_bool_t
_dbus_daemon_unpublish_session_bus_address (void)
{
return TRUE;
}
dbus_bool_t
_dbus_get_is_errno_eagain_or_ewouldblock (int e)
{
/* Avoid the -Wlogical-op GCC warning, which can be triggered when EAGAIN and
* EWOULDBLOCK are numerically equal, which is permitted as described by
* errno(3).
*/
#if EAGAIN == EWOULDBLOCK
return e == EAGAIN;
#else
return e == EAGAIN || e == EWOULDBLOCK;
#endif
}
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;
}
dbus_bool_t
_dbus_socket_can_pass_unix_fd (DBusSocket 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.fd, &sa_buf.sa, &sa_len) < 0)
return FALSE;
return sa_buf.sa.sa_family == AF_UNIX;
#else
return FALSE;
#endif
}
/*
* Similar to Solaris fdwalk(3), but without the ability to stop iteration,
* and may call func for integers that are not actually valid fds.
*/
static void
act_on_fds_3_and_up (void (*func) (int fd))
{
int maxfds, i;
#if defined(__linux__) && defined(__GLIBC__)
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 *de;
int fd;
long l;
char *e = NULL;
de = readdir (d);
if (!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;
func (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++)
func (i);
}
/* Some library implementations of closefrom() are not async-signal-safe,
* and we call _dbus_close_all() after forking, so we only do this on
* operating systems where we know that closefrom() is a system call */
#if defined(HAVE_CLOSEFROM) && ( \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__) || \
defined(__sun__) && defined(F_CLOSEFROM) \
)
#define CLOSEFROM_SIGNAL_SAFE 1
#else
#define CLOSEFROM_SIGNAL_SAFE 0
static void
close_ignore_error (int fd)
{
close (fd);
}
#endif
void
_dbus_close_all (void)
{
#ifdef HAVE_CLOSE_RANGE
if (close_range (3, INT_MAX, 0) == 0)
return;
#endif
#if CLOSEFROM_SIGNAL_SAFE
closefrom (3);
#else
act_on_fds_3_and_up (close_ignore_error);
#endif
}
void
_dbus_fd_set_all_close_on_exec (void)
{
#if defined(HAVE_CLOSE_RANGE) && defined(CLOSE_RANGE_CLOEXEC)
if (close_range (3, INT_MAX, CLOSE_RANGE_CLOEXEC) == 0)
return;
#endif
act_on_fds_3_and_up (_dbus_fd_set_close_on_exec);
}
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
}
dbus_bool_t
_dbus_append_address_from_socket (DBusSocket 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];
socklen_t size = sizeof (socket);
DBusString path_str;
const char *family_name = NULL;
dbus_uint16_t port;
if (getsockname (fd.fd, &socket.sa, &size))
goto err;
switch (socket.sa.sa_family)
{
case AF_UNIX:
if (socket.un.sun_path[0]=='\0')
{
_dbus_string_init_const (&path_str, &(socket.un.sun_path[1]));
if (_dbus_string_append (address, "unix:abstract=") &&
_dbus_address_append_escaped (address, &path_str))
{
return TRUE;
}
else
{
_DBUS_SET_OOM (error);
return FALSE;
}
}
else
{
_dbus_string_init_const (&path_str, socket.un.sun_path);
if (_dbus_string_append (address, "unix:path=") &&
_dbus_address_append_escaped (address, &path_str))
{
return TRUE;
}
else
{
_DBUS_SET_OOM (error);
return FALSE;
}
}
/* not reached */
break;
case AF_INET:
#ifdef AF_INET6
case AF_INET6:
#endif
_dbus_string_init_const (&path_str, hostip);
if (_dbus_inet_sockaddr_to_string (&socket, size, hostip, sizeof (hostip),
&family_name, &port, error))
{
if (_dbus_string_append_printf (address, "tcp:family=%s,port=%u,host=",
family_name, port) &&
_dbus_address_append_escaped (address, &path_str))
{
return TRUE;
}
else
{
_DBUS_SET_OOM (error);
return FALSE;
}
}
else
{
return FALSE;
}
/* not reached */
break;
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 read address from socket: %s",
_dbus_strerror (errno));
return FALSE;
}
int
_dbus_save_socket_errno (void)
{
return errno;
}
void
_dbus_restore_socket_errno (int saved_errno)
{
errno = saved_errno;
}
static const char *syslog_tag = "dbus";
#ifdef HAVE_SYSLOG_H
static DBusLogFlags log_flags = DBUS_LOG_FLAGS_STDERR;
#endif
void
_dbus_init_system_log (const char *tag,
DBusLogFlags flags)
{
/* We never want to turn off logging completely */
_dbus_assert (
(flags & (DBUS_LOG_FLAGS_STDERR | DBUS_LOG_FLAGS_SYSTEM_LOG)) != 0);
syslog_tag = tag;
#ifdef HAVE_SYSLOG_H
log_flags = flags;
if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG)
openlog (tag, LOG_PID, LOG_DAEMON);
#endif
}
void
_dbus_logv (DBusSystemLogSeverity severity,
const char *msg,
va_list args)
{
va_list tmp;
#ifdef HAVE_SYSLOG_H
if (log_flags & DBUS_LOG_FLAGS_SYSTEM_LOG)
{
int flags = LOG_DAEMON | LOG_WARNING;
switch (severity)
{
case DBUS_SYSTEM_LOG_INFO:
flags = LOG_DAEMON | LOG_INFO;
break;
case DBUS_SYSTEM_LOG_WARNING:
flags = LOG_DAEMON | LOG_WARNING;
break;
case DBUS_SYSTEM_LOG_SECURITY:
flags = LOG_AUTH | LOG_NOTICE;
break;
case DBUS_SYSTEM_LOG_ERROR:
flags = LOG_DAEMON|LOG_CRIT;
break;
default:
_dbus_assert_not_reached ("invalid log severity");
}
va_copy (tmp, args);
vsyslog (flags, msg, tmp);
va_end (tmp);
}
/* If we don't have syslog.h, we always behave as though stderr was in
* the flags */
if (log_flags & DBUS_LOG_FLAGS_STDERR)
#endif
{
va_copy (tmp, args);
fprintf (stderr, "%s[" DBUS_PID_FORMAT "]: ", syslog_tag, _dbus_getpid ());
vfprintf (stderr, msg, tmp);
fputc ('\n', stderr);
va_end (tmp);
}
}
/*
* Return the low-level representation of a socket error, as used by
* cross-platform socket APIs like inet_ntop(), send() and recv(). This
* is the standard errno on Unix, but is WSAGetLastError() on Windows.
*
* Some libdbus internal functions copy this into errno, but with
* hindsight that was probably a design flaw.
*/
int
_dbus_get_low_level_socket_errno (void)
{
return errno;
}
/* tests in dbus-sysdeps-util.c */
_dbus_address_append_escaped
dbus_bool_t _dbus_address_append_escaped(DBusString *escaped, const DBusString *unescaped)
Appends an escaped version of one string to another string, using the D-Bus address escaping mechanis...
Definition dbus-address.c:109
_dbus_credentials_clear
void _dbus_credentials_clear(DBusCredentials *credentials)
Clear all credentials in the object.
Definition dbus-credentials.c:688
_dbus_credentials_get_unix_uid
dbus_uid_t _dbus_credentials_get_unix_uid(DBusCredentials *credentials)
Gets the UNIX user ID in the credentials, or DBUS_UID_UNSET if the credentials object doesn't contain...
Definition dbus-credentials.c:440
_dbus_credentials_take_pid_fd
_DBUS_GNUC_NORETURN void _dbus_credentials_take_pid_fd(DBusCredentials *credentials, int pid_fd)
Add a UNIX process ID FD to the credentials.
Definition dbus-credentials.c:200
_dbus_credentials_add_linux_security_label
dbus_bool_t _dbus_credentials_add_linux_security_label(DBusCredentials *credentials, const char *label)
Add a Linux security label, as used by LSMs such as SELinux, Smack and AppArmor, to the credentials.
Definition dbus-credentials.c:317
_dbus_credentials_take_unix_gids
void _dbus_credentials_take_unix_gids(DBusCredentials *credentials, dbus_gid_t *gids, size_t n_gids)
Add UNIX group IDs to the credentials, replacing any group IDs that might already have been present.
Definition dbus-credentials.c:252
_dbus_credentials_add_unix_uid
dbus_bool_t _dbus_credentials_add_unix_uid(DBusCredentials *credentials, dbus_uid_t uid)
Add a UNIX user ID to the credentials.
Definition dbus-credentials.c:220
_dbus_credentials_add_pid
dbus_bool_t _dbus_credentials_add_pid(DBusCredentials *credentials, dbus_pid_t pid)
Add a UNIX process ID to the credentials.
Definition dbus-credentials.c:181
_dbus_credentials_add_adt_audit_data
dbus_bool_t _dbus_credentials_add_adt_audit_data(DBusCredentials *credentials, void *audit_data, dbus_int32_t size)
Add ADT audit data to the credentials.
Definition dbus-credentials.c:341
_dbus_credentials_are_anonymous
dbus_bool_t _dbus_credentials_are_anonymous(DBusCredentials *credentials)
Checks whether a credentials object contains a user identity.
Definition dbus-credentials.c:558
DBUS_ERROR_INIT
#define DBUS_ERROR_INIT
Expands to a suitable initializer for a DBusError on the stack.
Definition dbus-errors.h:64
dbus_move_error
void dbus_move_error(DBusError *src, DBusError *dest)
Moves an error src into dest, freeing src and overwriting dest.
Definition dbus-errors.c:281
dbus_set_error_const
void dbus_set_error_const(DBusError *error, const char *name, const char *message)
Assigns an error name and message to a DBusError.
Definition dbus-errors.c:245
dbus_error_init
void dbus_error_init(DBusError *error)
Initializes a DBusError structure.
Definition dbus-errors.c:190
dbus_set_error
void dbus_set_error(DBusError *error, const char *name, const char *format,...)
Assigns an error name and message to a DBusError.
Definition dbus-errors.c:356
dbus_error_free
void dbus_error_free(DBusError *error)
Frees an error that's been set (or just initialized), then reinitializes the error as in dbus_error_i...
Definition dbus-errors.c:213
_dbus_file_get_contents
dbus_bool_t _dbus_file_get_contents(DBusString *str, const DBusString *filename, DBusError *error)
Appends the contents of the given file to the string, returning error code.
Definition dbus-file-unix.c:63
_dbus_assert_not_reached
#define _dbus_assert_not_reached(explanation)
Aborts with an error message if called.
Definition dbus-internals.h:164
_dbus_assert
#define _dbus_assert(condition)
Aborts with an error message if the condition is false.
Definition dbus-internals.h:153
_DBUS_UNLOCK
#define _DBUS_UNLOCK(name)
Unlocks a global lock.
Definition dbus-internals.h:438
READ_END
#define READ_END
Helps remember which end of the pipe is which.
Definition dbus-spawn-unix.c:895
WRITE_END
#define WRITE_END
Helps remember which end of the pipe is which.
Definition dbus-spawn-unix.c:897
_DBUS_LOCK
#define _DBUS_LOCK(name)
Locks a global lock, initializing it first if necessary.
Definition dbus-internals.h:437
_dbus_error_from_errno
const char * _dbus_error_from_errno(int error_number)
Converts a UNIX errno, or Windows errno or WinSock error value into a DBusError name.
Definition dbus-sysdeps.c:565
_dbus_generate_uuid
dbus_bool_t _dbus_generate_uuid(DBusGUID *uuid, DBusError *error)
Generates a new UUID.
Definition dbus-internals.c:752
_dbus_strdup
char * _dbus_strdup(const char *str)
Duplicates a string.
Definition dbus-internals.c:621
_dbus_read_uuid_file
dbus_bool_t _dbus_read_uuid_file(const DBusString *filename, DBusGUID *uuid, dbus_bool_t create_if_not_found, DBusError *error)
Reads (and optionally writes) a uuid to a file.
Definition dbus-internals.c:931
_dbus_user_database_flush_system
void _dbus_user_database_flush_system(void)
Flushes the system global user database;.
Definition dbus-userdb.c:396
_dbus_get_local_machine_uuid_encoded
dbus_bool_t _dbus_get_local_machine_uuid_encoded(DBusString *uuid_str, DBusError *error)
Gets the hex-encoded UUID of the machine this function is executed on.
Definition dbus-internals.c:983
_dbus_write_uuid_file
dbus_bool_t _dbus_write_uuid_file(const DBusString *filename, const DBusGUID *uuid, DBusError *error)
Write the give UUID to a file.
Definition dbus-internals.c:882
_dbus_homedir_from_uid
dbus_bool_t _dbus_homedir_from_uid(dbus_uid_t uid, DBusString *homedir)
Gets the home directory for the given user.
Definition dbus-userdb.c:466
_dbus_warn
void _dbus_warn(const char *format,...)
Prints a warning message to stderr.
Definition dbus-internals.c:278
_DBUS_N_ELEMENTS
#define _DBUS_N_ELEMENTS(array)
Computes the number of elements in a fixed-size array using sizeof().
Definition dbus-internals.h:189
_DBUS_ZERO
#define _DBUS_ZERO(object)
Sets all bits in an object to zero.
Definition dbus-internals.h:194
_DBUS_INT32_MAX
#define _DBUS_INT32_MAX
Maximum value of type "int32".
Definition dbus-internals.h:323
_dbus_list_pop_first
void * _dbus_list_pop_first(DBusList **list)
Removes the first value in the list and returns it.
Definition dbus-list.c:679
_dbus_list_append
dbus_bool_t _dbus_list_append(DBusList **list, void *data)
Appends a value to the list.
Definition dbus-list.c:273
NULL
#define NULL
A null pointer, defined appropriately for C or C++.
Definition dbus-macros.h:51
TRUE
#define TRUE
Expands to "1".
Definition dbus-macros.h:41
FALSE
#define FALSE
Expands to "0".
Definition dbus-macros.h:44
_dbus_verbose_bytes_of_string
DBUS_PRIVATE_EXPORT void _dbus_verbose_bytes_of_string(const DBusString *str, int start, int len)
Dump the given part of the string to verbose log.
Definition dbus-marshal-basic.c:1428
dbus_free
void dbus_free(void *memory)
Frees a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
Definition dbus-memory.c:710
dbus_realloc
void * dbus_realloc(void *memory, size_t bytes)
Resizes a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
Definition dbus-memory.c:610
dbus_new
#define dbus_new(type, count)
Safe macro for using dbus_malloc().
Definition dbus-memory.h:59
dbus_new0
#define dbus_new0(type, count)
Safe macro for using dbus_malloc0().
Definition dbus-memory.h:60
dbus_malloc
void * dbus_malloc(size_t bytes)
Allocates the given number of bytes, as with standard malloc().
Definition dbus-memory.c:470
DBUS_MAXIMUM_MESSAGE_UNIX_FDS
#define DBUS_MAXIMUM_MESSAGE_UNIX_FDS
The maximum total number of unix fds in a message.
Definition dbus-protocol.h:220
DBUS_ERROR_NOT_SUPPORTED
#define DBUS_ERROR_NOT_SUPPORTED
Requested operation isn't supported (like ENOSYS on UNIX).
Definition dbus-protocol.h:375
DBUS_ERROR_BAD_ADDRESS
#define DBUS_ERROR_BAD_ADDRESS
A D-Bus bus address was malformed.
Definition dbus-protocol.h:373
DBUS_ERROR_IO_ERROR
#define DBUS_ERROR_IO_ERROR
Something went wrong reading or writing to a socket, for example.
Definition dbus-protocol.h:371
DBUS_ERROR_FAILED
#define DBUS_ERROR_FAILED
A generic error; "something went wrong" - see the error message for more.
Definition dbus-protocol.h:361
DBUS_ERROR_SPAWN_EXEC_FAILED
#define DBUS_ERROR_SPAWN_EXEC_FAILED
While starting a new process, the exec() call failed.
Definition dbus-protocol.h:422
DBUS_ERROR_NO_MEMORY
#define DBUS_ERROR_NO_MEMORY
There was not enough memory to complete an operation.
Definition dbus-protocol.h:363
_dbus_string_set_length
dbus_bool_t _dbus_string_set_length(DBusString *str, int length)
Sets the length of a string.
Definition dbus-string.c:847
_dbus_string_append
dbus_bool_t _dbus_string_append(DBusString *str, const char *buffer)
Appends a nul-terminated C-style string to a DBusString.
Definition dbus-string.c:980
_dbus_string_init
dbus_bool_t _dbus_string_init(DBusString *str)
Initializes a string.
Definition dbus-string.c:182
_dbus_string_init_const
void _dbus_string_init_const(DBusString *str, const char *value)
Initializes a constant string.
Definition dbus-string.c:197
_dbus_string_copy
dbus_bool_t _dbus_string_copy(const DBusString *source, int start, DBusString *dest, int insert_at)
Like _dbus_string_move(), but does not delete the section of the source string that's copied to the d...
Definition dbus-string.c:1345
_dbus_string_init_preallocated
dbus_bool_t _dbus_string_init_preallocated(DBusString *str, int allocate_size)
Initializes a string that can be up to the given allocation size before it has to realloc.
Definition dbus-string.c:139
_dbus_string_find
dbus_bool_t _dbus_string_find(const DBusString *str, int start, const char *substr, int *found)
Finds the given substring in the string, returning TRUE and filling in the byte index where the subst...
Definition dbus-string.c:1666
_dbus_string_get_data_len
char * _dbus_string_get_data_len(DBusString *str, int start, int len)
Gets a sub-portion of the raw character buffer from the string.
Definition dbus-string.c:535
_dbus_string_free
void _dbus_string_free(DBusString *str)
Frees a string created by _dbus_string_init(), and fills it with the same contents as _DBUS_STRING_IN...
Definition dbus-string.c:278
_dbus_string_shorten
void _dbus_string_shorten(DBusString *str, int length_to_remove)
Makes a string shorter by the given number of bytes.
Definition dbus-string.c:825
_dbus_string_parse_uint
DBUS_PRIVATE_EXPORT dbus_bool_t _dbus_string_parse_uint(const DBusString *str, int start, unsigned long *value_return, int *end_return)
Parses an unsigned integer contained in a DBusString.
Definition dbus-sysdeps.c:410
_dbus_string_lengthen
dbus_bool_t _dbus_string_lengthen(DBusString *str, int additional_length)
Makes a string longer by the given number of bytes.
Definition dbus-string.c:805
_dbus_string_append_byte
dbus_bool_t _dbus_string_append_byte(DBusString *str, unsigned char byte)
Appends a single byte to the string, returning FALSE if not enough memory.
Definition dbus-string.c:1190
_dbus_string_append_printf
dbus_bool_t _dbus_string_append_printf(DBusString *str, const char *format,...)
Appends a printf-style formatted string to the DBusString.
Definition dbus-string.c:1147
_dbus_fd_clear_close_on_exec
void _dbus_fd_clear_close_on_exec(int fd)
Sets the file descriptor to not be close-on-exec.
Definition dbus-sysdeps-unix.c:3705
_dbus_fd_set_all_close_on_exec
void _dbus_fd_set_all_close_on_exec(void)
Sets all file descriptors except the first three (i.e.
Definition dbus-sysdeps-unix.c:4982
_dbus_close
dbus_bool_t _dbus_close(int fd, DBusError *error)
Closes a file descriptor.
Definition dbus-sysdeps-unix.c:3727
_dbus_write
int _dbus_write(int fd, const DBusString *buffer, int start, int len)
Thin wrapper around the write() system call that writes a part of a DBusString and handles EINTR for ...
Definition dbus-sysdeps-unix.c:827
_dbus_write_two
int _dbus_write_two(int fd, const DBusString *buffer1, int start1, int len1, const DBusString *buffer2, int start2, int len2)
Like _dbus_write() but will use writev() if possible to write both buffers in sequence.
Definition dbus-sysdeps-unix.c:873
_dbus_lookup_launchd_socket
dbus_bool_t _dbus_lookup_launchd_socket(DBusString *socket_path, const char *launchd_env_var, DBusError *error)
quries launchd for a specific env var which holds the socket path.
Definition dbus-sysdeps-unix.c:4486
_dbus_listen_systemd_sockets
int _dbus_listen_systemd_sockets(DBusSocket **fds, DBusError *error)
Acquires one or more sockets passed in from systemd.
Definition dbus-sysdeps-unix.c:1299
_dbus_append_address_from_socket
dbus_bool_t _dbus_append_address_from_socket(DBusSocket fd, DBusString *address, DBusError *error)
Read the address from the socket and append it to the string.
Definition dbus-sysdeps-unix.c:5055
_dbus_user_info_fill
dbus_bool_t _dbus_user_info_fill(DBusUserInfo *info, const DBusString *username, DBusError *error)
Gets user info for the given username.
Definition dbus-sysdeps-unix.c:2966
_dbus_close_all
void _dbus_close_all(void)
Closes all file descriptors except the first three (i.e.
Definition dbus-sysdeps-unix.c:4963
_dbus_dup
int _dbus_dup(int fd, DBusError *error)
Duplicates a file descriptor.
Definition dbus-sysdeps-unix.c:3755
_dbus_fd_set_close_on_exec
void _dbus_fd_set_close_on_exec(int fd)
Sets the file descriptor to be close on exec.
Definition dbus-sysdeps-unix.c:3683
_dbus_read
int _dbus_read(int fd, DBusString *buffer, int count)
Thin wrapper around the read() system call that appends the data it reads to the DBusString buffer.
Definition dbus-sysdeps-unix.c:767
_dbus_ensure_standard_fds
dbus_bool_t _dbus_ensure_standard_fds(DBusEnsureStandardFdsFlags flags, const char **error_str_p)
Ensure that the standard file descriptors stdin, stdout and stderr are open, by opening /dev/null if ...
Definition dbus-sysdeps-unix.c:179
_dbus_geteuid
dbus_uid_t _dbus_geteuid(void)
Gets our effective UID.
Definition dbus-sysdeps-unix.c:3145
_dbus_user_info_fill_uid
dbus_bool_t _dbus_user_info_fill_uid(DBusUserInfo *info, dbus_uid_t uid, DBusError *error)
Gets user info for the given user ID.
Definition dbus-sysdeps-unix.c:2983
_dbus_logv
void _dbus_logv(DBusSystemLogSeverity severity, const char *msg, va_list args)
Log a message to the system log file (e.g.
Definition dbus-sysdeps-unix.c:5208
_dbus_get_monotonic_time
void _dbus_get_monotonic_time(dbus_int64_t *tv_sec, long *tv_usec)
Get current time, as in gettimeofday().
Definition dbus-sysdeps-unix.c:3381
_dbus_read_local_machine_uuid
dbus_bool_t _dbus_read_local_machine_uuid(DBusGUID *machine_id, dbus_bool_t create_if_not_found, DBusError *error)
Reads the uuid of the machine we're running on from the dbus configuration.
Definition dbus-sysdeps-unix.c:4421
_DBUS_POLLOUT
#define _DBUS_POLLOUT
Writing now will not block.
Definition dbus-sysdeps.h:448
dbus_uid_t
unsigned long dbus_uid_t
A user ID.
Definition dbus-sysdeps.h:141
_dbus_get_is_errno_eagain_or_ewouldblock
dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock(int e)
See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently for Winsock so is abstracted).
Definition dbus-sysdeps-unix.c:4801
_dbus_pid_for_log
unsigned long _dbus_pid_for_log(void)
The only reason this is separate from _dbus_getpid() is to allow it on Windows for logging but not fo...
Definition dbus-sysdeps-unix.c:3157
dbus_pid_t
unsigned long dbus_pid_t
A process ID.
Definition dbus-sysdeps.h:139
_dbus_read_socket
int _dbus_read_socket(DBusSocket fd, DBusString *buffer, int count)
Like _dbus_read(), but only works on sockets so is available on Windows.
Definition dbus-sysdeps-unix.c:338
_dbus_exit
void _dbus_exit(int code)
Exit the process, returning the given value.
Definition dbus-sysdeps-unix.c:3641
_DBUS_POLLERR
#define _DBUS_POLLERR
Error condition.
Definition dbus-sysdeps.h:450
_dbus_socket_can_pass_unix_fd
dbus_bool_t _dbus_socket_can_pass_unix_fd(DBusSocket fd)
Checks whether file descriptors may be passed via the socket.
Definition dbus-sysdeps-unix.c:4850
_dbus_write_socket
int _dbus_write_socket(DBusSocket fd, const DBusString *buffer, int start, int len)
Like _dbus_write(), but only supports sockets and is thus available on Windows.
Definition dbus-sysdeps-unix.c:356
_dbus_atomic_set_nonzero
void _dbus_atomic_set_nonzero(DBusAtomic *atomic)
Atomically set the value of an integer to something nonzero.
Definition dbus-sysdeps-unix.c:3279
_dbus_socketpair
dbus_bool_t _dbus_socketpair(DBusSocket *fd1, DBusSocket *fd2, dbus_bool_t blocking, DBusError *error)
Creates pair of connect sockets (as in socketpair()).
Definition dbus-sysdeps-unix.c:3884
dbus_gid_t
unsigned long dbus_gid_t
A group ID.
Definition dbus-sysdeps.h:143
_dbus_read_socket_with_unix_fds
int _dbus_read_socket_with_unix_fds(DBusSocket fd, DBusString *buffer, int count, int *fds, unsigned int *n_fds)
Like _dbus_read_socket() but also tries to read unix fds from the socket.
Definition dbus-sysdeps-unix.c:394
_dbus_append_keyring_directory_for_credentials
dbus_bool_t _dbus_append_keyring_directory_for_credentials(DBusString *directory, DBusCredentials *credentials)
Appends the directory in which a keyring for the given credentials should be stored.
Definition dbus-sysdeps-unix.c:4721
DBUS_UID_UNSET
#define DBUS_UID_UNSET
an invalid UID used to represent an uninitialized dbus_uid_t field
Definition dbus-sysdeps.h:148
_dbus_atomic_dec
dbus_int32_t _dbus_atomic_dec(DBusAtomic *atomic)
Atomically decrement an integer.
Definition dbus-sysdeps-unix.c:3205
_dbus_read_credentials_socket
dbus_bool_t _dbus_read_credentials_socket(DBusSocket client_fd, DBusCredentials *credentials, DBusError *error)
Reads a single byte which must be nul (an error occurs otherwise), and reads unix credentials if avai...
Definition dbus-sysdeps-unix.c:2209
DBUS_PID_UNSET
#define DBUS_PID_UNSET
an invalid PID used to represent an uninitialized dbus_pid_t field
Definition dbus-sysdeps.h:146
_dbus_getenv
const char * _dbus_getenv(const char *varname)
Wrapper for getenv().
Definition dbus-sysdeps.c:197
_dbus_listen_unix_socket
DBusSocket _dbus_listen_unix_socket(const char *path, dbus_bool_t abstract, DBusError *error)
Creates a socket and binds it to the given path, then listens on the socket.
Definition dbus-sysdeps-unix.c:1170
_dbus_getpid
dbus_pid_t _dbus_getpid(void)
Gets our process ID.
Definition dbus-sysdeps-unix.c:3127
_dbus_atomic_get
dbus_int32_t _dbus_atomic_get(DBusAtomic *atomic)
Atomically get the value of an integer.
Definition dbus-sysdeps-unix.c:3233
_dbus_set_socket_nonblocking
dbus_bool_t _dbus_set_socket_nonblocking(DBusSocket fd, DBusError *error)
Sets a file descriptor to be nonblocking.
Definition dbus-sysdeps-unix.c:3797
_dbus_lookup_session_address
dbus_bool_t _dbus_lookup_session_address(dbus_bool_t *supported, DBusString *address, DBusError *error)
Determines the address of the session bus by querying a platform-specific method.
Definition dbus-sysdeps-unix.c:4670
_dbus_connect_tcp_socket
DBusSocket _dbus_connect_tcp_socket(const char *host, const char *port, const char *family, DBusError *error)
Creates a socket and connects to a socket at the given host and port.
Definition dbus-sysdeps-unix.c:1449
_dbus_disable_sigpipe
void _dbus_disable_sigpipe(void)
signal (SIGPIPE, SIG_IGN);
Definition dbus-sysdeps-unix.c:3670
_dbus_check_setuid
dbus_bool_t _dbus_check_setuid(void)
NOTE: If you modify this function, please also consider making the corresponding change in GLib.
Definition dbus-sysdeps-unix.c:5002
_dbus_sleep_milliseconds
void _dbus_sleep_milliseconds(int milliseconds)
Sleeps the given number of milliseconds.
Definition dbus-sysdeps-unix.c:3542
DBUS_GID_UNSET
#define DBUS_GID_UNSET
an invalid GID used to represent an uninitialized dbus_gid_t field
Definition dbus-sysdeps.h:150
_dbus_resolve_pid_fd
dbus_pid_t _dbus_resolve_pid_fd(int pid_fd)
Resolve the PID from the PID FD, if any.
Definition dbus-sysdeps-unix.c:3045
_dbus_check_dir_is_private_to_user
dbus_bool_t _dbus_check_dir_is_private_to_user(DBusString *dir, DBusError *error)
Checks to make sure the given directory is private to the user.
Definition dbus-sysdeps-unix.c:2644
_dbus_daemon_unpublish_session_bus_address
dbus_bool_t _dbus_daemon_unpublish_session_bus_address(void)
Clear the platform-specific centralized location where the session bus address is published.
Definition dbus-sysdeps-win.c:3112
_DBUS_POLLIN
#define _DBUS_POLLIN
There is data to read.
Definition dbus-sysdeps.h:444
_dbus_listen_tcp_socket
int _dbus_listen_tcp_socket(const char *host, const char *port, const char *family, DBusString *retport, const char **retfamily, DBusSocket **fds_p, DBusError *error)
Creates a socket and binds it to the given path, then listens on the socket.
Definition dbus-sysdeps-unix.c:1606
_dbus_send_credentials_socket
dbus_bool_t _dbus_send_credentials_socket(DBusSocket server_fd, DBusError *error)
Sends a single nul byte with our UNIX credentials as ancillary data.
Definition dbus-sysdeps-unix.c:2568
_dbus_getuid
dbus_uid_t _dbus_getuid(void)
Gets our UID.
Definition dbus-sysdeps-unix.c:3136
_dbus_credentials_add_from_current_process
dbus_bool_t _dbus_credentials_add_from_current_process(DBusCredentials *credentials)
Adds the most important credentials of the current process (the uid and pid) to the passed-in credent...
Definition dbus-sysdeps-unix.c:3005
_dbus_close_socket
dbus_bool_t _dbus_close_socket(DBusSocket *fd, DBusError *error)
Closes a socket and invalidates it.
Definition dbus-sysdeps-unix.c:314
_dbus_connect_unix_socket
DBusSocket _dbus_connect_unix_socket(const char *path, dbus_bool_t abstract, DBusError *error)
Creates a socket and connects it to the UNIX domain socket at the given path.
Definition dbus-sysdeps-unix.c:957
_dbus_atomic_set_zero
void _dbus_atomic_set_zero(DBusAtomic *atomic)
Atomically set the value of an integer to 0.
Definition dbus-sysdeps-unix.c:3258
_dbus_atomic_inc
dbus_int32_t _dbus_atomic_inc(DBusAtomic *atomic)
Atomically increments an integer.
Definition dbus-sysdeps-unix.c:3178
_dbus_generate_random_bytes
dbus_bool_t _dbus_generate_random_bytes(DBusString *str, int n_bytes, DBusError *error)
Generates the given number of securely random bytes, using the best mechanism we can come up with.
Definition dbus-sysdeps-unix.c:3572
DBUS_GID_FORMAT
#define DBUS_GID_FORMAT
an appropriate printf format for dbus_gid_t
Definition dbus-sysdeps.h:157
_dbus_printf_string_upper_bound
int _dbus_printf_string_upper_bound(const char *format, va_list args)
Measure the length of the given format string and arguments, not including the terminating nul.
Definition dbus-sysdeps-unix.c:3959
_dbus_delete_directory
dbus_bool_t _dbus_delete_directory(const DBusString *filename, DBusError *error)
Removes a directory; Directory must be empty.
Definition dbus-sysdeps-unix.c:4822
DBUS_UID_FORMAT
#define DBUS_UID_FORMAT
an appropriate printf format for dbus_uid_t
Definition dbus-sysdeps.h:155
_dbus_poll
int _dbus_poll(DBusPollFD *fds, int n_fds, int timeout_milliseconds)
Wrapper for poll().
Definition dbus-sysdeps-unix.c:3303
_dbus_get_autolaunch_address
dbus_bool_t _dbus_get_autolaunch_address(const char *scope, DBusString *address, DBusError *error)
Returns the address of a new session bus.
Definition dbus-sysdeps-unix.c:4299
_dbus_write_socket_two
int _dbus_write_socket_two(DBusSocket fd, const DBusString *buffer1, int start1, int len1, const DBusString *buffer2, int start2, int len2)
Like _dbus_write_two() but only works on sockets and is thus available on Windows.
Definition dbus-sysdeps-unix.c:694
_dbus_concat_dir_and_file
dbus_bool_t _dbus_concat_dir_and_file(DBusString *dir, const DBusString *next_component)
Appends the given filename to the given directory.
Definition dbus-sysdeps-unix.c:3497
_dbus_connect_exec
DBusSocket _dbus_connect_exec(const char *path, char *const argv[], DBusError *error)
Creates a UNIX domain socket and connects it to the specified process to execute.
Definition dbus-sysdeps-unix.c:1054
_dbus_print_backtrace
void _dbus_print_backtrace(void)
On GNU libc systems, print a crude backtrace to stderr.
Definition dbus-sysdeps-unix.c:3842
_dbus_get_real_time
void _dbus_get_real_time(dbus_int64_t *tv_sec, long *tv_usec)
Get current time, as in gettimeofday().
Definition dbus-sysdeps-unix.c:3412
_dbus_init_system_log
void _dbus_init_system_log(const char *tag, DBusLogFlags flags)
Initialize the system log.
Definition dbus-sysdeps-unix.c:5183
_dbus_accept
DBusSocket _dbus_accept(DBusSocket listen_fd)
Accepts a connection on a listening socket.
Definition dbus-sysdeps-unix.c:2589
_DBUS_MAX_SUN_PATH_LENGTH
#define _DBUS_MAX_SUN_PATH_LENGTH
Maximum length of the path to a UNIX domain socket, sockaddr_un::sun_path member.
Definition dbus-sysdeps.h:765
_dbus_append_user_from_current_process
dbus_bool_t _dbus_append_user_from_current_process(DBusString *str)
Append to the string the identity we would like to have when we authenticate, on UNIX this is the cur...
Definition dbus-sysdeps-unix.c:3117
_dbus_flush_caches
void _dbus_flush_caches(void)
Called when the bus daemon is signaled to reload its configuration; any caches should be nuked.
Definition dbus-sysdeps-unix.c:4702
_dbus_get_tmpdir
const char * _dbus_get_tmpdir(void)
Gets the temporary files directory by inspecting the environment variables TMPDIR,...
Definition dbus-sysdeps-unix.c:4029
DBUS_PID_FORMAT
#define DBUS_PID_FORMAT
an appropriate printf format for dbus_pid_t
Definition dbus-sysdeps.h:153
_dbus_ensure_directory
dbus_bool_t _dbus_ensure_directory(const DBusString *filename, DBusError *error)
Creates a directory; succeeds if the directory is created or already existed.
Definition dbus-sysdeps-unix.c:3434
_dbus_create_directory
dbus_bool_t _dbus_create_directory(const DBusString *filename, DBusError *error)
Creates a directory.
Definition dbus-sysdeps-unix.c:3466
dbus_uint16_t
unsigned short dbus_uint16_t
A 16-bit unsigned integer on all platforms.
Definition dbus-arch-deps.h:48
dbus_int64_t
_DBUS_GNUC_EXTENSION typedef long dbus_int64_t
A 64-bit signed integer.
Definition dbus-arch-deps.h:37
dbus_int32_t
int dbus_int32_t
A 32-bit signed integer on all platforms.
Definition dbus-arch-deps.h:44
dbus_bool_t
dbus_uint32_t dbus_bool_t
A boolean, valid values are TRUE and FALSE.
Definition dbus-types.h:37
DBusAtomic
An atomic integer safe to increment or decrement from multiple threads.
Definition dbus-sysdeps.h:340
DBusAtomic::value
volatile dbus_int32_t value
Value of the atomic integer.
Definition dbus-sysdeps.h:346
DBusCredentials
Definition dbus-credentials.c:60
DBusError
Object representing an exception.
Definition dbus-errors.h:51
DBusError::name
const char * name
public error name field
Definition dbus-errors.h:52
DBusError::message
const char * message
public error message field
Definition dbus-errors.h:53
DBusList
A node in a linked list.
Definition dbus-list.h:37
DBusPollFD
Definition dbus-sysdeps.h:437
DBusPollFD::events
short events
Events to poll for.
Definition dbus-sysdeps.h:439
DBusPollFD::revents
short revents
Events that occurred.
Definition dbus-sysdeps.h:440
DBusPollFD::fd
DBusPollable fd
File descriptor.
Definition dbus-sysdeps.h:438
DBusSocket
Socket interface.
Definition dbus-sysdeps.h:185
DBusString
Definition dbus-string.h:47
DBusUserInfo
Information about a UNIX user.
Definition dbus-sysdeps-unix.h:93
DBusUserInfo::n_group_ids
int n_group_ids
Size of group IDs array.
Definition dbus-sysdeps-unix.h:98
DBusUserInfo::uid
dbus_uid_t uid
UID.
Definition dbus-sysdeps-unix.h:95
DBusUserInfo::homedir
char * homedir
Home directory.
Definition dbus-sysdeps-unix.h:100
DBusUserInfo::group_ids
dbus_gid_t * group_ids
Groups IDs, including above primary group.
Definition dbus-sysdeps-unix.h:97
DBusUserInfo::username
char * username
Username.
Definition dbus-sysdeps-unix.h:99
DBusUserInfo::primary_gid
dbus_gid_t primary_gid
GID.
Definition dbus-sysdeps-unix.h:96
DBusGUID
A globally unique ID ; we have one for each DBusServer, and also one for each machine with libdbus in...
Definition dbus-internals.h:458