summaryrefslogtreecommitdiff
path: root/bus
diff options
context:
space:
mode:
Diffstat (limited to 'bus')
-rw-r--r--bus/Makefile.am234
-rw-r--r--bus/Makefile.in1458
-rw-r--r--bus/activation-exit-codes.h45
-rw-r--r--bus/activation-helper-bin.c100
-rw-r--r--bus/activation-helper.c563
-rw-r--r--bus/activation-helper.h31
-rw-r--r--bus/activation.c2351
-rw-r--r--bus/activation.h68
-rw-r--r--bus/bus.c1569
-rw-r--r--bus/bus.h122
-rw-r--r--bus/config-loader-expat.c294
-rw-r--r--bus/config-loader-libxml.c323
-rw-r--r--bus/config-parser-common.c183
-rw-r--r--bus/config-parser-common.h59
-rw-r--r--bus/config-parser-trivial.c696
-rw-r--r--bus/config-parser-trivial.h71
-rw-r--r--bus/config-parser.c3466
-rw-r--r--bus/config-parser.h89
-rw-r--r--bus/connection.c2303
-rw-r--r--bus/connection.h141
-rw-r--r--bus/dbus-daemon.1760
-rw-r--r--bus/dbus-daemon.1.in760
-rw-r--r--bus/desktop-file.c800
-rw-r--r--bus/desktop-file.h56
-rw-r--r--bus/dir-watch-default.c40
-rw-r--r--bus/dir-watch-dnotify.c93
-rw-r--r--bus/dir-watch-inotify.c279
-rw-r--r--bus/dir-watch-kqueue.c255
-rw-r--r--bus/dir-watch.h40
-rw-r--r--bus/dispatch.c4725
-rw-r--r--bus/dispatch.h38
-rw-r--r--bus/driver.c2022
-rw-r--r--bus/driver.h52
-rw-r--r--bus/expirelist.c412
-rw-r--r--bus/expirelist.h80
-rw-r--r--bus/main.c479
-rwxr-xr-xbus/messagebus.in92
-rw-r--r--bus/policy.c1296
-rw-r--r--bus/policy.h164
-rw-r--r--bus/rc.messagebus.in79
-rw-r--r--bus/selinux.c1091
-rw-r--r--bus/selinux.h72
-rw-r--r--bus/services.c1306
-rw-r--r--bus/services.h94
-rw-r--r--bus/session.conf.in60
-rw-r--r--bus/signals.c2030
-rw-r--r--bus/signals.h88
-rw-r--r--bus/system.conf.in83
-rw-r--r--bus/test-launch-helper.c146
-rw-r--r--bus/test-main.c151
-rw-r--r--bus/test-system.c106
-rw-r--r--bus/test.c346
-rw-r--r--bus/test.h58
-rw-r--r--bus/utils.c48
-rw-r--r--bus/utils.h36
55 files changed, 32403 insertions, 0 deletions
diff --git a/bus/Makefile.am b/bus/Makefile.am
new file mode 100644
index 00000000..ad49e6dd
--- /dev/null
+++ b/bus/Makefile.am
@@ -0,0 +1,234 @@
+
+configdir=$(sysconfdir)/dbus-1
+
+INCLUDES=-I$(top_srcdir) $(DBUS_BUS_CFLAGS) @PIE_CFLAGS@ \
+ -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \
+ -DDAEMON_NAME=\"dbus-daemon\" -DDBUS_COMPILATION
+
+EFENCE=
+
+CONFIG_IN_FILES= \
+ session.conf.in \
+ system.conf.in
+
+config_DATA= \
+ session.conf \
+ system.conf
+
+if DBUS_USE_LIBXML
+XML_SOURCES=config-loader-libxml.c
+endif
+if DBUS_USE_EXPAT
+XML_SOURCES=config-loader-expat.c
+endif
+
+if DBUS_BUS_ENABLE_KQUEUE
+DIR_WATCH_SOURCE=dir-watch-kqueue.c
+else
+if DBUS_BUS_ENABLE_INOTIFY
+DIR_WATCH_SOURCE=dir-watch-inotify.c
+else
+if DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
+DIR_WATCH_SOURCE=dir-watch-dnotify.c
+else
+DIR_WATCH_SOURCE=dir-watch-default.c
+endif
+endif
+endif
+
+BUS_SOURCES= \
+ activation.c \
+ activation.h \
+ activation-exit-codes.h \
+ bus.c \
+ bus.h \
+ config-parser.c \
+ config-parser.h \
+ config-parser-common.c \
+ config-parser-common.h \
+ connection.c \
+ connection.h \
+ desktop-file.c \
+ desktop-file.h \
+ $(DIR_WATCH_SOURCE) \
+ dir-watch.h \
+ dispatch.c \
+ dispatch.h \
+ driver.c \
+ driver.h \
+ expirelist.c \
+ expirelist.h \
+ policy.c \
+ policy.h \
+ selinux.h \
+ selinux.c \
+ services.c \
+ services.h \
+ signals.c \
+ signals.h \
+ test.c \
+ test.h \
+ utils.c \
+ utils.h \
+ $(XML_SOURCES)
+
+dbus_daemon_SOURCES= \
+ $(BUS_SOURCES) \
+ main.c
+
+dbus_daemon_LDADD= \
+ $(EFENCE) \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_BUS_LIBS)
+
+dbus_daemon_LDFLAGS=@R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@ @PIE_LDFLAGS@
+
+LAUNCH_HELPER_SOURCES= \
+ $(XML_SOURCES) \
+ config-parser-common.c \
+ config-parser-common.h \
+ config-parser-trivial.c \
+ config-parser-trivial.h \
+ desktop-file.c \
+ desktop-file.h \
+ utils.c \
+ utils.h \
+ activation-exit-codes.h \
+ activation-helper.h \
+ activation-helper.c
+
+## This is the installed launch helper with the setuid checks
+dbus_daemon_launch_helper_SOURCES= \
+ activation-helper-bin.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+dbus_daemon_launch_helper_LDADD= \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+dbus_daemon_launch_helper_LDFLAGS=@R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+
+## we build another binary so we can do the launch testing without root privs.
+## DO NOT INSTALL THIS FILE
+dbus_daemon_launch_helper_test_SOURCES= \
+ activation-helper-bin.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+dbus_daemon_launch_helper_test_LDADD= \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+dbus_daemon_launch_helper_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+dbus_daemon_launch_helper_test_CPPFLAGS= \
+ -DACTIVATION_LAUNCHER_TEST
+
+## we build yet another binary so we can do the OOM tests
+## DO NOT INSTALL THIS FILE
+bus_test_launch_helper_SOURCES= \
+ test-launch-helper.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+bus_test_launch_helper_LDADD= \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+bus_test_launch_helper_LDFLAGS=@R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+bus_test_launch_helper_CPPFLAGS= \
+ -DACTIVATION_LAUNCHER_TEST \
+ -DACTIVATION_LAUNCHER_DO_OOM
+
+## note that TESTS has special meaning (stuff to use in make check)
+## so if adding tests not to be run in make check, don't add them to
+## TESTS
+if DBUS_BUILD_TESTS
+TESTS_ENVIRONMENT=DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus DBUS_FATAL_WARNINGS=1 DBUS_BLOCK_ON_ABORT=1
+TESTS=bus-test bus-test-system bus-test-launch-helper
+else
+TESTS=
+endif
+
+## we use noinst_PROGRAMS not check_PROGRAMS so that we build
+## even when not doing "make check"
+noinst_PROGRAMS=$(TESTS) dbus-daemon dbus-daemon-launch-helper-test dbus-daemon-launch-helper
+
+bus_test_system_SOURCES= \
+ $(XML_SOURCES) \
+ config-parser-common.c \
+ config-parser-common.h \
+ config-parser-trivial.c \
+ config-parser-trivial.h \
+ utils.c \
+ utils.h \
+ test-system.c
+
+bus_test_system_LDADD=$(top_builddir)/dbus/libdbus-convenience.la $(DBUS_BUS_LIBS)
+bus_test_system_LDFLAGS=@R_DYNAMIC_LDFLAG@
+
+bus_test_SOURCES= \
+ $(BUS_SOURCES) \
+ test-main.c
+
+bus_test_LDADD=$(top_builddir)/dbus/libdbus-convenience.la $(DBUS_BUS_LIBS)
+bus_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
+
+## mop up the gcov files
+clean-local:
+ /bin/rm *.bb *.bbg *.da *.gcov || true
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon
+ rm -f $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper
+
+install-data-hook:
+ if test '!' -d $(DESTDIR)$(DBUS_DAEMONDIR); then \
+ $(mkinstalldirs) $(DESTDIR)$(DBUS_DAEMONDIR); \
+ chmod 755 $(DESTDIR)$(DBUS_DAEMONDIR); \
+ fi
+ $(INSTALL_PROGRAM) dbus-daemon $(DESTDIR)$(DBUS_DAEMONDIR)
+ $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run/dbus
+ $(mkinstalldirs) $(DESTDIR)$(configdir)/system.d
+ $(mkinstalldirs) $(DESTDIR)$(configdir)/session.d
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/dbus-1/services
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/dbus-1/system-services
+ $(mkinstalldirs) $(DESTDIR)$(libexecdir)/dbus-1
+ $(INSTALL_PROGRAM) dbus-daemon-launch-helper $(DESTDIR)$(libexecdir)
+ if test `id -u` -eq 0; then \
+ chown root:$(DBUS_USER) $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper; \
+ chmod 4750 $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper; \
+ else \
+ echo "Not installing $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper binary setuid!"; \
+ echo "You'll need to manually set permissions to root:$(DBUS_USER) and permissions 4750"; \
+ fi
+
+#### Init scripts fun
+SCRIPT_IN_FILES=messagebus.in \
+ rc.messagebus.in
+
+## Red Hat start
+if DBUS_INIT_SCRIPTS_RED_HAT
+
+initddir=$(sysconfdir)/rc.d/init.d
+
+initd_SCRIPTS= \
+ messagebus
+
+endif
+ ## Red Hat end
+
+## Slackware start
+if DBUS_INIT_SCRIPTS_SLACKWARE
+
+initddir=$(sysconfdir)/rc.d/
+
+initd_SCRIPTS= \
+ rc.messagebus
+
+endif
+## Slackware end
+
+MAN_IN_FILES=dbus-daemon.1.in
+man_MANS = dbus-daemon.1
+
+#### Extra dist
+
+EXTRA_DIST=$(CONFIG_IN_FILES) $(SCRIPT_IN_FILES) $(man_MANS) $(MAN_IN_FILES)
diff --git a/bus/Makefile.in b/bus/Makefile.in
new file mode 100644
index 00000000..13a599af
--- /dev/null
+++ b/bus/Makefile.in
@@ -0,0 +1,1458 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@DBUS_BUILD_TESTS_TRUE@TESTS = bus-test$(EXEEXT) \
+@DBUS_BUILD_TESTS_TRUE@ bus-test-system$(EXEEXT) \
+@DBUS_BUILD_TESTS_TRUE@ bus-test-launch-helper$(EXEEXT)
+noinst_PROGRAMS = $(am__EXEEXT_1) dbus-daemon$(EXEEXT) \
+ dbus-daemon-launch-helper-test$(EXEEXT) \
+ dbus-daemon-launch-helper$(EXEEXT)
+subdir = bus
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/dbus-daemon.1.in $(srcdir)/messagebus.in \
+ $(srcdir)/rc.messagebus.in $(srcdir)/session.conf.in \
+ $(srcdir)/system.conf.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = system.conf session.conf messagebus rc.messagebus \
+ dbus-daemon.1
+CONFIG_CLEAN_VPATH_FILES =
+@DBUS_BUILD_TESTS_TRUE@am__EXEEXT_1 = bus-test$(EXEEXT) \
+@DBUS_BUILD_TESTS_TRUE@ bus-test-system$(EXEEXT) \
+@DBUS_BUILD_TESTS_TRUE@ bus-test-launch-helper$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+am__bus_test_SOURCES_DIST = activation.c activation.h \
+ activation-exit-codes.h bus.c bus.h config-parser.c \
+ config-parser.h config-parser-common.c config-parser-common.h \
+ connection.c connection.h desktop-file.c desktop-file.h \
+ dir-watch-default.c dir-watch-dnotify.c dir-watch-inotify.c \
+ dir-watch-kqueue.c dir-watch.h dispatch.c dispatch.h driver.c \
+ driver.h expirelist.c expirelist.h policy.c policy.h selinux.h \
+ selinux.c services.c services.h signals.c signals.h test.c \
+ test.h utils.c utils.h config-loader-expat.c \
+ config-loader-libxml.c test-main.c
+@DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX_FALSE@@DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-default.$(OBJEXT)
+@DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX_TRUE@@DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-dnotify.$(OBJEXT)
+@DBUS_BUS_ENABLE_INOTIFY_TRUE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-inotify.$(OBJEXT)
+@DBUS_BUS_ENABLE_KQUEUE_TRUE@am__objects_1 = \
+@DBUS_BUS_ENABLE_KQUEUE_TRUE@ dir-watch-kqueue.$(OBJEXT)
+@DBUS_USE_EXPAT_FALSE@@DBUS_USE_LIBXML_TRUE@am__objects_2 = config-loader-libxml.$(OBJEXT)
+@DBUS_USE_EXPAT_TRUE@am__objects_2 = config-loader-expat.$(OBJEXT)
+am__objects_3 = activation.$(OBJEXT) bus.$(OBJEXT) \
+ config-parser.$(OBJEXT) config-parser-common.$(OBJEXT) \
+ connection.$(OBJEXT) desktop-file.$(OBJEXT) $(am__objects_1) \
+ dispatch.$(OBJEXT) driver.$(OBJEXT) expirelist.$(OBJEXT) \
+ policy.$(OBJEXT) selinux.$(OBJEXT) services.$(OBJEXT) \
+ signals.$(OBJEXT) test.$(OBJEXT) utils.$(OBJEXT) \
+ $(am__objects_2)
+am_bus_test_OBJECTS = $(am__objects_3) test-main.$(OBJEXT)
+bus_test_OBJECTS = $(am_bus_test_OBJECTS)
+am__DEPENDENCIES_1 =
+bus_test_DEPENDENCIES = $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+bus_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(bus_test_LDFLAGS) $(LDFLAGS) -o $@
+am__bus_test_launch_helper_SOURCES_DIST = test-launch-helper.c \
+ config-loader-expat.c config-loader-libxml.c \
+ config-parser-common.c config-parser-common.h \
+ config-parser-trivial.c config-parser-trivial.h desktop-file.c \
+ desktop-file.h utils.c utils.h activation-exit-codes.h \
+ activation-helper.h activation-helper.c
+@DBUS_USE_EXPAT_FALSE@@DBUS_USE_LIBXML_TRUE@am__objects_4 = bus_test_launch_helper-config-loader-libxml.$(OBJEXT)
+@DBUS_USE_EXPAT_TRUE@am__objects_4 = bus_test_launch_helper-config-loader-expat.$(OBJEXT)
+am__objects_5 = $(am__objects_4) \
+ bus_test_launch_helper-config-parser-common.$(OBJEXT) \
+ bus_test_launch_helper-config-parser-trivial.$(OBJEXT) \
+ bus_test_launch_helper-desktop-file.$(OBJEXT) \
+ bus_test_launch_helper-utils.$(OBJEXT) \
+ bus_test_launch_helper-activation-helper.$(OBJEXT)
+am_bus_test_launch_helper_OBJECTS = \
+ bus_test_launch_helper-test-launch-helper.$(OBJEXT) \
+ $(am__objects_5)
+bus_test_launch_helper_OBJECTS = $(am_bus_test_launch_helper_OBJECTS)
+bus_test_launch_helper_DEPENDENCIES = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+bus_test_launch_helper_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(bus_test_launch_helper_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__bus_test_system_SOURCES_DIST = config-loader-expat.c \
+ config-loader-libxml.c config-parser-common.c \
+ config-parser-common.h config-parser-trivial.c \
+ config-parser-trivial.h utils.c utils.h test-system.c
+am_bus_test_system_OBJECTS = $(am__objects_2) \
+ config-parser-common.$(OBJEXT) config-parser-trivial.$(OBJEXT) \
+ utils.$(OBJEXT) test-system.$(OBJEXT)
+bus_test_system_OBJECTS = $(am_bus_test_system_OBJECTS)
+bus_test_system_DEPENDENCIES = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+bus_test_system_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(bus_test_system_LDFLAGS) $(LDFLAGS) \
+ -o $@
+am__dbus_daemon_SOURCES_DIST = activation.c activation.h \
+ activation-exit-codes.h bus.c bus.h config-parser.c \
+ config-parser.h config-parser-common.c config-parser-common.h \
+ connection.c connection.h desktop-file.c desktop-file.h \
+ dir-watch-default.c dir-watch-dnotify.c dir-watch-inotify.c \
+ dir-watch-kqueue.c dir-watch.h dispatch.c dispatch.h driver.c \
+ driver.h expirelist.c expirelist.h policy.c policy.h selinux.h \
+ selinux.c services.c services.h signals.c signals.h test.c \
+ test.h utils.c utils.h config-loader-expat.c \
+ config-loader-libxml.c main.c
+am_dbus_daemon_OBJECTS = $(am__objects_3) main.$(OBJEXT)
+dbus_daemon_OBJECTS = $(am_dbus_daemon_OBJECTS)
+dbus_daemon_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+dbus_daemon_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(dbus_daemon_LDFLAGS) $(LDFLAGS) -o $@
+am__dbus_daemon_launch_helper_SOURCES_DIST = activation-helper-bin.c \
+ config-loader-expat.c config-loader-libxml.c \
+ config-parser-common.c config-parser-common.h \
+ config-parser-trivial.c config-parser-trivial.h desktop-file.c \
+ desktop-file.h utils.c utils.h activation-exit-codes.h \
+ activation-helper.h activation-helper.c
+am__objects_6 = $(am__objects_2) config-parser-common.$(OBJEXT) \
+ config-parser-trivial.$(OBJEXT) desktop-file.$(OBJEXT) \
+ utils.$(OBJEXT) activation-helper.$(OBJEXT)
+am_dbus_daemon_launch_helper_OBJECTS = \
+ activation-helper-bin.$(OBJEXT) $(am__objects_6)
+dbus_daemon_launch_helper_OBJECTS = \
+ $(am_dbus_daemon_launch_helper_OBJECTS)
+dbus_daemon_launch_helper_DEPENDENCIES = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+dbus_daemon_launch_helper_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(dbus_daemon_launch_helper_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__dbus_daemon_launch_helper_test_SOURCES_DIST = \
+ activation-helper-bin.c config-loader-expat.c \
+ config-loader-libxml.c config-parser-common.c \
+ config-parser-common.h config-parser-trivial.c \
+ config-parser-trivial.h desktop-file.c desktop-file.h utils.c \
+ utils.h activation-exit-codes.h activation-helper.h \
+ activation-helper.c
+@DBUS_USE_EXPAT_FALSE@@DBUS_USE_LIBXML_TRUE@am__objects_7 = dbus_daemon_launch_helper_test-config-loader-libxml.$(OBJEXT)
+@DBUS_USE_EXPAT_TRUE@am__objects_7 = dbus_daemon_launch_helper_test-config-loader-expat.$(OBJEXT)
+am__objects_8 = $(am__objects_7) \
+ dbus_daemon_launch_helper_test-config-parser-common.$(OBJEXT) \
+ dbus_daemon_launch_helper_test-config-parser-trivial.$(OBJEXT) \
+ dbus_daemon_launch_helper_test-desktop-file.$(OBJEXT) \
+ dbus_daemon_launch_helper_test-utils.$(OBJEXT) \
+ dbus_daemon_launch_helper_test-activation-helper.$(OBJEXT)
+am_dbus_daemon_launch_helper_test_OBJECTS = dbus_daemon_launch_helper_test-activation-helper-bin.$(OBJEXT) \
+ $(am__objects_8)
+dbus_daemon_launch_helper_test_OBJECTS = \
+ $(am_dbus_daemon_launch_helper_test_OBJECTS)
+dbus_daemon_launch_helper_test_DEPENDENCIES = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(am__DEPENDENCIES_1)
+dbus_daemon_launch_helper_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) \
+ $(dbus_daemon_launch_helper_test_LDFLAGS) $(LDFLAGS) -o $@
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(initddir)" "$(DESTDIR)$(man1dir)" \
+ "$(DESTDIR)$(configdir)"
+SCRIPTS = $(initd_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(bus_test_SOURCES) $(bus_test_launch_helper_SOURCES) \
+ $(bus_test_system_SOURCES) $(dbus_daemon_SOURCES) \
+ $(dbus_daemon_launch_helper_SOURCES) \
+ $(dbus_daemon_launch_helper_test_SOURCES)
+DIST_SOURCES = $(am__bus_test_SOURCES_DIST) \
+ $(am__bus_test_launch_helper_SOURCES_DIST) \
+ $(am__bus_test_system_SOURCES_DIST) \
+ $(am__dbus_daemon_SOURCES_DIST) \
+ $(am__dbus_daemon_launch_helper_SOURCES_DIST) \
+ $(am__dbus_daemon_launch_helper_test_SOURCES_DIST)
+man1dir = $(mandir)/man1
+NROFF = nroff
+MANS = $(man_MANS)
+DATA = $(config_DATA)
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_BINDIR = @DBUS_BINDIR@
+DBUS_BUS_CFLAGS = @DBUS_BUS_CFLAGS@
+DBUS_BUS_LIBS = @DBUS_BUS_LIBS@
+DBUS_CLIENT_CFLAGS = @DBUS_CLIENT_CFLAGS@
+DBUS_CLIENT_LIBS = @DBUS_CLIENT_LIBS@
+DBUS_CONSOLE_AUTH_DIR = @DBUS_CONSOLE_AUTH_DIR@
+DBUS_CONSOLE_OWNER_FILE = @DBUS_CONSOLE_OWNER_FILE@
+DBUS_DAEMONDIR = @DBUS_DAEMONDIR@
+DBUS_DATADIR = @DBUS_DATADIR@
+DBUS_HAVE_INT64 = @DBUS_HAVE_INT64@
+DBUS_INT16_TYPE = @DBUS_INT16_TYPE@
+DBUS_INT32_TYPE = @DBUS_INT32_TYPE@
+DBUS_INT64_CONSTANT = @DBUS_INT64_CONSTANT@
+DBUS_INT64_TYPE = @DBUS_INT64_TYPE@
+DBUS_LAUNCHER_CFLAGS = @DBUS_LAUNCHER_CFLAGS@
+DBUS_LAUNCHER_LIBS = @DBUS_LAUNCHER_LIBS@
+DBUS_LIBEXECDIR = @DBUS_LIBEXECDIR@
+DBUS_MAJOR_VERSION = @DBUS_MAJOR_VERSION@
+DBUS_MICRO_VERSION = @DBUS_MICRO_VERSION@
+DBUS_MINOR_VERSION = @DBUS_MINOR_VERSION@
+DBUS_PATH_OR_ABSTRACT = @DBUS_PATH_OR_ABSTRACT@
+DBUS_SESSION_SOCKET_DIR = @DBUS_SESSION_SOCKET_DIR@
+DBUS_SYSTEM_BUS_DEFAULT_ADDRESS = @DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@
+DBUS_SYSTEM_PID_FILE = @DBUS_SYSTEM_PID_FILE@
+DBUS_SYSTEM_SOCKET = @DBUS_SYSTEM_SOCKET@
+DBUS_TEST_CFLAGS = @DBUS_TEST_CFLAGS@
+DBUS_TEST_LIBS = @DBUS_TEST_LIBS@
+DBUS_UINT64_CONSTANT = @DBUS_UINT64_CONSTANT@
+DBUS_USER = @DBUS_USER@
+DBUS_VERSION = @DBUS_VERSION@
+DBUS_X_CFLAGS = @DBUS_X_CFLAGS@
+DBUS_X_LIBS = @DBUS_X_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPANDED_BINDIR = @EXPANDED_BINDIR@
+EXPANDED_DATADIR = @EXPANDED_DATADIR@
+EXPANDED_LIBDIR = @EXPANDED_LIBDIR@
+EXPANDED_LIBEXECDIR = @EXPANDED_LIBEXECDIR@
+EXPANDED_LOCALSTATEDIR = @EXPANDED_LOCALSTATEDIR@
+EXPANDED_SYSCONFDIR = @EXPANDED_SYSCONFDIR@
+FGREP = @FGREP@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBXML_CFLAGS = @LIBXML_CFLAGS@
+LIBXML_LIBS = @LIBXML_LIBS@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_AGE = @LT_AGE@
+LT_CURRENT = @LT_CURRENT@
+LT_REVISION = @LT_REVISION@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PIC_CFLAGS = @PIC_CFLAGS@
+PIC_LDFLAGS = @PIC_LDFLAGS@
+PIE_CFLAGS = @PIE_CFLAGS@
+PIE_LDFLAGS = @PIE_LDFLAGS@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+R_DYNAMIC_LDFLAG = @R_DYNAMIC_LDFLAG@
+SECTION_FLAGS = @SECTION_FLAGS@
+SECTION_LDFLAGS = @SECTION_LDFLAGS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TEST_BUS_BINARY = @TEST_BUS_BINARY@
+TEST_EXIT_BINARY = @TEST_EXIT_BINARY@
+TEST_INVALID_SERVICE_DIR = @TEST_INVALID_SERVICE_DIR@
+TEST_INVALID_SERVICE_SYSTEM_DIR = @TEST_INVALID_SERVICE_SYSTEM_DIR@
+TEST_LAUNCH_HELPER_BINARY = @TEST_LAUNCH_HELPER_BINARY@
+TEST_PRIVSERVER_BINARY = @TEST_PRIVSERVER_BINARY@
+TEST_SEGFAULT_BINARY = @TEST_SEGFAULT_BINARY@
+TEST_SERVICE_BINARY = @TEST_SERVICE_BINARY@
+TEST_SHELL_SERVICE_BINARY = @TEST_SHELL_SERVICE_BINARY@
+TEST_SLEEP_FOREVER_BINARY = @TEST_SLEEP_FOREVER_BINARY@
+TEST_SOCKET_DIR = @TEST_SOCKET_DIR@
+TEST_VALID_SERVICE_DIR = @TEST_VALID_SERVICE_DIR@
+TEST_VALID_SERVICE_SYSTEM_DIR = @TEST_VALID_SERVICE_SYSTEM_DIR@
+VERSION = @VERSION@
+XMKMF = @XMKMF@
+XMLTO = @XMLTO@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+configdir = $(sysconfdir)/dbus-1
+INCLUDES = -I$(top_srcdir) $(DBUS_BUS_CFLAGS) @PIE_CFLAGS@ \
+ -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \
+ -DDAEMON_NAME=\"dbus-daemon\" -DDBUS_COMPILATION
+
+EFENCE =
+CONFIG_IN_FILES = \
+ session.conf.in \
+ system.conf.in
+
+config_DATA = \
+ session.conf \
+ system.conf
+
+@DBUS_USE_EXPAT_TRUE@XML_SOURCES = config-loader-expat.c
+@DBUS_USE_LIBXML_TRUE@XML_SOURCES = config-loader-libxml.c
+@DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX_FALSE@@DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@DIR_WATCH_SOURCE = dir-watch-default.c
+@DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX_TRUE@@DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@DIR_WATCH_SOURCE = dir-watch-dnotify.c
+@DBUS_BUS_ENABLE_INOTIFY_TRUE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@DIR_WATCH_SOURCE = dir-watch-inotify.c
+@DBUS_BUS_ENABLE_KQUEUE_TRUE@DIR_WATCH_SOURCE = dir-watch-kqueue.c
+BUS_SOURCES = \
+ activation.c \
+ activation.h \
+ activation-exit-codes.h \
+ bus.c \
+ bus.h \
+ config-parser.c \
+ config-parser.h \
+ config-parser-common.c \
+ config-parser-common.h \
+ connection.c \
+ connection.h \
+ desktop-file.c \
+ desktop-file.h \
+ $(DIR_WATCH_SOURCE) \
+ dir-watch.h \
+ dispatch.c \
+ dispatch.h \
+ driver.c \
+ driver.h \
+ expirelist.c \
+ expirelist.h \
+ policy.c \
+ policy.h \
+ selinux.h \
+ selinux.c \
+ services.c \
+ services.h \
+ signals.c \
+ signals.h \
+ test.c \
+ test.h \
+ utils.c \
+ utils.h \
+ $(XML_SOURCES)
+
+dbus_daemon_SOURCES = \
+ $(BUS_SOURCES) \
+ main.c
+
+dbus_daemon_LDADD = \
+ $(EFENCE) \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_BUS_LIBS)
+
+dbus_daemon_LDFLAGS = @R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@ @PIE_LDFLAGS@
+LAUNCH_HELPER_SOURCES = \
+ $(XML_SOURCES) \
+ config-parser-common.c \
+ config-parser-common.h \
+ config-parser-trivial.c \
+ config-parser-trivial.h \
+ desktop-file.c \
+ desktop-file.h \
+ utils.c \
+ utils.h \
+ activation-exit-codes.h \
+ activation-helper.h \
+ activation-helper.c
+
+dbus_daemon_launch_helper_SOURCES = \
+ activation-helper-bin.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+dbus_daemon_launch_helper_LDADD = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+dbus_daemon_launch_helper_LDFLAGS = @R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+dbus_daemon_launch_helper_test_SOURCES = \
+ activation-helper-bin.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+dbus_daemon_launch_helper_test_LDADD = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+dbus_daemon_launch_helper_test_LDFLAGS = @R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+dbus_daemon_launch_helper_test_CPPFLAGS = \
+ -DACTIVATION_LAUNCHER_TEST
+
+bus_test_launch_helper_SOURCES = \
+ test-launch-helper.c \
+ $(LAUNCH_HELPER_SOURCES)
+
+bus_test_launch_helper_LDADD = \
+ $(top_builddir)/dbus/libdbus-convenience.la \
+ $(DBUS_LAUNCHER_LIBS)
+
+bus_test_launch_helper_LDFLAGS = @R_DYNAMIC_LDFLAG@ @SECTION_LDFLAGS@
+bus_test_launch_helper_CPPFLAGS = \
+ -DACTIVATION_LAUNCHER_TEST \
+ -DACTIVATION_LAUNCHER_DO_OOM
+
+@DBUS_BUILD_TESTS_TRUE@TESTS_ENVIRONMENT = DBUS_TEST_DATA=$(top_builddir)/test/data DBUS_TEST_HOMEDIR=$(top_builddir)/dbus DBUS_FATAL_WARNINGS=1 DBUS_BLOCK_ON_ABORT=1
+bus_test_system_SOURCES = \
+ $(XML_SOURCES) \
+ config-parser-common.c \
+ config-parser-common.h \
+ config-parser-trivial.c \
+ config-parser-trivial.h \
+ utils.c \
+ utils.h \
+ test-system.c
+
+bus_test_system_LDADD = $(top_builddir)/dbus/libdbus-convenience.la $(DBUS_BUS_LIBS)
+bus_test_system_LDFLAGS = @R_DYNAMIC_LDFLAG@
+bus_test_SOURCES = \
+ $(BUS_SOURCES) \
+ test-main.c
+
+bus_test_LDADD = $(top_builddir)/dbus/libdbus-convenience.la $(DBUS_BUS_LIBS)
+bus_test_LDFLAGS = @R_DYNAMIC_LDFLAG@
+
+#### Init scripts fun
+SCRIPT_IN_FILES = messagebus.in \
+ rc.messagebus.in
+
+@DBUS_INIT_SCRIPTS_RED_HAT_TRUE@initddir = $(sysconfdir)/rc.d/init.d
+@DBUS_INIT_SCRIPTS_SLACKWARE_TRUE@initddir = $(sysconfdir)/rc.d/
+@DBUS_INIT_SCRIPTS_RED_HAT_TRUE@initd_SCRIPTS = \
+@DBUS_INIT_SCRIPTS_RED_HAT_TRUE@ messagebus
+
+@DBUS_INIT_SCRIPTS_SLACKWARE_TRUE@initd_SCRIPTS = \
+@DBUS_INIT_SCRIPTS_SLACKWARE_TRUE@ rc.messagebus
+
+MAN_IN_FILES = dbus-daemon.1.in
+man_MANS = dbus-daemon.1
+
+#### Extra dist
+EXTRA_DIST = $(CONFIG_IN_FILES) $(SCRIPT_IN_FILES) $(man_MANS) $(MAN_IN_FILES)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bus/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu bus/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+system.conf: $(top_builddir)/config.status $(srcdir)/system.conf.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+session.conf: $(top_builddir)/config.status $(srcdir)/session.conf.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+messagebus: $(top_builddir)/config.status $(srcdir)/messagebus.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+rc.messagebus: $(top_builddir)/config.status $(srcdir)/rc.messagebus.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+dbus-daemon.1: $(top_builddir)/config.status $(srcdir)/dbus-daemon.1.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+bus-test$(EXEEXT): $(bus_test_OBJECTS) $(bus_test_DEPENDENCIES)
+ @rm -f bus-test$(EXEEXT)
+ $(AM_V_CCLD)$(bus_test_LINK) $(bus_test_OBJECTS) $(bus_test_LDADD) $(LIBS)
+bus-test-launch-helper$(EXEEXT): $(bus_test_launch_helper_OBJECTS) $(bus_test_launch_helper_DEPENDENCIES)
+ @rm -f bus-test-launch-helper$(EXEEXT)
+ $(AM_V_CCLD)$(bus_test_launch_helper_LINK) $(bus_test_launch_helper_OBJECTS) $(bus_test_launch_helper_LDADD) $(LIBS)
+bus-test-system$(EXEEXT): $(bus_test_system_OBJECTS) $(bus_test_system_DEPENDENCIES)
+ @rm -f bus-test-system$(EXEEXT)
+ $(AM_V_CCLD)$(bus_test_system_LINK) $(bus_test_system_OBJECTS) $(bus_test_system_LDADD) $(LIBS)
+dbus-daemon$(EXEEXT): $(dbus_daemon_OBJECTS) $(dbus_daemon_DEPENDENCIES)
+ @rm -f dbus-daemon$(EXEEXT)
+ $(AM_V_CCLD)$(dbus_daemon_LINK) $(dbus_daemon_OBJECTS) $(dbus_daemon_LDADD) $(LIBS)
+dbus-daemon-launch-helper$(EXEEXT): $(dbus_daemon_launch_helper_OBJECTS) $(dbus_daemon_launch_helper_DEPENDENCIES)
+ @rm -f dbus-daemon-launch-helper$(EXEEXT)
+ $(AM_V_CCLD)$(dbus_daemon_launch_helper_LINK) $(dbus_daemon_launch_helper_OBJECTS) $(dbus_daemon_launch_helper_LDADD) $(LIBS)
+dbus-daemon-launch-helper-test$(EXEEXT): $(dbus_daemon_launch_helper_test_OBJECTS) $(dbus_daemon_launch_helper_test_DEPENDENCIES)
+ @rm -f dbus-daemon-launch-helper-test$(EXEEXT)
+ $(AM_V_CCLD)$(dbus_daemon_launch_helper_test_LINK) $(dbus_daemon_launch_helper_test_OBJECTS) $(dbus_daemon_launch_helper_test_LDADD) $(LIBS)
+install-initdSCRIPTS: $(initd_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ test -z "$(initddir)" || $(MKDIR_P) "$(DESTDIR)$(initddir)"
+ @list='$(initd_SCRIPTS)'; test -n "$(initddir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(initddir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(initddir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-initdSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(initd_SCRIPTS)'; test -n "$(initddir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(initddir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(initddir)" && rm -f $$files
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper-bin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-activation-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-config-loader-expat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-config-parser-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-desktop-file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-test-launch-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus_test_launch_helper-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-loader-expat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-loader-libxml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser-trivial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus_daemon_launch_helper_test-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/desktop-file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir-watch-default.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir-watch-dnotify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir-watch-inotify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir-watch-kqueue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/driver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expirelist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/policy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selinux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/services.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signals.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-system.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+bus_test_launch_helper-test-launch-helper.o: test-launch-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-test-launch-helper.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Tpo -c -o bus_test_launch_helper-test-launch-helper.o `test -f 'test-launch-helper.c' || echo '$(srcdir)/'`test-launch-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Tpo $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-launch-helper.c' object='bus_test_launch_helper-test-launch-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-test-launch-helper.o `test -f 'test-launch-helper.c' || echo '$(srcdir)/'`test-launch-helper.c
+
+bus_test_launch_helper-test-launch-helper.obj: test-launch-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-test-launch-helper.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Tpo -c -o bus_test_launch_helper-test-launch-helper.obj `if test -f 'test-launch-helper.c'; then $(CYGPATH_W) 'test-launch-helper.c'; else $(CYGPATH_W) '$(srcdir)/test-launch-helper.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Tpo $(DEPDIR)/bus_test_launch_helper-test-launch-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test-launch-helper.c' object='bus_test_launch_helper-test-launch-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-test-launch-helper.obj `if test -f 'test-launch-helper.c'; then $(CYGPATH_W) 'test-launch-helper.c'; else $(CYGPATH_W) '$(srcdir)/test-launch-helper.c'; fi`
+
+bus_test_launch_helper-config-loader-expat.o: config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-loader-expat.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Tpo -c -o bus_test_launch_helper-config-loader-expat.o `test -f 'config-loader-expat.c' || echo '$(srcdir)/'`config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Tpo $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-expat.c' object='bus_test_launch_helper-config-loader-expat.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-loader-expat.o `test -f 'config-loader-expat.c' || echo '$(srcdir)/'`config-loader-expat.c
+
+bus_test_launch_helper-config-loader-expat.obj: config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-loader-expat.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Tpo -c -o bus_test_launch_helper-config-loader-expat.obj `if test -f 'config-loader-expat.c'; then $(CYGPATH_W) 'config-loader-expat.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-expat.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Tpo $(DEPDIR)/bus_test_launch_helper-config-loader-expat.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-expat.c' object='bus_test_launch_helper-config-loader-expat.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-loader-expat.obj `if test -f 'config-loader-expat.c'; then $(CYGPATH_W) 'config-loader-expat.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-expat.c'; fi`
+
+bus_test_launch_helper-config-loader-libxml.o: config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-loader-libxml.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Tpo -c -o bus_test_launch_helper-config-loader-libxml.o `test -f 'config-loader-libxml.c' || echo '$(srcdir)/'`config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Tpo $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-libxml.c' object='bus_test_launch_helper-config-loader-libxml.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-loader-libxml.o `test -f 'config-loader-libxml.c' || echo '$(srcdir)/'`config-loader-libxml.c
+
+bus_test_launch_helper-config-loader-libxml.obj: config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-loader-libxml.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Tpo -c -o bus_test_launch_helper-config-loader-libxml.obj `if test -f 'config-loader-libxml.c'; then $(CYGPATH_W) 'config-loader-libxml.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-libxml.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Tpo $(DEPDIR)/bus_test_launch_helper-config-loader-libxml.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-libxml.c' object='bus_test_launch_helper-config-loader-libxml.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-loader-libxml.obj `if test -f 'config-loader-libxml.c'; then $(CYGPATH_W) 'config-loader-libxml.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-libxml.c'; fi`
+
+bus_test_launch_helper-config-parser-common.o: config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-parser-common.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-parser-common.Tpo -c -o bus_test_launch_helper-config-parser-common.o `test -f 'config-parser-common.c' || echo '$(srcdir)/'`config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-parser-common.Tpo $(DEPDIR)/bus_test_launch_helper-config-parser-common.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-common.c' object='bus_test_launch_helper-config-parser-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-parser-common.o `test -f 'config-parser-common.c' || echo '$(srcdir)/'`config-parser-common.c
+
+bus_test_launch_helper-config-parser-common.obj: config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-parser-common.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-parser-common.Tpo -c -o bus_test_launch_helper-config-parser-common.obj `if test -f 'config-parser-common.c'; then $(CYGPATH_W) 'config-parser-common.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-common.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-parser-common.Tpo $(DEPDIR)/bus_test_launch_helper-config-parser-common.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-common.c' object='bus_test_launch_helper-config-parser-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-parser-common.obj `if test -f 'config-parser-common.c'; then $(CYGPATH_W) 'config-parser-common.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-common.c'; fi`
+
+bus_test_launch_helper-config-parser-trivial.o: config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-parser-trivial.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Tpo -c -o bus_test_launch_helper-config-parser-trivial.o `test -f 'config-parser-trivial.c' || echo '$(srcdir)/'`config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Tpo $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-trivial.c' object='bus_test_launch_helper-config-parser-trivial.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-parser-trivial.o `test -f 'config-parser-trivial.c' || echo '$(srcdir)/'`config-parser-trivial.c
+
+bus_test_launch_helper-config-parser-trivial.obj: config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-config-parser-trivial.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Tpo -c -o bus_test_launch_helper-config-parser-trivial.obj `if test -f 'config-parser-trivial.c'; then $(CYGPATH_W) 'config-parser-trivial.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-trivial.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Tpo $(DEPDIR)/bus_test_launch_helper-config-parser-trivial.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-trivial.c' object='bus_test_launch_helper-config-parser-trivial.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-config-parser-trivial.obj `if test -f 'config-parser-trivial.c'; then $(CYGPATH_W) 'config-parser-trivial.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-trivial.c'; fi`
+
+bus_test_launch_helper-desktop-file.o: desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-desktop-file.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-desktop-file.Tpo -c -o bus_test_launch_helper-desktop-file.o `test -f 'desktop-file.c' || echo '$(srcdir)/'`desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-desktop-file.Tpo $(DEPDIR)/bus_test_launch_helper-desktop-file.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='desktop-file.c' object='bus_test_launch_helper-desktop-file.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-desktop-file.o `test -f 'desktop-file.c' || echo '$(srcdir)/'`desktop-file.c
+
+bus_test_launch_helper-desktop-file.obj: desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-desktop-file.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-desktop-file.Tpo -c -o bus_test_launch_helper-desktop-file.obj `if test -f 'desktop-file.c'; then $(CYGPATH_W) 'desktop-file.c'; else $(CYGPATH_W) '$(srcdir)/desktop-file.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-desktop-file.Tpo $(DEPDIR)/bus_test_launch_helper-desktop-file.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='desktop-file.c' object='bus_test_launch_helper-desktop-file.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-desktop-file.obj `if test -f 'desktop-file.c'; then $(CYGPATH_W) 'desktop-file.c'; else $(CYGPATH_W) '$(srcdir)/desktop-file.c'; fi`
+
+bus_test_launch_helper-utils.o: utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-utils.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-utils.Tpo -c -o bus_test_launch_helper-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-utils.Tpo $(DEPDIR)/bus_test_launch_helper-utils.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils.c' object='bus_test_launch_helper-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+
+bus_test_launch_helper-utils.obj: utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-utils.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-utils.Tpo -c -o bus_test_launch_helper-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-utils.Tpo $(DEPDIR)/bus_test_launch_helper-utils.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils.c' object='bus_test_launch_helper-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+
+bus_test_launch_helper-activation-helper.o: activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-activation-helper.o -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-activation-helper.Tpo -c -o bus_test_launch_helper-activation-helper.o `test -f 'activation-helper.c' || echo '$(srcdir)/'`activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-activation-helper.Tpo $(DEPDIR)/bus_test_launch_helper-activation-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper.c' object='bus_test_launch_helper-activation-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-activation-helper.o `test -f 'activation-helper.c' || echo '$(srcdir)/'`activation-helper.c
+
+bus_test_launch_helper-activation-helper.obj: activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT bus_test_launch_helper-activation-helper.obj -MD -MP -MF $(DEPDIR)/bus_test_launch_helper-activation-helper.Tpo -c -o bus_test_launch_helper-activation-helper.obj `if test -f 'activation-helper.c'; then $(CYGPATH_W) 'activation-helper.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/bus_test_launch_helper-activation-helper.Tpo $(DEPDIR)/bus_test_launch_helper-activation-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper.c' object='bus_test_launch_helper-activation-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(bus_test_launch_helper_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o bus_test_launch_helper-activation-helper.obj `if test -f 'activation-helper.c'; then $(CYGPATH_W) 'activation-helper.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper.c'; fi`
+
+dbus_daemon_launch_helper_test-activation-helper-bin.o: activation-helper-bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-activation-helper-bin.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Tpo -c -o dbus_daemon_launch_helper_test-activation-helper-bin.o `test -f 'activation-helper-bin.c' || echo '$(srcdir)/'`activation-helper-bin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper-bin.c' object='dbus_daemon_launch_helper_test-activation-helper-bin.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-activation-helper-bin.o `test -f 'activation-helper-bin.c' || echo '$(srcdir)/'`activation-helper-bin.c
+
+dbus_daemon_launch_helper_test-activation-helper-bin.obj: activation-helper-bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-activation-helper-bin.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Tpo -c -o dbus_daemon_launch_helper_test-activation-helper-bin.obj `if test -f 'activation-helper-bin.c'; then $(CYGPATH_W) 'activation-helper-bin.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper-bin.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper-bin.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper-bin.c' object='dbus_daemon_launch_helper_test-activation-helper-bin.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-activation-helper-bin.obj `if test -f 'activation-helper-bin.c'; then $(CYGPATH_W) 'activation-helper-bin.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper-bin.c'; fi`
+
+dbus_daemon_launch_helper_test-config-loader-expat.o: config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-loader-expat.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Tpo -c -o dbus_daemon_launch_helper_test-config-loader-expat.o `test -f 'config-loader-expat.c' || echo '$(srcdir)/'`config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-expat.c' object='dbus_daemon_launch_helper_test-config-loader-expat.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-loader-expat.o `test -f 'config-loader-expat.c' || echo '$(srcdir)/'`config-loader-expat.c
+
+dbus_daemon_launch_helper_test-config-loader-expat.obj: config-loader-expat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-loader-expat.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Tpo -c -o dbus_daemon_launch_helper_test-config-loader-expat.obj `if test -f 'config-loader-expat.c'; then $(CYGPATH_W) 'config-loader-expat.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-expat.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-expat.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-expat.c' object='dbus_daemon_launch_helper_test-config-loader-expat.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-loader-expat.obj `if test -f 'config-loader-expat.c'; then $(CYGPATH_W) 'config-loader-expat.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-expat.c'; fi`
+
+dbus_daemon_launch_helper_test-config-loader-libxml.o: config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-loader-libxml.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Tpo -c -o dbus_daemon_launch_helper_test-config-loader-libxml.o `test -f 'config-loader-libxml.c' || echo '$(srcdir)/'`config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-libxml.c' object='dbus_daemon_launch_helper_test-config-loader-libxml.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-loader-libxml.o `test -f 'config-loader-libxml.c' || echo '$(srcdir)/'`config-loader-libxml.c
+
+dbus_daemon_launch_helper_test-config-loader-libxml.obj: config-loader-libxml.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-loader-libxml.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Tpo -c -o dbus_daemon_launch_helper_test-config-loader-libxml.obj `if test -f 'config-loader-libxml.c'; then $(CYGPATH_W) 'config-loader-libxml.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-libxml.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-loader-libxml.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-loader-libxml.c' object='dbus_daemon_launch_helper_test-config-loader-libxml.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-loader-libxml.obj `if test -f 'config-loader-libxml.c'; then $(CYGPATH_W) 'config-loader-libxml.c'; else $(CYGPATH_W) '$(srcdir)/config-loader-libxml.c'; fi`
+
+dbus_daemon_launch_helper_test-config-parser-common.o: config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-parser-common.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Tpo -c -o dbus_daemon_launch_helper_test-config-parser-common.o `test -f 'config-parser-common.c' || echo '$(srcdir)/'`config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-common.c' object='dbus_daemon_launch_helper_test-config-parser-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-parser-common.o `test -f 'config-parser-common.c' || echo '$(srcdir)/'`config-parser-common.c
+
+dbus_daemon_launch_helper_test-config-parser-common.obj: config-parser-common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-parser-common.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Tpo -c -o dbus_daemon_launch_helper_test-config-parser-common.obj `if test -f 'config-parser-common.c'; then $(CYGPATH_W) 'config-parser-common.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-common.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-common.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-common.c' object='dbus_daemon_launch_helper_test-config-parser-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-parser-common.obj `if test -f 'config-parser-common.c'; then $(CYGPATH_W) 'config-parser-common.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-common.c'; fi`
+
+dbus_daemon_launch_helper_test-config-parser-trivial.o: config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-parser-trivial.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Tpo -c -o dbus_daemon_launch_helper_test-config-parser-trivial.o `test -f 'config-parser-trivial.c' || echo '$(srcdir)/'`config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-trivial.c' object='dbus_daemon_launch_helper_test-config-parser-trivial.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-parser-trivial.o `test -f 'config-parser-trivial.c' || echo '$(srcdir)/'`config-parser-trivial.c
+
+dbus_daemon_launch_helper_test-config-parser-trivial.obj: config-parser-trivial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-config-parser-trivial.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Tpo -c -o dbus_daemon_launch_helper_test-config-parser-trivial.obj `if test -f 'config-parser-trivial.c'; then $(CYGPATH_W) 'config-parser-trivial.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-trivial.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-config-parser-trivial.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config-parser-trivial.c' object='dbus_daemon_launch_helper_test-config-parser-trivial.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-config-parser-trivial.obj `if test -f 'config-parser-trivial.c'; then $(CYGPATH_W) 'config-parser-trivial.c'; else $(CYGPATH_W) '$(srcdir)/config-parser-trivial.c'; fi`
+
+dbus_daemon_launch_helper_test-desktop-file.o: desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-desktop-file.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Tpo -c -o dbus_daemon_launch_helper_test-desktop-file.o `test -f 'desktop-file.c' || echo '$(srcdir)/'`desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='desktop-file.c' object='dbus_daemon_launch_helper_test-desktop-file.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-desktop-file.o `test -f 'desktop-file.c' || echo '$(srcdir)/'`desktop-file.c
+
+dbus_daemon_launch_helper_test-desktop-file.obj: desktop-file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-desktop-file.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Tpo -c -o dbus_daemon_launch_helper_test-desktop-file.obj `if test -f 'desktop-file.c'; then $(CYGPATH_W) 'desktop-file.c'; else $(CYGPATH_W) '$(srcdir)/desktop-file.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-desktop-file.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='desktop-file.c' object='dbus_daemon_launch_helper_test-desktop-file.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-desktop-file.obj `if test -f 'desktop-file.c'; then $(CYGPATH_W) 'desktop-file.c'; else $(CYGPATH_W) '$(srcdir)/desktop-file.c'; fi`
+
+dbus_daemon_launch_helper_test-utils.o: utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-utils.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Tpo -c -o dbus_daemon_launch_helper_test-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils.c' object='dbus_daemon_launch_helper_test-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c
+
+dbus_daemon_launch_helper_test-utils.obj: utils.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-utils.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Tpo -c -o dbus_daemon_launch_helper_test-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-utils.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='utils.c' object='dbus_daemon_launch_helper_test-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi`
+
+dbus_daemon_launch_helper_test-activation-helper.o: activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-activation-helper.o -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Tpo -c -o dbus_daemon_launch_helper_test-activation-helper.o `test -f 'activation-helper.c' || echo '$(srcdir)/'`activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper.c' object='dbus_daemon_launch_helper_test-activation-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-activation-helper.o `test -f 'activation-helper.c' || echo '$(srcdir)/'`activation-helper.c
+
+dbus_daemon_launch_helper_test-activation-helper.obj: activation-helper.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dbus_daemon_launch_helper_test-activation-helper.obj -MD -MP -MF $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Tpo -c -o dbus_daemon_launch_helper_test-activation-helper.obj `if test -f 'activation-helper.c'; then $(CYGPATH_W) 'activation-helper.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Tpo $(DEPDIR)/dbus_daemon_launch_helper_test-activation-helper.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='activation-helper.c' object='dbus_daemon_launch_helper_test-activation-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(dbus_daemon_launch_helper_test_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dbus_daemon_launch_helper_test-activation-helper.obj `if test -f 'activation-helper.c'; then $(CYGPATH_W) 'activation-helper.c'; else $(CYGPATH_W) '$(srcdir)/activation-helper.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
+install-configDATA: $(config_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(configdir)" || $(MKDIR_P) "$(DESTDIR)$(configdir)"
+ @list='$(config_DATA)'; test -n "$(configdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(configdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(configdir)" || exit $$?; \
+ done
+
+uninstall-configDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(config_DATA)'; test -n "$(configdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(configdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(configdir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ echo "$$grn$$dashes"; \
+ else \
+ echo "$$red$$dashes"; \
+ fi; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes$$std"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(initddir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(configdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-configDATA install-initdSCRIPTS install-man
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-configDATA uninstall-initdSCRIPTS \
+ uninstall-man
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+uninstall-man: uninstall-man1
+
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-generic clean-libtool clean-local clean-noinstPROGRAMS \
+ ctags distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-configDATA \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-initdSCRIPTS install-man install-man1 install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-configDATA \
+ uninstall-hook uninstall-initdSCRIPTS uninstall-man \
+ uninstall-man1
+
+
+clean-local:
+ /bin/rm *.bb *.bbg *.da *.gcov || true
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon
+ rm -f $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper
+
+install-data-hook:
+ if test '!' -d $(DESTDIR)$(DBUS_DAEMONDIR); then \
+ $(mkinstalldirs) $(DESTDIR)$(DBUS_DAEMONDIR); \
+ chmod 755 $(DESTDIR)$(DBUS_DAEMONDIR); \
+ fi
+ $(INSTALL_PROGRAM) dbus-daemon $(DESTDIR)$(DBUS_DAEMONDIR)
+ $(mkinstalldirs) $(DESTDIR)$(localstatedir)/run/dbus
+ $(mkinstalldirs) $(DESTDIR)$(configdir)/system.d
+ $(mkinstalldirs) $(DESTDIR)$(configdir)/session.d
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/dbus-1/services
+ $(mkinstalldirs) $(DESTDIR)$(datadir)/dbus-1/system-services
+ $(mkinstalldirs) $(DESTDIR)$(libexecdir)/dbus-1
+ $(INSTALL_PROGRAM) dbus-daemon-launch-helper $(DESTDIR)$(libexecdir)
+ if test `id -u` -eq 0; then \
+ chown root:$(DBUS_USER) $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper; \
+ chmod 4750 $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper; \
+ else \
+ echo "Not installing $(DESTDIR)$(libexecdir)/dbus-daemon-launch-helper binary setuid!"; \
+ echo "You'll need to manually set permissions to root:$(DBUS_USER) and permissions 4750"; \
+ fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/bus/activation-exit-codes.h b/bus/activation-exit-codes.h
new file mode 100644
index 00000000..bbb98dca
--- /dev/null
+++ b/bus/activation-exit-codes.h
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation-exit-codes.h Return values for the launch helper which is set
+ * in the helper and read in dbus-spawn.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_ACTIVATION_EXIT_CODES_H
+#define BUS_ACTIVATION_EXIT_CODES_H
+
+/** Return codes from the launch helper - not public API. However,
+ * presumably if some third party did write their own launch helper,
+ * they would have to rely on these, or at least always return
+ * 1 for GENERIC_FAILURE.
+ */
+#define BUS_SPAWN_EXIT_CODE_GENERIC_FAILURE 1
+#define BUS_SPAWN_EXIT_CODE_NO_MEMORY 2
+#define BUS_SPAWN_EXIT_CODE_CONFIG_INVALID 3
+#define BUS_SPAWN_EXIT_CODE_SETUP_FAILED 4
+#define BUS_SPAWN_EXIT_CODE_NAME_INVALID 5
+#define BUS_SPAWN_EXIT_CODE_SERVICE_NOT_FOUND 6
+#define BUS_SPAWN_EXIT_CODE_PERMISSIONS_INVALID 7
+#define BUS_SPAWN_EXIT_CODE_FILE_INVALID 8
+#define BUS_SPAWN_EXIT_CODE_EXEC_FAILED 9
+#define BUS_SPAWN_EXIT_CODE_INVALID_ARGS 10
+#define BUS_SPAWN_EXIT_CODE_CHILD_SIGNALED 11
+
+#endif /* BUS_ACTIVATION_EXIT_CODES_H */
diff --git a/bus/activation-helper-bin.c b/bus/activation-helper-bin.c
new file mode 100644
index 00000000..a360acc7
--- /dev/null
+++ b/bus/activation-helper-bin.c
@@ -0,0 +1,100 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation-helper-bin.c Setuid helper for launching programs as a custom
+ * user. This file is security sensitive.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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 "utils.h"
+#include "activation-helper.h"
+#include "activation-exit-codes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+convert_error_to_exit_code (DBusError *error)
+{
+ if (dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
+ return BUS_SPAWN_EXIT_CODE_NO_MEMORY;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_CONFIG_INVALID))
+ return BUS_SPAWN_EXIT_CODE_CONFIG_INVALID;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_SETUP_FAILED))
+ return BUS_SPAWN_EXIT_CODE_SETUP_FAILED;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_INVALID))
+ return BUS_SPAWN_EXIT_CODE_SERVICE_NOT_FOUND;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID))
+ return BUS_SPAWN_EXIT_CODE_PERMISSIONS_INVALID;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_FILE_INVALID))
+ return BUS_SPAWN_EXIT_CODE_FILE_INVALID;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_EXEC_FAILED))
+ return BUS_SPAWN_EXIT_CODE_EXEC_FAILED;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_INVALID_ARGS))
+ return BUS_SPAWN_EXIT_CODE_INVALID_ARGS;
+
+ if (dbus_error_has_name (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED))
+ return BUS_SPAWN_EXIT_CODE_CHILD_SIGNALED;
+
+ /* should we assert? */
+ fprintf(stderr, "%s: %s\n", error->name, error->message);
+
+ return BUS_SPAWN_EXIT_CODE_SETUP_FAILED;
+}
+
+int
+main (int argc, char **argv)
+{
+ DBusError error;
+ int retval;
+
+ /* default is all okay */
+ retval = 0;
+
+ /* have we used a help option or not specified the correct arguments? */
+ if (argc != 2 ||
+ strcmp (argv[1], "--help") == 0 ||
+ strcmp (argv[1], "-h") == 0 ||
+ strcmp (argv[1], "-?") == 0)
+ {
+ fprintf (stderr, "dbus-daemon-activation-helper service.to.activate\n");
+ exit (0);
+ }
+
+ dbus_error_init (&error);
+ if (!run_launch_helper (argv[1], &error))
+ {
+ /* convert error to an exit code */
+ retval = convert_error_to_exit_code (&error);
+ dbus_error_free (&error);
+ }
+
+ return retval;
+}
+
diff --git a/bus/activation-helper.c b/bus/activation-helper.c
new file mode 100644
index 00000000..baba8f04
--- /dev/null
+++ b/bus/activation-helper.c
@@ -0,0 +1,563 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation-helper.c Setuid helper for launching programs as a custom
+ * user. This file is security sensitive.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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 "bus.h"
+#include "driver.h"
+#include "utils.h"
+#include "desktop-file.h"
+#include "config-parser-trivial.h"
+#include "activation-helper.h"
+#include "activation-exit-codes.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <dbus/dbus-shell.h>
+#include <dbus/dbus-marshal-validate.h>
+
+static BusDesktopFile *
+desktop_file_for_name (BusConfigParser *parser,
+ const char *name,
+ DBusError *error)
+{
+ BusDesktopFile *desktop_file;
+ DBusList **service_dirs;
+ DBusList *link;
+ DBusError tmp_error;
+ DBusString full_path;
+ DBusString filename;
+ const char *dir;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ desktop_file = NULL;
+
+ if (!_dbus_string_init (&filename))
+ {
+ BUS_SET_OOM (error);
+ goto out_all;
+ }
+
+ if (!_dbus_string_init (&full_path))
+ {
+ BUS_SET_OOM (error);
+ goto out_filename;
+ }
+
+ if (!_dbus_string_append (&filename, name) ||
+ !_dbus_string_append (&filename, ".service"))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ service_dirs = bus_config_parser_get_service_dirs (parser);
+ for (link = _dbus_list_get_first_link (service_dirs);
+ link != NULL;
+ link = _dbus_list_get_next_link (service_dirs, link))
+ {
+ dir = link->data;
+ _dbus_verbose ("Looking at '%s'\n", dir);
+
+ dbus_error_init (&tmp_error);
+
+ /* clear the path from last time */
+ _dbus_string_set_length (&full_path, 0);
+
+ /* build the full path */
+ if (!_dbus_string_append (&full_path, dir) ||
+ !_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ _dbus_verbose ("Trying to load file '%s'\n", _dbus_string_get_data (&full_path));
+ desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
+ if (desktop_file == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ _dbus_verbose ("Could not load %s: %s: %s\n",
+ _dbus_string_get_const_data (&full_path),
+ tmp_error.name, tmp_error.message);
+
+ /* we may have failed if the file is not found; this is not fatal */
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ /* we only bail out on OOM */
+ goto out;
+ }
+ dbus_error_free (&tmp_error);
+ }
+
+ /* did we find the desktop file we want? */
+ if (desktop_file != NULL)
+ break;
+ }
+
+ /* Didn't find desktop file; set error */
+ if (desktop_file == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND,
+ "The name %s was not provided by any .service files",
+ name);
+ }
+
+out:
+ _dbus_string_free (&full_path);
+out_filename:
+ _dbus_string_free (&filename);
+out_all:
+ return desktop_file;
+}
+
+/* Cleares the environment, except for DBUS_VERBOSE and DBUS_STARTER_x */
+static dbus_bool_t
+clear_environment (DBusError *error)
+{
+ const char *debug_env = NULL;
+ const char *starter_env = NULL;
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ /* are we debugging */
+ debug_env = _dbus_getenv ("DBUS_VERBOSE");
+#endif
+
+ /* we save the starter */
+ starter_env = _dbus_getenv ("DBUS_STARTER_ADDRESS");
+
+#ifndef ACTIVATION_LAUNCHER_TEST
+ /* totally clear the environment */
+ if (!_dbus_clearenv ())
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "could not clear environment\n");
+ return FALSE;
+ }
+#endif
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ /* restore the debugging environment setting if set */
+ if (debug_env)
+ _dbus_setenv ("DBUS_VERBOSE", debug_env);
+#endif
+
+ /* restore the starter */
+ if (starter_env)
+ _dbus_setenv ("DBUS_STARTER_ADDRESS", starter_env);
+
+ /* set the type, which must be system if we got this far */
+ _dbus_setenv ("DBUS_STARTER_BUS_TYPE", "system");
+
+ return TRUE;
+}
+
+static dbus_bool_t
+check_permissions (const char *dbus_user, DBusError *error)
+{
+ uid_t uid, euid;
+ struct passwd *pw;
+
+ pw = NULL;
+ uid = 0;
+ euid = 0;
+
+#ifndef ACTIVATION_LAUNCHER_TEST
+ /* bail out unless the dbus user is invoking the helper */
+ pw = getpwnam(dbus_user);
+ if (!pw)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,
+ "cannot find user '%s'", dbus_user);
+ return FALSE;
+ }
+ uid = getuid();
+ if (pw->pw_uid != uid)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,
+ "not invoked from user '%s'", dbus_user);
+ return FALSE;
+ }
+
+ /* bail out unless we are setuid to user root */
+ euid = geteuid();
+ if (euid != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,
+ "not setuid root");
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
+static dbus_bool_t
+check_service_name (BusDesktopFile *desktop_file,
+ const char *service_name,
+ DBusError *error)
+{
+ char *name_tmp;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ /* try to get Name */
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_NAME,
+ &name_tmp,
+ error))
+ goto failed;
+
+ /* verify that the name is the same as the file service name */
+ if (strcmp (service_name, name_tmp) != 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_FILE_INVALID,
+ "Service '%s' does not match expected value", name_tmp);
+ goto failed_free;
+ }
+
+ retval = TRUE;
+
+failed_free:
+ /* we don't return the name, so free it here */
+ dbus_free (name_tmp);
+failed:
+ return retval;
+}
+
+static dbus_bool_t
+get_parameters_for_service (BusDesktopFile *desktop_file,
+ const char *service_name,
+ char **exec,
+ char **user,
+ DBusError *error)
+{
+ char *exec_tmp;
+ char *user_tmp;
+
+ exec_tmp = NULL;
+ user_tmp = NULL;
+
+ /* check the name of the service */
+ if (!check_service_name (desktop_file, service_name, error))
+ goto failed;
+
+ /* get the complete path of the executable */
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_EXEC,
+ &exec_tmp,
+ error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ /* get the user that should run this service - user is compulsary for system activation */
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_USER,
+ &user_tmp,
+ error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ /* only assign if all the checks passed */
+ *exec = exec_tmp;
+ *user = user_tmp;
+ return TRUE;
+
+failed:
+ dbus_free (exec_tmp);
+ dbus_free (user_tmp);
+ return FALSE;
+}
+
+static dbus_bool_t
+switch_user (char *user, DBusError *error)
+{
+#ifndef ACTIVATION_LAUNCHER_TEST
+ struct passwd *pw;
+
+ /* find user */
+ pw = getpwnam (user);
+ if (!pw)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "cannot find user '%s'\n", user);
+ return FALSE;
+ }
+
+ /* initialize the group access list */
+ if (initgroups (user, pw->pw_gid))
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "could not initialize groups");
+ return FALSE;
+ }
+
+ /* change to the primary group for the user */
+ if (setgid (pw->pw_gid))
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "cannot setgid group %i", pw->pw_gid);
+ return FALSE;
+ }
+
+ /* change to the user specified */
+ if (setuid (pw->pw_uid) < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "cannot setuid user %i", pw->pw_uid);
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+static dbus_bool_t
+exec_for_correct_user (char *exec, char *user, DBusError *error)
+{
+ char **argv;
+ int argc;
+ dbus_bool_t retval;
+
+ argc = 0;
+ retval = TRUE;
+ argv = NULL;
+
+ if (!switch_user (user, error))
+ return FALSE;
+
+ /* convert command into arguments */
+ if (!_dbus_shell_parse_argv (exec, &argc, &argv, error))
+ return FALSE;
+
+#ifndef ACTIVATION_LAUNCHER_DO_OOM
+ /* replace with new binary, with no environment */
+ if (execv (argv[0], argv) < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+ "Failed to exec: %s", argv[0]);
+ retval = FALSE;
+ }
+#endif
+
+ dbus_free_string_array (argv);
+ return retval;
+}
+
+static dbus_bool_t
+check_bus_name (const char *bus_name,
+ DBusError *error)
+{
+ DBusString str;
+
+ _dbus_string_init_const (&str, bus_name);
+ if (!_dbus_validate_bus_name (&str, 0, _dbus_string_get_length (&str)))
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND,
+ "bus name '%s' is not a valid bus name\n",
+ bus_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+get_correct_parser (BusConfigParser **parser, DBusError *error)
+{
+ DBusString config_file;
+ dbus_bool_t retval;
+ const char *test_config_file;
+
+ retval = FALSE;
+ test_config_file = NULL;
+
+#ifdef ACTIVATION_LAUNCHER_TEST
+ /* there is no _way_ we should be setuid if this define is set.
+ * but we should be doubly paranoid and check... */
+ if (getuid() != geteuid())
+ _dbus_assert_not_reached ("dbus-daemon-launch-helper-test binary is setuid!");
+
+ /* this is not a security hole. The environment variable is only passed in the
+ * dbus-daemon-lauch-helper-test NON-SETUID launcher */
+ test_config_file = _dbus_getenv ("TEST_LAUNCH_HELPER_CONFIG");
+ if (test_config_file == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "the TEST_LAUNCH_HELPER_CONFIG env variable is not set");
+ goto out;
+ }
+#endif
+
+ /* we _only_ use the predefined system config file */
+ if (!_dbus_string_init (&config_file))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+#ifndef ACTIVATION_LAUNCHER_TEST
+ if (!_dbus_string_append (&config_file, DBUS_SYSTEM_CONFIG_FILE))
+ {
+ BUS_SET_OOM (error);
+ goto out_free_config;
+ }
+#else
+ if (!_dbus_string_append (&config_file, test_config_file))
+ {
+ BUS_SET_OOM (error);
+ goto out_free_config;
+ }
+#endif
+
+ /* where are we pointing.... */
+ _dbus_verbose ("dbus-daemon-activation-helper: using config file: %s\n",
+ _dbus_string_get_const_data (&config_file));
+
+ /* get the dbus user */
+ *parser = bus_config_load (&config_file, TRUE, NULL, error);
+ if (*parser == NULL)
+ {
+ goto out_free_config;
+ }
+
+ /* woot */
+ retval = TRUE;
+
+out_free_config:
+ _dbus_string_free (&config_file);
+out:
+ return retval;
+}
+
+static dbus_bool_t
+launch_bus_name (const char *bus_name, BusConfigParser *parser, DBusError *error)
+{
+ BusDesktopFile *desktop_file;
+ char *exec, *user;
+ dbus_bool_t retval;
+
+ exec = NULL;
+ user = NULL;
+ retval = FALSE;
+
+ /* get the correct service file for the name we are trying to activate */
+ desktop_file = desktop_file_for_name (parser, bus_name, error);
+ if (desktop_file == NULL)
+ return FALSE;
+
+ /* get exec and user for service name */
+ if (!get_parameters_for_service (desktop_file, bus_name, &exec, &user, error))
+ goto finish;
+
+ _dbus_verbose ("dbus-daemon-activation-helper: Name='%s'\n", bus_name);
+ _dbus_verbose ("dbus-daemon-activation-helper: Exec='%s'\n", exec);
+ _dbus_verbose ("dbus-daemon-activation-helper: User='%s'\n", user);
+
+ /* actually execute */
+ if (!exec_for_correct_user (exec, user, error))
+ goto finish;
+
+ retval = TRUE;
+
+finish:
+ dbus_free (exec);
+ dbus_free (user);
+ bus_desktop_file_free (desktop_file);
+ return retval;
+}
+
+static dbus_bool_t
+check_dbus_user (BusConfigParser *parser, DBusError *error)
+{
+ const char *dbus_user;
+
+ dbus_user = bus_config_parser_get_user (parser);
+ if (dbus_user == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_SPAWN_CONFIG_INVALID,
+ "could not get user from config file\n");
+ return FALSE;
+ }
+
+ /* check to see if permissions are correct */
+ if (!check_permissions (dbus_user, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+run_launch_helper (const char *bus_name,
+ DBusError *error)
+{
+ BusConfigParser *parser;
+ dbus_bool_t retval;
+
+ parser = NULL;
+ retval = FALSE;
+
+ /* clear the environment, apart from a few select settings */
+ if (!clear_environment (error))
+ goto error;
+
+ /* check to see if we have a valid bus name */
+ if (!check_bus_name (bus_name, error))
+ goto error;
+
+ /* get the correct parser, either the test or default parser */
+ if (!get_correct_parser (&parser, error))
+ goto error;
+
+ /* check we are being invoked by the correct dbus user */
+ if (!check_dbus_user (parser, error))
+ goto error_free_parser;
+
+ /* launch the bus with the service defined user */
+ if (!launch_bus_name (bus_name, parser, error))
+ goto error_free_parser;
+
+ /* woohoo! */
+ retval = TRUE;
+
+error_free_parser:
+ bus_config_parser_unref (parser);
+error:
+ return retval;
+}
+
diff --git a/bus/activation-helper.h b/bus/activation-helper.h
new file mode 100644
index 00000000..361a4c6a
--- /dev/null
+++ b/bus/activation-helper.h
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation-helper.h The actual activation helper split from the main
+ * function for testing.
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_ACTIVATION_HELPER_H
+#define BUS_ACTIVATION_HELPER_H
+
+dbus_bool_t run_launch_helper (const char *bus_name, DBusError *error);
+
+
+#endif /* BUS_ACTIVATION_HELPER_H */
diff --git a/bus/activation.c b/bus/activation.c
new file mode 100644
index 00000000..2fcd85d2
--- /dev/null
+++ b/bus/activation.c
@@ -0,0 +1,2351 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation.c Activation of services
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2004 Imendio HB
+ *
+ * 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 "activation.h"
+#include "activation-exit-codes.h"
+#include "desktop-file.h"
+#include "dispatch.h"
+#include "services.h"
+#include "test.h"
+#include "utils.h"
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-shell.h>
+#include <dbus/dbus-spawn.h>
+#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-sysdeps.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+struct BusActivation
+{
+ int refcount;
+ DBusHashTable *entries;
+ DBusHashTable *pending_activations;
+ char *server_address;
+ BusContext *context;
+ int n_pending_activations; /**< This is in fact the number of BusPendingActivationEntry,
+ * i.e. number of pending activation requests, not pending
+ * activations per se
+ */
+ DBusHashTable *directories;
+ DBusHashTable *environment;
+};
+
+typedef struct
+{
+ int refcount;
+ char *dir_c;
+ DBusHashTable *entries;
+} BusServiceDirectory;
+
+typedef struct
+{
+ int refcount;
+ char *name;
+ char *exec;
+ char *user;
+ unsigned long mtime;
+ BusServiceDirectory *s_dir;
+ char *filename;
+} BusActivationEntry;
+
+typedef struct BusPendingActivationEntry BusPendingActivationEntry;
+
+struct BusPendingActivationEntry
+{
+ DBusMessage *activation_message;
+ DBusConnection *connection;
+
+ dbus_bool_t auto_activation;
+};
+
+typedef struct
+{
+ int refcount;
+ BusActivation *activation;
+ char *service_name;
+ char *exec;
+ DBusList *entries;
+ int n_entries;
+ DBusBabysitter *babysitter;
+ DBusTimeout *timeout;
+ unsigned int timeout_added : 1;
+} BusPendingActivation;
+
+#if 0
+static BusServiceDirectory *
+bus_service_directory_ref (BusServiceDirectory *dir)
+{
+ _dbus_assert (dir->refcount);
+
+ dir->refcount++;
+
+ return dir;
+}
+#endif
+
+static void
+bus_service_directory_unref (BusServiceDirectory *dir)
+{
+ if (dir == NULL)
+ return;
+
+ _dbus_assert (dir->refcount > 0);
+ dir->refcount--;
+
+ if (dir->refcount > 0)
+ return;
+
+ if (dir->entries)
+ _dbus_hash_table_unref (dir->entries);
+
+ dbus_free (dir->dir_c);
+ dbus_free (dir);
+}
+
+static void
+bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
+{
+ if (entry->activation_message)
+ dbus_message_unref (entry->activation_message);
+
+ if (entry->connection)
+ dbus_connection_unref (entry->connection);
+
+ dbus_free (entry);
+}
+
+static void
+handle_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ while (!dbus_timeout_handle (pending_activation->timeout))
+ _dbus_wait_for_memory ();
+}
+
+static BusPendingActivation *
+bus_pending_activation_ref (BusPendingActivation *pending_activation)
+{
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount += 1;
+
+ return pending_activation;
+}
+
+static void
+bus_pending_activation_unref (BusPendingActivation *pending_activation)
+{
+ DBusList *link;
+
+ if (pending_activation == NULL) /* hash table requires this */
+ return;
+
+ _dbus_assert (pending_activation->refcount > 0);
+ pending_activation->refcount -= 1;
+
+ if (pending_activation->refcount > 0)
+ return;
+
+ if (pending_activation->timeout_added)
+ {
+ _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
+ pending_activation->timeout,
+ handle_timeout_callback, pending_activation);
+ pending_activation->timeout_added = FALSE;
+ }
+
+ if (pending_activation->timeout)
+ _dbus_timeout_unref (pending_activation->timeout);
+
+ if (pending_activation->babysitter)
+ {
+ if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
+ NULL, NULL, NULL,
+ pending_activation->babysitter,
+ NULL))
+ _dbus_assert_not_reached ("setting watch functions to NULL failed");
+
+ _dbus_babysitter_unref (pending_activation->babysitter);
+ }
+
+ dbus_free (pending_activation->service_name);
+ dbus_free (pending_activation->exec);
+
+ link = _dbus_list_get_first_link (&pending_activation->entries);
+
+ while (link != NULL)
+ {
+ BusPendingActivationEntry *entry = link->data;
+
+ bus_pending_activation_entry_free (entry);
+
+ link = _dbus_list_get_next_link (&pending_activation->entries, link);
+ }
+ _dbus_list_clear (&pending_activation->entries);
+
+ pending_activation->activation->n_pending_activations -=
+ pending_activation->n_entries;
+
+ _dbus_assert (pending_activation->activation->n_pending_activations >= 0);
+
+ dbus_free (pending_activation);
+}
+
+static BusActivationEntry *
+bus_activation_entry_ref (BusActivationEntry *entry)
+{
+ _dbus_assert (entry->refcount > 0);
+ entry->refcount++;
+
+ return entry;
+}
+
+static void
+bus_activation_entry_unref (BusActivationEntry *entry)
+{
+ if (entry == NULL) /* hash table requires this */
+ return;
+
+ _dbus_assert (entry->refcount > 0);
+ entry->refcount--;
+
+ if (entry->refcount > 0)
+ return;
+
+ dbus_free (entry->name);
+ dbus_free (entry->exec);
+ dbus_free (entry->user);
+ dbus_free (entry->filename);
+
+ dbus_free (entry);
+}
+
+static dbus_bool_t
+update_desktop_file_entry (BusActivation *activation,
+ BusServiceDirectory *s_dir,
+ DBusString *filename,
+ BusDesktopFile *desktop_file,
+ DBusError *error)
+{
+ char *name, *exec, *user;
+ BusActivationEntry *entry;
+ DBusStat stat_buf;
+ DBusString file_path;
+ DBusError tmp_error;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ name = NULL;
+ exec = NULL;
+ user = NULL;
+ entry = NULL;
+
+ dbus_error_init (&tmp_error);
+
+ if (!_dbus_string_init (&file_path))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&file_path, s_dir->dir_c) ||
+ !_dbus_concat_dir_and_file (&file_path, filename))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_stat (&file_path, &stat_buf, NULL))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Can't stat the service file\n");
+ goto failed;
+ }
+
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_NAME,
+ &name,
+ error))
+ goto failed;
+
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_EXEC,
+ &exec,
+ error))
+ goto failed;
+
+ /* user is not _required_ unless we are using system activation */
+ if (!bus_desktop_file_get_string (desktop_file,
+ DBUS_SERVICE_SECTION,
+ DBUS_SERVICE_USER,
+ &user, &tmp_error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+ /* if we got OOM, then exit */
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto failed;
+ }
+ else
+ {
+ /* if we have error because we didn't find anything then continue */
+ dbus_error_free (&tmp_error);
+ dbus_free (user);
+ user = NULL;
+ }
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+ entry = _dbus_hash_table_lookup_string (s_dir->entries,
+ _dbus_string_get_const_data (filename));
+ if (entry == NULL) /* New file */
+ {
+ /* FIXME we need a better-defined algorithm for which service file to
+ * pick than "whichever one is first in the directory listing"
+ */
+ if (_dbus_hash_table_lookup_string (activation->entries, name))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Service %s already exists in activation entry list\n", name);
+ goto failed;
+ }
+
+ entry = dbus_new0 (BusActivationEntry, 1);
+ if (entry == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ entry->name = name;
+ entry->exec = exec;
+ entry->user = user;
+ entry->refcount = 1;
+
+ entry->s_dir = s_dir;
+ entry->filename = _dbus_strdup (_dbus_string_get_const_data (filename));
+ if (!entry->filename)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_hash_table_insert_string (activation->entries, entry->name, bus_activation_entry_ref (entry)))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_hash_table_insert_string (s_dir->entries, entry->filename, bus_activation_entry_ref (entry)))
+ {
+ /* Revert the insertion in the entries table */
+ _dbus_hash_table_remove_string (activation->entries, entry->name);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
+ }
+ else /* Just update the entry */
+ {
+ bus_activation_entry_ref (entry);
+ _dbus_hash_table_remove_string (activation->entries, entry->name);
+
+ if (_dbus_hash_table_lookup_string (activation->entries, name))
+ {
+ _dbus_verbose ("The new service name \"%s\" of service file \"%s\" already in cache, ignoring\n",
+ name, _dbus_string_get_const_data (&file_path));
+ goto failed;
+ }
+
+ dbus_free (entry->name);
+ dbus_free (entry->exec);
+ dbus_free (entry->user);
+ entry->name = name;
+ entry->exec = exec;
+ entry->user = user;
+ if (!_dbus_hash_table_insert_string (activation->entries,
+ entry->name, bus_activation_entry_ref(entry)))
+ {
+ BUS_SET_OOM (error);
+ /* Also remove path to entries hash since we want this in sync with
+ * the entries hash table */
+ _dbus_hash_table_remove_string (entry->s_dir->entries,
+ entry->filename);
+ bus_activation_entry_unref (entry);
+ return FALSE;
+ }
+ }
+
+ entry->mtime = stat_buf.mtime;
+
+ _dbus_string_free (&file_path);
+ bus_activation_entry_unref (entry);
+
+ return TRUE;
+
+failed:
+ dbus_free (name);
+ dbus_free (exec);
+ dbus_free (user);
+ _dbus_string_free (&file_path);
+
+ if (entry)
+ bus_activation_entry_unref (entry);
+
+ return FALSE;
+}
+
+static dbus_bool_t
+check_service_file (BusActivation *activation,
+ BusActivationEntry *entry,
+ BusActivationEntry **updated_entry,
+ DBusError *error)
+{
+ DBusStat stat_buf;
+ dbus_bool_t retval;
+ BusActivationEntry *tmp_entry;
+ DBusString file_path;
+ DBusString filename;
+
+ retval = TRUE;
+ tmp_entry = entry;
+
+ _dbus_string_init_const (&filename, entry->filename);
+
+ if (!_dbus_string_init (&file_path))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&file_path, entry->s_dir->dir_c) ||
+ !_dbus_concat_dir_and_file (&file_path, &filename))
+ {
+ BUS_SET_OOM (error);
+ retval = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_stat (&file_path, &stat_buf, NULL))
+ {
+ _dbus_verbose ("****** Can't stat file \"%s\", removing from cache\n",
+ _dbus_string_get_const_data (&file_path));
+
+ _dbus_hash_table_remove_string (activation->entries, entry->name);
+ _dbus_hash_table_remove_string (entry->s_dir->entries, entry->filename);
+
+ tmp_entry = NULL;
+ retval = TRUE;
+ goto out;
+ }
+ else
+ {
+ if (stat_buf.mtime > entry->mtime)
+ {
+ BusDesktopFile *desktop_file;
+ DBusError tmp_error;
+
+ dbus_error_init (&tmp_error);
+
+ desktop_file = bus_desktop_file_load (&file_path, &tmp_error);
+ if (desktop_file == NULL)
+ {
+ _dbus_verbose ("Could not load %s: %s\n",
+ _dbus_string_get_const_data (&file_path),
+ tmp_error.message);
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ retval = FALSE;
+ goto out;
+ }
+ dbus_error_free (&tmp_error);
+ retval = TRUE;
+ goto out;
+ }
+
+ /* @todo We can return OOM or a DBUS_ERROR_FAILED error
+ * Handle these both better
+ */
+ if (!update_desktop_file_entry (activation, entry->s_dir, &filename, desktop_file, &tmp_error))
+ {
+ bus_desktop_file_free (desktop_file);
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ retval = FALSE;
+ goto out;
+ }
+ dbus_error_free (&tmp_error);
+ retval = TRUE;
+ goto out;
+ }
+
+ bus_desktop_file_free (desktop_file);
+ retval = TRUE;
+ }
+ }
+
+out:
+ _dbus_string_free (&file_path);
+
+ if (updated_entry != NULL)
+ *updated_entry = tmp_entry;
+ return retval;
+}
+
+
+/* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
+ * hash entries it already added.
+ */
+static dbus_bool_t
+update_directory (BusActivation *activation,
+ BusServiceDirectory *s_dir,
+ DBusError *error)
+{
+ DBusDirIter *iter;
+ DBusString dir, filename;
+ BusDesktopFile *desktop_file;
+ DBusError tmp_error;
+ dbus_bool_t retval;
+ BusActivationEntry *entry;
+ DBusString full_path;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ iter = NULL;
+ desktop_file = NULL;
+
+ _dbus_string_init_const (&dir, s_dir->dir_c);
+
+ if (!_dbus_string_init (&filename))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&full_path))
+ {
+ BUS_SET_OOM (error);
+ _dbus_string_free (&filename);
+ return FALSE;
+ }
+
+ retval = FALSE;
+
+ /* from this point it's safe to "goto out" */
+
+ iter = _dbus_directory_open (&dir, error);
+ if (iter == NULL)
+ {
+ _dbus_verbose ("Failed to open directory %s: %s\n",
+ s_dir->dir_c,
+ error ? error->message : "unknown");
+ goto out;
+ }
+
+ /* Now read the files */
+ dbus_error_init (&tmp_error);
+ while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
+ {
+ _dbus_assert (!dbus_error_is_set (&tmp_error));
+
+ _dbus_string_set_length (&full_path, 0);
+
+ if (!_dbus_string_ends_with_c_str (&filename, ".service"))
+ {
+ _dbus_verbose ("Skipping non-.service file %s\n",
+ _dbus_string_get_const_data (&filename));
+ continue;
+ }
+
+ entry = _dbus_hash_table_lookup_string (s_dir->entries, _dbus_string_get_const_data (&filename));
+ if (entry) /* Already has this service file in the cache */
+ {
+ if (!check_service_file (activation, entry, NULL, error))
+ goto out;
+
+ continue;
+ }
+
+ if (!_dbus_string_append (&full_path, s_dir->dir_c) ||
+ !_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ /* New file */
+ desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
+ if (desktop_file == NULL)
+ {
+ _dbus_verbose ("Could not load %s: %s\n",
+ _dbus_string_get_const_data (&full_path),
+ tmp_error.message);
+
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto out;
+ }
+
+ dbus_error_free (&tmp_error);
+ continue;
+ }
+
+ /* @todo We can return OOM or a DBUS_ERROR_FAILED error
+ * Handle these both better
+ */
+ if (!update_desktop_file_entry (activation, s_dir, &filename, desktop_file, &tmp_error))
+ {
+ bus_desktop_file_free (desktop_file);
+ desktop_file = NULL;
+
+ _dbus_verbose ("Could not add %s to activation entry list: %s\n",
+ _dbus_string_get_const_data (&full_path), tmp_error.message);
+
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto out;
+ }
+
+ dbus_error_free (&tmp_error);
+ continue;
+ }
+ else
+ {
+ bus_desktop_file_free (desktop_file);
+ desktop_file = NULL;
+ continue;
+ }
+ }
+
+ if (dbus_error_is_set (&tmp_error))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (!retval)
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ else
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (iter != NULL)
+ _dbus_directory_close (iter);
+ _dbus_string_free (&filename);
+ _dbus_string_free (&full_path);
+
+ return retval;
+}
+
+static dbus_bool_t
+populate_environment (BusActivation *activation)
+{
+ DBusString key;
+ DBusString value;
+ int i;
+ char **environment;
+ dbus_bool_t retval = FALSE;
+
+ environment = _dbus_get_environment ();
+
+ if (environment == NULL)
+ return FALSE;
+
+ if (!_dbus_string_init (&key))
+ {
+ dbus_free_string_array (environment);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&value))
+ {
+ _dbus_string_free (&key);
+ dbus_free_string_array (environment);
+ return FALSE;
+ }
+
+ for (i = 0; environment[i] != NULL; i++)
+ {
+ if (!_dbus_string_append (&key, environment[i]))
+ break;
+
+ if (_dbus_string_split_on_byte (&key, '=', &value))
+ {
+ char *hash_key, *hash_value;
+
+ if (!_dbus_string_steal_data (&key, &hash_key))
+ break;
+
+ if (!_dbus_string_steal_data (&value, &hash_value))
+ break;
+
+ if (!_dbus_hash_table_insert_string (activation->environment,
+ hash_key, hash_value))
+ break;
+ }
+ _dbus_string_set_length (&key, 0);
+ _dbus_string_set_length (&value, 0);
+ }
+
+ if (environment[i] != NULL)
+ goto out;
+
+ retval = TRUE;
+out:
+
+ _dbus_string_free (&key);
+ _dbus_string_free (&value);
+ dbus_free_string_array (environment);
+
+ return retval;
+}
+
+dbus_bool_t
+bus_activation_reload (BusActivation *activation,
+ const DBusString *address,
+ DBusList **directories,
+ DBusError *error)
+{
+ DBusList *link;
+ char *dir;
+
+ if (activation->server_address != NULL)
+ dbus_free (activation->server_address);
+ if (!_dbus_string_copy_data (address, &activation->server_address))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (activation->entries != NULL)
+ _dbus_hash_table_unref (activation->entries);
+ activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
+ (DBusFreeFunction)bus_activation_entry_unref);
+ if (activation->entries == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (activation->directories != NULL)
+ _dbus_hash_table_unref (activation->directories);
+ activation->directories = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
+ (DBusFreeFunction)bus_service_directory_unref);
+
+ if (activation->directories == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ link = _dbus_list_get_first_link (directories);
+ while (link != NULL)
+ {
+ BusServiceDirectory *s_dir;
+
+ dir = _dbus_strdup ((const char *) link->data);
+ if (!dir)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ s_dir = dbus_new0 (BusServiceDirectory, 1);
+ if (!s_dir)
+ {
+ dbus_free (dir);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ s_dir->refcount = 1;
+ s_dir->dir_c = dir;
+
+ s_dir->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
+ (DBusFreeFunction)bus_activation_entry_unref);
+
+ if (!s_dir->entries)
+ {
+ bus_service_directory_unref (s_dir);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_hash_table_insert_string (activation->directories, s_dir->dir_c, s_dir))
+ {
+ bus_service_directory_unref (s_dir);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ /* only fail on OOM, it is ok if we can't read the directory */
+ if (!update_directory (activation, s_dir, error))
+ {
+ if (dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
+ goto failed;
+ else
+ dbus_error_free (error);
+ }
+
+ link = _dbus_list_get_next_link (directories, link);
+ }
+
+ return TRUE;
+ failed:
+ return FALSE;
+}
+
+BusActivation*
+bus_activation_new (BusContext *context,
+ const DBusString *address,
+ DBusList **directories,
+ DBusError *error)
+{
+ BusActivation *activation;
+ DBusList *link;
+ char *dir;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ activation = dbus_new0 (BusActivation, 1);
+ if (activation == NULL)
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ activation->refcount = 1;
+ activation->context = context;
+ activation->n_pending_activations = 0;
+
+ if (!bus_activation_reload (activation, address, directories, error))
+ goto failed;
+
+ /* Initialize this hash table once, we don't want to lose pending
+ * activations on reload. */
+ activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
+ (DBusFreeFunction)bus_pending_activation_unref);
+
+ if (activation->pending_activations == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ activation->environment = _dbus_hash_table_new (DBUS_HASH_STRING,
+ (DBusFreeFunction) dbus_free,
+ (DBusFreeFunction) dbus_free);
+
+ if (activation->environment == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!populate_environment (activation))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ return activation;
+
+ failed:
+ bus_activation_unref (activation);
+ return NULL;
+}
+
+BusActivation *
+bus_activation_ref (BusActivation *activation)
+{
+ _dbus_assert (activation->refcount > 0);
+
+ activation->refcount += 1;
+
+ return activation;
+}
+
+void
+bus_activation_unref (BusActivation *activation)
+{
+ _dbus_assert (activation->refcount > 0);
+
+ activation->refcount -= 1;
+
+ if (activation->refcount > 0)
+ return;
+
+ dbus_free (activation->server_address);
+ if (activation->entries)
+ _dbus_hash_table_unref (activation->entries);
+ if (activation->pending_activations)
+ _dbus_hash_table_unref (activation->pending_activations);
+ if (activation->directories)
+ _dbus_hash_table_unref (activation->directories);
+ if (activation->environment)
+ _dbus_hash_table_unref (activation->environment);
+
+ dbus_free (activation);
+}
+
+static dbus_bool_t
+add_bus_environment (BusActivation *activation,
+ DBusError *error)
+{
+ const char *type;
+
+ if (!bus_activation_set_environment_variable (activation,
+ "DBUS_STARTER_ADDRESS",
+ activation->server_address,
+ error))
+ return FALSE;
+
+ type = bus_context_get_type (activation->context);
+ if (type != NULL)
+ {
+ if (!bus_activation_set_environment_variable (activation,
+ "DBUS_STARTER_BUS_TYPE", type,
+ error))
+ return FALSE;
+
+ if (strcmp (type, "session") == 0)
+ {
+ if (!bus_activation_set_environment_variable (activation,
+ "DBUS_SESSION_BUS_ADDRESS",
+ activation->server_address,
+ error))
+ return FALSE;
+ }
+ else if (strcmp (type, "system") == 0)
+ {
+ if (!bus_activation_set_environment_variable (activation,
+ "DBUS_SYSTEM_BUS_ADDRESS",
+ activation->server_address,
+ error))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+typedef struct
+{
+ BusPendingActivation *pending_activation;
+ DBusPreallocatedHash *hash_entry;
+} RestorePendingData;
+
+static void
+restore_pending (void *data)
+{
+ RestorePendingData *d = data;
+
+ _dbus_assert (d->pending_activation != NULL);
+ _dbus_assert (d->hash_entry != NULL);
+
+ _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
+ d->pending_activation->service_name,
+ d->pending_activation->timeout_added);
+
+ _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
+ d->hash_entry,
+ d->pending_activation->service_name, d->pending_activation);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ d->hash_entry = NULL;
+}
+
+static void
+free_pending_restore_data (void *data)
+{
+ RestorePendingData *d = data;
+
+ if (d->hash_entry)
+ _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
+ d->hash_entry);
+
+ bus_pending_activation_unref (d->pending_activation);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_restore_pending_to_transaction (BusTransaction *transaction,
+ BusPendingActivation *pending_activation)
+{
+ RestorePendingData *d;
+
+ d = dbus_new (RestorePendingData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->pending_activation = pending_activation;
+ d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
+
+ bus_pending_activation_ref (d->pending_activation);
+
+ if (d->hash_entry == NULL ||
+ !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
+ free_pending_restore_data))
+ {
+ free_pending_restore_data (d);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_activation_service_created (BusActivation *activation,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ BusPendingActivation *pending_activation;
+ DBusMessage *message;
+ DBusList *link;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* Check if it's a pending activation */
+ pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
+
+ if (!pending_activation)
+ return TRUE;
+
+ link = _dbus_list_get_first_link (&pending_activation->entries);
+ while (link != NULL)
+ {
+ BusPendingActivationEntry *entry = link->data;
+ DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
+
+ if (dbus_connection_get_is_connected (entry->connection))
+ {
+ /* Only send activation replies to regular activation requests. */
+ if (!entry->auto_activation)
+ {
+ dbus_uint32_t result;
+
+ message = dbus_message_new_method_return (entry->activation_message);
+ if (!message)
+ {
+ BUS_SET_OOM (error);
+ goto error;
+ }
+
+ result = DBUS_START_REPLY_SUCCESS;
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_UINT32, &result,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ goto error;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ goto error;
+ }
+
+ dbus_message_unref (message);
+ }
+ }
+
+ link = next;
+ }
+
+ return TRUE;
+
+ error:
+ return FALSE;
+}
+
+dbus_bool_t
+bus_activation_send_pending_auto_activation_messages (BusActivation *activation,
+ BusService *service,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ BusPendingActivation *pending_activation;
+ DBusList *link;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* Check if it's a pending activation */
+ pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations,
+ bus_service_get_name (service));
+
+ if (!pending_activation)
+ return TRUE;
+
+ link = _dbus_list_get_first_link (&pending_activation->entries);
+ while (link != NULL)
+ {
+ BusPendingActivationEntry *entry = link->data;
+ DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
+
+ if (entry->auto_activation && dbus_connection_get_is_connected (entry->connection))
+ {
+ DBusConnection *addressed_recipient;
+
+ addressed_recipient = bus_service_get_primary_owners_connection (service);
+
+ /* Resume dispatching where we left off in bus_dispatch() */
+ if (!bus_dispatch_matches (transaction,
+ entry->connection,
+ addressed_recipient,
+ entry->activation_message, error))
+ goto error;
+ }
+
+ link = next;
+ }
+
+ if (!add_restore_pending_to_transaction (transaction, pending_activation))
+ {
+ _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
+ BUS_SET_OOM (error);
+ goto error;
+ }
+
+ _dbus_hash_table_remove_string (activation->pending_activations, bus_service_get_name (service));
+
+ return TRUE;
+
+ error:
+ return FALSE;
+}
+
+/**
+ * FIXME @todo the error messages here would ideally be preallocated
+ * so we don't need to allocate memory to send them.
+ * Using the usual tactic, prealloc an OOM message, then
+ * if we can't alloc the real error send the OOM error instead.
+ */
+static dbus_bool_t
+try_send_activation_failure (BusPendingActivation *pending_activation,
+ const DBusError *how)
+{
+ BusActivation *activation;
+ DBusList *link;
+ BusTransaction *transaction;
+
+ activation = pending_activation->activation;
+
+ transaction = bus_transaction_new (activation->context);
+ if (transaction == NULL)
+ return FALSE;
+
+ link = _dbus_list_get_first_link (&pending_activation->entries);
+ while (link != NULL)
+ {
+ BusPendingActivationEntry *entry = link->data;
+ DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
+
+ if (dbus_connection_get_is_connected (entry->connection))
+ {
+ if (!bus_transaction_send_error_reply (transaction,
+ entry->connection,
+ how,
+ entry->activation_message))
+ goto error;
+ }
+
+ link = next;
+ }
+
+ bus_transaction_execute_and_free (transaction);
+
+ return TRUE;
+
+ error:
+ if (transaction)
+ bus_transaction_cancel_and_free (transaction);
+ return FALSE;
+}
+
+/**
+ * Free the pending activation and send an error message to all the
+ * connections that were waiting for it.
+ */
+static void
+pending_activation_failed (BusPendingActivation *pending_activation,
+ const DBusError *how)
+{
+ /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
+ while (!try_send_activation_failure (pending_activation, how))
+ _dbus_wait_for_memory ();
+
+ /* Destroy this pending activation */
+ _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
+ pending_activation->service_name);
+}
+
+/**
+ * Depending on the exit code of the helper, set the error accordingly
+ */
+static void
+handle_servicehelper_exit_error (int exit_code,
+ DBusError *error)
+{
+ switch (exit_code)
+ {
+ case BUS_SPAWN_EXIT_CODE_NO_MEMORY:
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
+ "Launcher could not run (out of memory)");
+ break;
+ case BUS_SPAWN_EXIT_CODE_SETUP_FAILED:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SETUP_FAILED,
+ "Failed to setup environment correctly");
+ break;
+ case BUS_SPAWN_EXIT_CODE_NAME_INVALID:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SERVICE_INVALID,
+ "Bus name is not valid or missing");
+ break;
+ case BUS_SPAWN_EXIT_CODE_SERVICE_NOT_FOUND:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND,
+ "Bus name not found in system service directory");
+ break;
+ case BUS_SPAWN_EXIT_CODE_PERMISSIONS_INVALID:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,
+ "The permission of the setuid helper is not correct");
+ break;
+ case BUS_SPAWN_EXIT_CODE_FILE_INVALID:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_PERMISSIONS_INVALID,
+ "The service file is incorrect or does not have all required attributes");
+ break;
+ case BUS_SPAWN_EXIT_CODE_EXEC_FAILED:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
+ "Cannot launch daemon, file not found or permissions invalid");
+ break;
+ case BUS_SPAWN_EXIT_CODE_INVALID_ARGS:
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments to command line");
+ break;
+ case BUS_SPAWN_EXIT_CODE_CHILD_SIGNALED:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED,
+ "Launched child was signaled, it probably crashed");
+ break;
+ default:
+ dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
+ "Launch helper exited with unknown return code %i", exit_code);
+ break;
+ }
+}
+
+static dbus_bool_t
+babysitter_watch_callback (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ BusPendingActivation *pending_activation = data;
+ dbus_bool_t retval;
+ DBusBabysitter *babysitter;
+ dbus_bool_t uses_servicehelper;
+
+ babysitter = pending_activation->babysitter;
+
+ _dbus_babysitter_ref (babysitter);
+
+ retval = dbus_watch_handle (watch, condition);
+
+ /* There are two major cases here; are we the system bus or the session? Here this
+ * is distinguished by whether or not we use a setuid helper launcher. With the launch helper,
+ * some process exit codes are meaningful, processed by handle_servicehelper_exit_error.
+ *
+ * In both cases though, just ignore when a process exits with status 0; it's possible for
+ * a program to (misguidedly) "daemonize", and that appears to us as an exit. This closes a race
+ * condition between this code and the child process claiming the bus name.
+ */
+ uses_servicehelper = bus_context_get_servicehelper (pending_activation->activation->context) != NULL;
+
+ /* FIXME this is broken in the same way that
+ * connection watches used to be; there should be
+ * a separate callback for status change, instead
+ * of doing "if we handled a watch status might
+ * have changed"
+ *
+ * Fixing this lets us move dbus_watch_handle
+ * calls into dbus-mainloop.c
+ */
+ if (_dbus_babysitter_get_child_exited (babysitter))
+ {
+ DBusError error;
+ DBusHashIter iter;
+ dbus_bool_t activation_failed;
+ int exit_code = 0;
+
+ dbus_error_init (&error);
+
+ _dbus_babysitter_set_child_exit_error (babysitter, &error);
+
+ /* Explicitly check for SPAWN_CHILD_EXITED to avoid overwriting an
+ * exec error */
+ if (dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)
+ && _dbus_babysitter_get_child_exit_status (babysitter, &exit_code))
+ {
+ activation_failed = exit_code != 0;
+
+ dbus_error_free(&error);
+
+ if (activation_failed)
+ {
+ if (uses_servicehelper)
+ handle_servicehelper_exit_error (exit_code, &error);
+ else
+ _dbus_babysitter_set_child_exit_error (babysitter, &error);
+ }
+ }
+ else
+ {
+ activation_failed = TRUE;
+ }
+
+ if (activation_failed)
+ {
+ /* Destroy all pending activations with the same exec */
+ _dbus_hash_iter_init (pending_activation->activation->pending_activations,
+ &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
+
+ if (p != pending_activation && strcmp (p->exec, pending_activation->exec) == 0)
+ pending_activation_failed (p, &error);
+ }
+
+ /* Destroys the pending activation */
+ pending_activation_failed (pending_activation, &error);
+
+ dbus_error_free (&error);
+ }
+ }
+
+ _dbus_babysitter_unref (babysitter);
+
+ return retval;
+}
+
+static dbus_bool_t
+add_babysitter_watch (DBusWatch *watch,
+ void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
+ watch, babysitter_watch_callback, pending_activation,
+ NULL);
+}
+
+static void
+remove_babysitter_watch (DBusWatch *watch,
+ void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
+ watch, babysitter_watch_callback, pending_activation);
+}
+
+static dbus_bool_t
+pending_activation_timed_out (void *data)
+{
+ BusPendingActivation *pending_activation = data;
+ DBusError error;
+
+ /* Kill the spawned process, since it sucks
+ * (not sure this is what we want to do, but
+ * may as well try it for now)
+ */
+ if (pending_activation->babysitter)
+ _dbus_babysitter_kill_child (pending_activation->babysitter);
+
+ dbus_error_init (&error);
+
+ dbus_set_error (&error, DBUS_ERROR_TIMED_OUT,
+ "Activation of %s timed out",
+ pending_activation->service_name);
+
+ pending_activation_failed (pending_activation, &error);
+
+ dbus_error_free (&error);
+
+ return TRUE;
+}
+
+static void
+cancel_pending (void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ _dbus_verbose ("Canceling pending activation of %s\n",
+ pending_activation->service_name);
+
+ if (pending_activation->babysitter)
+ _dbus_babysitter_kill_child (pending_activation->babysitter);
+
+ _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
+ pending_activation->service_name);
+}
+
+static void
+free_pending_cancel_data (void *data)
+{
+ BusPendingActivation *pending_activation = data;
+
+ bus_pending_activation_unref (pending_activation);
+}
+
+static dbus_bool_t
+add_cancel_pending_to_transaction (BusTransaction *transaction,
+ BusPendingActivation *pending_activation)
+{
+ if (!bus_transaction_add_cancel_hook (transaction, cancel_pending,
+ pending_activation,
+ free_pending_cancel_data))
+ return FALSE;
+
+ bus_pending_activation_ref (pending_activation);
+
+ _dbus_verbose ("Saved pending activation to be canceled if the transaction fails\n");
+
+ return TRUE;
+}
+
+static dbus_bool_t
+update_service_cache (BusActivation *activation, DBusError *error)
+{
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (activation->directories, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ DBusError tmp_error;
+ BusServiceDirectory *s_dir;
+
+ s_dir = _dbus_hash_iter_get_value (&iter);
+
+ dbus_error_init (&tmp_error);
+ if (!update_directory (activation, s_dir, &tmp_error))
+ {
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_move_error (&tmp_error, error);
+ return FALSE;
+ }
+
+ dbus_error_free (&tmp_error);
+ continue;
+ }
+ }
+
+ return TRUE;
+}
+
+static BusActivationEntry *
+activation_find_entry (BusActivation *activation,
+ const char *service_name,
+ DBusError *error)
+{
+ BusActivationEntry *entry;
+
+ entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
+ if (!entry)
+ {
+ if (!update_service_cache (activation, error))
+ return NULL;
+
+ entry = _dbus_hash_table_lookup_string (activation->entries,
+ service_name);
+ }
+ else
+ {
+ BusActivationEntry *updated_entry;
+
+ if (!check_service_file (activation, entry, &updated_entry, error))
+ return NULL;
+
+ entry = updated_entry;
+ }
+
+ if (!entry)
+ {
+ dbus_set_error (error, DBUS_ERROR_SERVICE_UNKNOWN,
+ "The name %s was not provided by any .service files",
+ service_name);
+ return NULL;
+ }
+
+ return entry;
+}
+
+static char **
+bus_activation_get_environment (BusActivation *activation)
+{
+ char **environment;
+ int i, length;
+ DBusString entry;
+ DBusHashIter iter;
+
+ length = _dbus_hash_table_get_n_entries (activation->environment);
+
+ environment = dbus_new0 (char *, length + 1);
+
+ if (environment == NULL)
+ return NULL;
+
+ i = 0;
+ _dbus_hash_iter_init (activation->environment, &iter);
+
+ if (!_dbus_string_init (&entry))
+ {
+ dbus_free_string_array (environment);
+ return NULL;
+ }
+
+ while (_dbus_hash_iter_next (&iter))
+ {
+ const char *key, *value;
+
+ key = (const char *) _dbus_hash_iter_get_string_key (&iter);
+ value = (const char *) _dbus_hash_iter_get_value (&iter);
+
+ if (!_dbus_string_append_printf (&entry, "%s=%s", key, value))
+ break;
+
+ if (!_dbus_string_steal_data (&entry, environment + i))
+ break;
+ i++;
+ }
+
+ _dbus_string_free (&entry);
+
+ if (i != length)
+ {
+ dbus_free_string_array (environment);
+ environment = NULL;
+ }
+
+ return environment;
+}
+
+dbus_bool_t
+bus_activation_set_environment_variable (BusActivation *activation,
+ const char *key,
+ const char *value,
+ DBusError *error)
+{
+ char *hash_key;
+ char *hash_value;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+ hash_key = NULL;
+ hash_value = NULL;
+ hash_key = _dbus_strdup (key);
+
+ if (hash_key == NULL)
+ goto out;
+
+ hash_value = _dbus_strdup (value);
+
+ if (hash_value == NULL)
+ goto out;
+
+ if (!_dbus_hash_table_insert_string (activation->environment,
+ hash_key, hash_value))
+ goto out;
+
+ retval = TRUE;
+out:
+ if (retval == FALSE)
+ {
+ dbus_free (hash_key);
+ dbus_free (hash_value);
+ BUS_SET_OOM (error);
+ }
+
+ return retval;
+}
+
+dbus_bool_t
+bus_activation_activate_service (BusActivation *activation,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ dbus_bool_t auto_activation,
+ DBusMessage *activation_message,
+ const char *service_name,
+ DBusError *error)
+{
+ BusActivationEntry *entry;
+ BusPendingActivation *pending_activation;
+ BusPendingActivationEntry *pending_activation_entry;
+ DBusMessage *message;
+ DBusString service_str;
+ const char *servicehelper;
+ char **argv;
+ char **envp = NULL;
+ int argc;
+ dbus_bool_t retval;
+ DBusHashIter iter;
+ dbus_bool_t activated;
+ DBusString command;
+
+ activated = TRUE;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (activation->n_pending_activations >=
+ bus_context_get_max_pending_activations (activation->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of pending activations has been reached, activation of %s failed",
+ service_name);
+ return FALSE;
+ }
+
+ entry = activation_find_entry (activation, service_name, error);
+ if (!entry)
+ return FALSE;
+
+ /* Bypass the registry lookup if we're auto-activating, bus_dispatch would not
+ * call us if the service is already active.
+ */
+ if (!auto_activation)
+ {
+ /* Check if the service is active */
+ _dbus_string_init_const (&service_str, service_name);
+ if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
+ {
+ dbus_uint32_t result;
+
+ _dbus_verbose ("Service \"%s\" is already active\n", service_name);
+
+ message = dbus_message_new_method_return (activation_message);
+
+ if (!message)
+ {
+ _dbus_verbose ("No memory to create reply to activate message\n");
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ result = DBUS_START_REPLY_ALREADY_RUNNING;
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_UINT32, &result,
+ DBUS_TYPE_INVALID))
+ {
+ _dbus_verbose ("No memory to set args of reply to activate message\n");
+ BUS_SET_OOM (error);
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ retval = bus_transaction_send_from_driver (transaction, connection, message);
+ dbus_message_unref (message);
+ if (!retval)
+ {
+ _dbus_verbose ("Failed to send reply\n");
+ BUS_SET_OOM (error);
+ }
+
+ return retval;
+ }
+ }
+
+ pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
+ if (!pending_activation_entry)
+ {
+ _dbus_verbose ("Failed to create pending activation entry\n");
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ pending_activation_entry->auto_activation = auto_activation;
+
+ pending_activation_entry->activation_message = activation_message;
+ dbus_message_ref (activation_message);
+ pending_activation_entry->connection = connection;
+ dbus_connection_ref (connection);
+
+ /* Check if the service is being activated */
+ pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
+ if (pending_activation)
+ {
+ if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
+ {
+ _dbus_verbose ("Failed to append a new entry to pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->n_entries += 1;
+ pending_activation->activation->n_pending_activations += 1;
+ }
+ else
+ {
+ pending_activation = dbus_new0 (BusPendingActivation, 1);
+ if (!pending_activation)
+ {
+ _dbus_verbose ("Failed to create pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->activation = activation;
+ pending_activation->refcount = 1;
+
+ pending_activation->service_name = _dbus_strdup (service_name);
+ if (!pending_activation->service_name)
+ {
+ _dbus_verbose ("Failed to copy service name for pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->exec = _dbus_strdup (entry->exec);
+ if (!pending_activation->exec)
+ {
+ _dbus_verbose ("Failed to copy service exec for pending activation\n");
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->timeout =
+ _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
+ pending_activation_timed_out,
+ pending_activation,
+ NULL);
+ if (!pending_activation->timeout)
+ {
+ _dbus_verbose ("Failed to create timeout for pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
+ pending_activation->timeout,
+ handle_timeout_callback,
+ pending_activation,
+ NULL))
+ {
+ _dbus_verbose ("Failed to add timeout for pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->timeout_added = TRUE;
+
+ if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
+ {
+ _dbus_verbose ("Failed to add entry to just-created pending activation\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ bus_pending_activation_entry_free (pending_activation_entry);
+ return FALSE;
+ }
+
+ pending_activation->n_entries += 1;
+ pending_activation->activation->n_pending_activations += 1;
+
+ activated = FALSE;
+ _dbus_hash_iter_init (activation->pending_activations, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
+
+ if (strcmp (p->exec, entry->exec) == 0)
+ {
+ activated = TRUE;
+ break;
+ }
+ }
+
+ if (!_dbus_hash_table_insert_string (activation->pending_activations,
+ pending_activation->service_name,
+ pending_activation))
+ {
+ _dbus_verbose ("Failed to put pending activation in hash table\n");
+
+ BUS_SET_OOM (error);
+ bus_pending_activation_unref (pending_activation);
+ return FALSE;
+ }
+ }
+
+ if (!add_cancel_pending_to_transaction (transaction, pending_activation))
+ {
+ _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
+ BUS_SET_OOM (error);
+ _dbus_hash_table_remove_string (activation->pending_activations,
+ pending_activation->service_name);
+
+ return FALSE;
+ }
+
+ if (activated)
+ return TRUE;
+
+ /* use command as system and session different */
+ if (!_dbus_string_init (&command))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* does the bus use a helper? */
+ servicehelper = bus_context_get_servicehelper (activation->context);
+ if (servicehelper != NULL)
+ {
+ if (entry->user == NULL)
+ {
+ _dbus_string_free (&command);
+ dbus_set_error (error, DBUS_ERROR_SPAWN_FILE_INVALID,
+ "Cannot do system-bus activation with no user\n");
+ return FALSE;
+ }
+
+ /* join the helper path and the service name */
+ if (!_dbus_string_append (&command, servicehelper))
+ {
+ _dbus_string_free (&command);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ if (!_dbus_string_append (&command, " "))
+ {
+ _dbus_string_free (&command);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ if (!_dbus_string_append (&command, service_name))
+ {
+ _dbus_string_free (&command);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* the bus does not use a helper, so we can append arguments with the exec line */
+ if (!_dbus_string_append (&command, entry->exec))
+ {
+ _dbus_string_free (&command);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ /* convert command into arguments */
+ if (!_dbus_shell_parse_argv (_dbus_string_get_const_data (&command), &argc, &argv, error))
+ {
+ _dbus_verbose ("Failed to parse command line: %s\n", entry->exec);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ _dbus_hash_table_remove_string (activation->pending_activations,
+ pending_activation->service_name);
+
+ _dbus_string_free (&command);
+ return FALSE;
+ }
+ _dbus_string_free (&command);
+
+ if (!add_bus_environment (activation, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_free_string_array (argv);
+ return FALSE;
+ }
+
+ envp = bus_activation_get_environment (activation);
+
+ if (envp == NULL)
+ {
+ BUS_SET_OOM (error);
+ dbus_free_string_array (argv);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Spawning %s ...\n", argv[0]);
+ if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
+ envp,
+ NULL, activation,
+ error))
+ {
+ _dbus_verbose ("Failed to spawn child\n");
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_free_string_array (argv);
+ dbus_free_string_array (envp);
+
+ return FALSE;
+ }
+
+ dbus_free_string_array (argv);
+ envp = NULL;
+
+ _dbus_assert (pending_activation->babysitter != NULL);
+
+ if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
+ add_babysitter_watch,
+ remove_babysitter_watch,
+ NULL,
+ pending_activation,
+ NULL))
+ {
+ BUS_SET_OOM (error);
+ _dbus_verbose ("Failed to set babysitter watch functions\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_activation_list_services (BusActivation *activation,
+ char ***listp,
+ int *array_len)
+{
+ int i, j, len;
+ char **retval;
+ DBusHashIter iter;
+
+ len = _dbus_hash_table_get_n_entries (activation->entries);
+ retval = dbus_new (char *, len + 1);
+
+ if (retval == NULL)
+ return FALSE;
+
+ _dbus_hash_iter_init (activation->entries, &iter);
+ i = 0;
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusActivationEntry *entry = _dbus_hash_iter_get_value (&iter);
+
+ retval[i] = _dbus_strdup (entry->name);
+ if (retval[i] == NULL)
+ goto error;
+
+ i++;
+ }
+
+ retval[i] = NULL;
+
+ if (array_len)
+ *array_len = len;
+
+ *listp = retval;
+ return TRUE;
+
+ error:
+ for (j = 0; j < i; j++)
+ dbus_free (retval[i]);
+ dbus_free (retval);
+
+ return FALSE;
+}
+
+
+#ifdef DBUS_BUILD_TESTS
+
+#include <stdio.h>
+
+#define SERVICE_NAME_1 "MyService1"
+#define SERVICE_NAME_2 "MyService2"
+#define SERVICE_NAME_3 "MyService3"
+
+#define SERVICE_FILE_1 "service-1.service"
+#define SERVICE_FILE_2 "service-2.service"
+#define SERVICE_FILE_3 "service-3.service"
+
+static dbus_bool_t
+test_create_service_file (DBusString *dir,
+ const char *filename,
+ const char *name,
+ const char *exec)
+{
+ DBusString file_name, full_path;
+ FILE *file;
+ dbus_bool_t ret_val;
+
+ ret_val = TRUE;
+ _dbus_string_init_const (&file_name, filename);
+
+ if (!_dbus_string_init (&full_path))
+ return FALSE;
+
+ if (!_dbus_string_append (&full_path, _dbus_string_get_const_data (dir)) ||
+ !_dbus_concat_dir_and_file (&full_path, &file_name))
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+ file = fopen (_dbus_string_get_const_data (&full_path), "w");
+ if (!file)
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+ fprintf (file, "[D-BUS Service]\nName=%s\nExec=%s\n", name, exec);
+ fclose (file);
+
+out:
+ _dbus_string_free (&full_path);
+ return ret_val;
+}
+
+static dbus_bool_t
+test_remove_service_file (DBusString *dir, const char *filename)
+{
+ DBusString file_name, full_path;
+ dbus_bool_t ret_val;
+
+ ret_val = TRUE;
+
+ _dbus_string_init_const (&file_name, filename);
+
+ if (!_dbus_string_init (&full_path))
+ return FALSE;
+
+ if (!_dbus_string_append (&full_path, _dbus_string_get_const_data (dir)) ||
+ !_dbus_concat_dir_and_file (&full_path, &file_name))
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_delete_file (&full_path, NULL))
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&full_path);
+ return ret_val;
+}
+
+static dbus_bool_t
+test_remove_directory (DBusString *dir)
+{
+ DBusDirIter *iter;
+ DBusString filename, full_path;
+ dbus_bool_t ret_val;
+
+ ret_val = TRUE;
+
+ if (!_dbus_string_init (&filename))
+ return FALSE;
+
+ if (!_dbus_string_init (&full_path))
+ {
+ _dbus_string_free (&filename);
+ return FALSE;
+ }
+
+ iter = _dbus_directory_open (dir, NULL);
+ if (iter == NULL)
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+ while (_dbus_directory_get_next_file (iter, &filename, NULL))
+ {
+ if (!test_remove_service_file (dir, _dbus_string_get_const_data (&filename)))
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+ }
+ _dbus_directory_close (iter);
+
+ if (!_dbus_delete_directory (dir, NULL))
+ {
+ ret_val = FALSE;
+ goto out;
+ }
+
+out:
+ _dbus_string_free (&filename);
+ _dbus_string_free (&full_path);
+
+ return ret_val;
+}
+
+static dbus_bool_t
+init_service_reload_test (DBusString *dir)
+{
+ DBusStat stat_buf;
+
+ if (!_dbus_stat (dir, &stat_buf, NULL))
+ {
+ if (!_dbus_create_directory (dir, NULL))
+ return FALSE;
+ }
+ else
+ {
+ if (!test_remove_directory (dir))
+ return FALSE;
+
+ if (!_dbus_create_directory (dir, NULL))
+ return FALSE;
+ }
+
+ /* Create one initial file */
+ if (!test_create_service_file (dir, SERVICE_FILE_1, SERVICE_NAME_1, "exec-1"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+cleanup_service_reload_test (DBusString *dir)
+{
+ if (!test_remove_directory (dir))
+ return FALSE;
+
+ return TRUE;
+}
+
+typedef struct
+{
+ BusActivation *activation;
+ const char *service_name;
+ dbus_bool_t expecting_find;
+} CheckData;
+
+static dbus_bool_t
+check_func (void *data)
+{
+ CheckData *d;
+ BusActivationEntry *entry;
+ DBusError error;
+ dbus_bool_t ret_val;
+
+ ret_val = TRUE;
+ d = data;
+
+ dbus_error_init (&error);
+
+ entry = activation_find_entry (d->activation, d->service_name, &error);
+ if (entry == NULL)
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ ret_val = TRUE;
+ }
+ else
+ {
+ if (d->expecting_find)
+ ret_val = FALSE;
+ }
+
+ dbus_error_free (&error);
+ }
+ else
+ {
+ if (!d->expecting_find)
+ ret_val = FALSE;
+ }
+
+ return ret_val;
+}
+
+static dbus_bool_t
+do_test (const char *description, dbus_bool_t oom_test, CheckData *data)
+{
+ dbus_bool_t err;
+
+ if (oom_test)
+ err = !_dbus_test_oom_handling (description, check_func, data);
+ else
+ err = !check_func (data);
+
+ if (err)
+ _dbus_assert_not_reached ("Test failed");
+
+ return TRUE;
+}
+
+static dbus_bool_t
+do_service_reload_test (DBusString *dir, dbus_bool_t oom_test)
+{
+ BusActivation *activation;
+ DBusString address;
+ DBusList *directories;
+ CheckData d;
+
+ directories = NULL;
+ _dbus_string_init_const (&address, "");
+
+ if (!_dbus_list_append (&directories, _dbus_string_get_data (dir)))
+ return FALSE;
+
+ activation = bus_activation_new (NULL, &address, &directories, NULL);
+ if (!activation)
+ return FALSE;
+
+ d.activation = activation;
+
+ /* Check for existing service file */
+ d.expecting_find = TRUE;
+ d.service_name = SERVICE_NAME_1;
+
+ if (!do_test ("Existing service file", oom_test, &d))
+ return FALSE;
+
+ /* Check for non-existing service file */
+ d.expecting_find = FALSE;
+ d.service_name = SERVICE_NAME_3;
+
+ if (!do_test ("Nonexisting service file", oom_test, &d))
+ return FALSE;
+
+ /* Check for added service file */
+ if (!test_create_service_file (dir, SERVICE_FILE_2, SERVICE_NAME_2, "exec-2"))
+ return FALSE;
+
+ d.expecting_find = TRUE;
+ d.service_name = SERVICE_NAME_2;
+
+ if (!do_test ("Added service file", oom_test, &d))
+ return FALSE;
+
+ /* Check for removed service file */
+ if (!test_remove_service_file (dir, SERVICE_FILE_2))
+ return FALSE;
+
+ d.expecting_find = FALSE;
+ d.service_name = SERVICE_FILE_2;
+
+ if (!do_test ("Removed service file", oom_test, &d))
+ return FALSE;
+
+ /* Check for updated service file */
+
+ _dbus_sleep_milliseconds (1000); /* Sleep a second to make sure the mtime is updated */
+
+ if (!test_create_service_file (dir, SERVICE_FILE_1, SERVICE_NAME_3, "exec-3"))
+ return FALSE;
+
+ d.expecting_find = TRUE;
+ d.service_name = SERVICE_NAME_3;
+
+ if (!do_test ("Updated service file, part 1", oom_test, &d))
+ return FALSE;
+
+ d.expecting_find = FALSE;
+ d.service_name = SERVICE_NAME_1;
+
+ if (!do_test ("Updated service file, part 2", oom_test, &d))
+ return FALSE;
+
+ bus_activation_unref (activation);
+ _dbus_list_clear (&directories);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_activation_service_reload_test (const DBusString *test_data_dir)
+{
+ DBusString directory;
+
+ if (!_dbus_string_init (&directory))
+ return FALSE;
+
+ if (!_dbus_string_append (&directory, _dbus_get_tmpdir()))
+ return FALSE;
+
+ if (!_dbus_string_append (&directory, "/dbus-reload-test-") ||
+ !_dbus_generate_random_ascii (&directory, 6))
+ {
+ return FALSE;
+ }
+
+ /* Do normal tests */
+ if (!init_service_reload_test (&directory))
+ _dbus_assert_not_reached ("could not initiate service reload test");
+
+ if (!do_service_reload_test (&directory, FALSE))
+ ; /* Do nothing? */
+
+ /* Do OOM tests */
+ if (!init_service_reload_test (&directory))
+ _dbus_assert_not_reached ("could not initiate service reload test");
+
+ if (!do_service_reload_test (&directory, TRUE))
+ ; /* Do nothing? */
+
+ /* Cleanup test directory */
+ if (!cleanup_service_reload_test (&directory))
+ return FALSE;
+
+ _dbus_string_free (&directory);
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/bus/activation.h b/bus/activation.h
new file mode 100644
index 00000000..03bfed28
--- /dev/null
+++ b/bus/activation.h
@@ -0,0 +1,68 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* activation.h Activation of services
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (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
+ *
+ */
+
+#ifndef BUS_ACTIVATION_H
+#define BUS_ACTIVATION_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-list.h>
+#include "bus.h"
+
+BusActivation* bus_activation_new (BusContext *context,
+ const DBusString *address,
+ DBusList **directories,
+ DBusError *error);
+dbus_bool_t bus_activation_reload (BusActivation *activation,
+ const DBusString *address,
+ DBusList **directories,
+ DBusError *error);
+BusActivation* bus_activation_ref (BusActivation *activation);
+void bus_activation_unref (BusActivation *activation);
+
+dbus_bool_t bus_activation_set_environment_variable (BusActivation *activation,
+ const char *key,
+ const char *value,
+ DBusError *error);
+dbus_bool_t bus_activation_activate_service (BusActivation *activation,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ dbus_bool_t auto_activation,
+ DBusMessage *activation_message,
+ const char *service_name,
+ DBusError *error);
+dbus_bool_t bus_activation_service_created (BusActivation *activation,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_activation_list_services (BusActivation *registry,
+ char ***listp,
+ int *array_len);
+
+dbus_bool_t bus_activation_send_pending_auto_activation_messages (BusActivation *activation,
+ BusService *service,
+ BusTransaction *transaction,
+ DBusError *error);
+
+
+
+#endif /* BUS_ACTIVATION_H */
diff --git a/bus/bus.c b/bus/bus.c
new file mode 100644
index 00000000..fd8a8724
--- /dev/null
+++ b/bus/bus.c
@@ -0,0 +1,1569 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* bus.c message bus context object
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * 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 "bus.h"
+#include "activation.h"
+#include "connection.h"
+#include "services.h"
+#include "utils.h"
+#include "policy.h"
+#include "config-parser.h"
+#include "signals.h"
+#include "selinux.h"
+#include "dir-watch.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-credentials.h>
+#include <dbus/dbus-internals.h>
+
+struct BusContext
+{
+ int refcount;
+ DBusGUID uuid;
+ char *config_file;
+ char *type;
+ char *servicehelper;
+ char *address;
+ char *pidfile;
+ char *user;
+ char *log_prefix;
+ DBusLoop *loop;
+ DBusList *servers;
+ BusConnections *connections;
+ BusActivation *activation;
+ BusRegistry *registry;
+ BusPolicy *policy;
+ BusMatchmaker *matchmaker;
+ BusLimits limits;
+ unsigned int fork : 1;
+ unsigned int syslog : 1;
+ unsigned int keep_umask : 1;
+};
+
+static dbus_int32_t server_data_slot = -1;
+
+typedef struct
+{
+ BusContext *context;
+} BusServerData;
+
+#define BUS_SERVER_DATA(server) (dbus_server_get_data ((server), server_data_slot))
+
+static BusContext*
+server_get_context (DBusServer *server)
+{
+ BusContext *context;
+ BusServerData *bd;
+
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
+ return NULL;
+
+ bd = BUS_SERVER_DATA (server);
+ if (bd == NULL)
+ {
+ dbus_server_free_data_slot (&server_data_slot);
+ return NULL;
+ }
+
+ context = bd->context;
+
+ dbus_server_free_data_slot (&server_data_slot);
+
+ return context;
+}
+
+static dbus_bool_t
+server_watch_callback (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ /* FIXME this can be done in dbus-mainloop.c
+ * if the code in activation.c for the babysitter
+ * watch handler is fixed.
+ */
+
+ return dbus_watch_handle (watch, condition);
+}
+
+static dbus_bool_t
+add_server_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusServer *server = data;
+ BusContext *context;
+
+ context = server_get_context (server);
+
+ return _dbus_loop_add_watch (context->loop,
+ watch, server_watch_callback, server,
+ NULL);
+}
+
+static void
+remove_server_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusServer *server = data;
+ BusContext *context;
+
+ context = server_get_context (server);
+
+ _dbus_loop_remove_watch (context->loop,
+ watch, server_watch_callback, server);
+}
+
+
+static void
+server_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ /* can return FALSE on OOM but we just let it fire again later */
+ dbus_timeout_handle (timeout);
+}
+
+static dbus_bool_t
+add_server_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusServer *server = data;
+ BusContext *context;
+
+ context = server_get_context (server);
+
+ return _dbus_loop_add_timeout (context->loop,
+ timeout, server_timeout_callback, server, NULL);
+}
+
+static void
+remove_server_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusServer *server = data;
+ BusContext *context;
+
+ context = server_get_context (server);
+
+ _dbus_loop_remove_timeout (context->loop,
+ timeout, server_timeout_callback, server);
+}
+
+static void
+new_connection_callback (DBusServer *server,
+ DBusConnection *new_connection,
+ void *data)
+{
+ BusContext *context = data;
+
+ if (!bus_connections_setup_connection (context->connections, new_connection))
+ {
+ _dbus_verbose ("No memory to setup new connection\n");
+
+ /* if we don't do this, it will get unref'd without
+ * being disconnected... kind of strange really
+ * that we have to do this, people won't get it right
+ * in general.
+ */
+ dbus_connection_close (new_connection);
+ }
+
+ dbus_connection_set_max_received_size (new_connection,
+ context->limits.max_incoming_bytes);
+
+ dbus_connection_set_max_message_size (new_connection,
+ context->limits.max_message_size);
+
+ /* on OOM, we won't have ref'd the connection so it will die. */
+}
+
+static void
+free_server_data (void *data)
+{
+ BusServerData *bd = data;
+
+ dbus_free (bd);
+}
+
+static dbus_bool_t
+setup_server (BusContext *context,
+ DBusServer *server,
+ char **auth_mechanisms,
+ DBusError *error)
+{
+ BusServerData *bd;
+
+ bd = dbus_new0 (BusServerData, 1);
+ if (bd == NULL || !dbus_server_set_data (server,
+ server_data_slot,
+ bd, free_server_data))
+ {
+ dbus_free (bd);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ bd->context = context;
+
+ if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ dbus_server_set_new_connection_function (server,
+ new_connection_callback,
+ context, NULL);
+
+ if (!dbus_server_set_watch_functions (server,
+ add_server_watch,
+ remove_server_watch,
+ NULL,
+ server,
+ NULL))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_server_set_timeout_functions (server,
+ add_server_timeout,
+ remove_server_timeout,
+ NULL,
+ server, NULL))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* This code only gets executed the first time the
+ * config files are parsed. It is not executed
+ * when config files are reloaded.
+ */
+static dbus_bool_t
+process_config_first_time_only (BusContext *context,
+ BusConfigParser *parser,
+ DBusError *error)
+{
+ DBusString log_prefix;
+ DBusList *link;
+ DBusList **addresses;
+ const char *user, *pidfile;
+ char **auth_mechanisms;
+ DBusList **auth_mechanisms_list;
+ int len;
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ retval = FALSE;
+ auth_mechanisms = NULL;
+
+ /* Check for an existing pid file. Of course this is a race;
+ * we'd have to use fcntl() locks on the pid file to
+ * avoid that. But we want to check for the pid file
+ * before overwriting any existing sockets, etc.
+ */
+ pidfile = bus_config_parser_get_pidfile (parser);
+ if (pidfile != NULL)
+ {
+ DBusString u;
+ DBusStat stbuf;
+
+ _dbus_string_init_const (&u, pidfile);
+
+ if (_dbus_stat (&u, &stbuf, NULL))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "The pid file \"%s\" exists, if the message bus is not running, remove this file",
+ pidfile);
+ goto failed;
+ }
+ }
+
+ /* keep around the pid filename so we can delete it later */
+ context->pidfile = _dbus_strdup (pidfile);
+
+ /* note that type may be NULL */
+ context->type = _dbus_strdup (bus_config_parser_get_type (parser));
+ if (bus_config_parser_get_type (parser) != NULL && context->type == NULL)
+ goto oom;
+
+ user = bus_config_parser_get_user (parser);
+ if (user != NULL)
+ {
+ context->user = _dbus_strdup (user);
+ if (context->user == NULL)
+ goto oom;
+ }
+
+ /* Set up the prefix for syslog messages */
+ if (!_dbus_string_init (&log_prefix))
+ goto oom;
+ if (context->type && !strcmp (context->type, "system"))
+ {
+ if (!_dbus_string_append (&log_prefix, "[system] "))
+ goto oom;
+ }
+ else if (context->type && !strcmp (context->type, "session"))
+ {
+ DBusCredentials *credentials;
+
+ credentials = _dbus_credentials_new_from_current_process ();
+ if (!credentials)
+ goto oom;
+ if (!_dbus_string_append (&log_prefix, "[session "))
+ goto oom;
+ if (!_dbus_credentials_to_string_append (credentials, &log_prefix))
+ goto oom;
+ if (!_dbus_string_append (&log_prefix, "] "))
+ goto oom;
+ _dbus_credentials_unref (credentials);
+ }
+ if (!_dbus_string_steal_data (&log_prefix, &context->log_prefix))
+ goto oom;
+ _dbus_string_free (&log_prefix);
+
+ /* Build an array of auth mechanisms */
+
+ auth_mechanisms_list = bus_config_parser_get_mechanisms (parser);
+ len = _dbus_list_get_length (auth_mechanisms_list);
+
+ if (len > 0)
+ {
+ int i;
+
+ auth_mechanisms = dbus_new0 (char*, len + 1);
+ if (auth_mechanisms == NULL)
+ goto oom;
+
+ i = 0;
+ link = _dbus_list_get_first_link (auth_mechanisms_list);
+ while (link != NULL)
+ {
+ auth_mechanisms[i] = _dbus_strdup (link->data);
+ if (auth_mechanisms[i] == NULL)
+ goto oom;
+ link = _dbus_list_get_next_link (auth_mechanisms_list, link);
+ }
+ }
+ else
+ {
+ auth_mechanisms = NULL;
+ }
+
+ /* Listen on our addresses */
+
+ addresses = bus_config_parser_get_addresses (parser);
+
+ link = _dbus_list_get_first_link (addresses);
+ while (link != NULL)
+ {
+ DBusServer *server;
+
+ server = dbus_server_listen (link->data, error);
+ if (server == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ else if (!setup_server (context, server, auth_mechanisms, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (!_dbus_list_append (&context->servers, server))
+ goto oom;
+
+ link = _dbus_list_get_next_link (addresses, link);
+ }
+
+ context->fork = bus_config_parser_get_fork (parser);
+ context->syslog = bus_config_parser_get_syslog (parser);
+ context->keep_umask = bus_config_parser_get_keep_umask (parser);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = TRUE;
+
+ failed:
+ dbus_free_string_array (auth_mechanisms);
+ return retval;
+
+ oom:
+ BUS_SET_OOM (error);
+ dbus_free_string_array (auth_mechanisms);
+ return FALSE;
+}
+
+/* This code gets executed every time the config files
+ * are parsed: both during BusContext construction
+ * and on reloads. This function is slightly screwy
+ * since it can do a "half reload" in out-of-memory
+ * situations. Realistically, unlikely to ever matter.
+ */
+static dbus_bool_t
+process_config_every_time (BusContext *context,
+ BusConfigParser *parser,
+ dbus_bool_t is_reload,
+ DBusError *error)
+{
+ DBusString full_address;
+ DBusList *link;
+ DBusList **dirs;
+ BusActivation *new_activation;
+ char *addr;
+ const char *servicehelper;
+ char *s;
+
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ addr = NULL;
+ retval = FALSE;
+
+ if (!_dbus_string_init (&full_address))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* get our limits and timeout lengths */
+ bus_config_parser_get_limits (parser, &context->limits);
+
+ if (context->policy)
+ bus_policy_unref (context->policy);
+ context->policy = bus_config_parser_steal_policy (parser);
+ _dbus_assert (context->policy != NULL);
+
+ /* We have to build the address backward, so that
+ * <listen> later in the config file have priority
+ */
+ link = _dbus_list_get_last_link (&context->servers);
+ while (link != NULL)
+ {
+ addr = dbus_server_get_address (link->data);
+ if (addr == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (_dbus_string_get_length (&full_address) > 0)
+ {
+ if (!_dbus_string_append (&full_address, ";"))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+
+ if (!_dbus_string_append (&full_address, addr))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ dbus_free (addr);
+ addr = NULL;
+
+ link = _dbus_list_get_prev_link (&context->servers, link);
+ }
+
+ if (is_reload)
+ dbus_free (context->address);
+
+ if (!_dbus_string_copy_data (&full_address, &context->address))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ /* get the service directories */
+ dirs = bus_config_parser_get_service_dirs (parser);
+
+ /* and the service helper */
+ servicehelper = bus_config_parser_get_servicehelper (parser);
+
+ s = _dbus_strdup(servicehelper);
+ if (s == NULL && servicehelper != NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ else
+ {
+ dbus_free(context->servicehelper);
+ context->servicehelper = s;
+ }
+
+ /* Create activation subsystem */
+ if (context->activation)
+ {
+ if (!bus_activation_reload (context->activation, &full_address, dirs, error))
+ goto failed;
+ }
+ else
+ {
+ context->activation = bus_activation_new (context, &full_address, dirs, error);
+ }
+
+ if (context->activation == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = TRUE;
+
+ failed:
+ _dbus_string_free (&full_address);
+
+ if (addr)
+ dbus_free (addr);
+
+ return retval;
+}
+
+static dbus_bool_t
+list_concat_new (DBusList **a,
+ DBusList **b,
+ DBusList **result)
+{
+ DBusList *link;
+
+ *result = NULL;
+
+ link = _dbus_list_get_first_link (a);
+ for (link = _dbus_list_get_first_link (a); link; link = _dbus_list_get_next_link (a, link))
+ {
+ if (!_dbus_list_append (result, link->data))
+ goto oom;
+ }
+ for (link = _dbus_list_get_first_link (b); link; link = _dbus_list_get_next_link (b, link))
+ {
+ if (!_dbus_list_append (result, link->data))
+ goto oom;
+ }
+
+ return TRUE;
+oom:
+ _dbus_list_clear (result);
+ return FALSE;
+}
+
+static dbus_bool_t
+process_config_postinit (BusContext *context,
+ BusConfigParser *parser,
+ DBusError *error)
+{
+ DBusHashTable *service_context_table;
+ DBusList *watched_dirs = NULL;
+
+ service_context_table = bus_config_parser_steal_service_context_table (parser);
+ if (!bus_registry_set_service_context_table (context->registry,
+ service_context_table))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_hash_table_unref (service_context_table);
+
+ /* We need to monitor both the configuration directories and directories
+ * containing .service files.
+ */
+ if (!list_concat_new (bus_config_parser_get_conf_dirs (parser),
+ bus_config_parser_get_service_dirs (parser),
+ &watched_dirs))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ bus_set_watched_dirs (context, &watched_dirs);
+
+ _dbus_list_clear (&watched_dirs);
+
+ return TRUE;
+}
+
+BusContext*
+bus_context_new (const DBusString *config_file,
+ ForceForkSetting force_fork,
+ DBusPipe *print_addr_pipe,
+ DBusPipe *print_pid_pipe,
+ DBusError *error)
+{
+ DBusString log_prefix;
+ BusContext *context;
+ BusConfigParser *parser;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ context = NULL;
+ parser = NULL;
+
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ context = dbus_new0 (BusContext, 1);
+ if (context == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ context->refcount = 1;
+
+ _dbus_generate_uuid (&context->uuid);
+
+ if (!_dbus_string_copy_data (config_file, &context->config_file))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ context->loop = _dbus_loop_new ();
+ if (context->loop == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ context->registry = bus_registry_new (context);
+ if (context->registry == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ parser = bus_config_load (config_file, TRUE, NULL, error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (!process_config_first_time_only (context, parser, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ if (!process_config_every_time (context, parser, FALSE, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ /* we need another ref of the server data slot for the context
+ * to own
+ */
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
+ _dbus_assert_not_reached ("second ref of server data slot failed");
+
+ /* Note that we don't know whether the print_addr_pipe is
+ * one of the sockets we're using to listen on, or some
+ * other random thing. But I think the answer is "don't do
+ * that then"
+ */
+ if (print_addr_pipe != NULL && _dbus_pipe_is_valid (print_addr_pipe))
+ {
+ DBusString addr;
+ const char *a = bus_context_get_address (context);
+ int bytes;
+
+ _dbus_assert (a != NULL);
+ if (!_dbus_string_init (&addr))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_string_append (&addr, a) ||
+ !_dbus_string_append (&addr, "\n"))
+ {
+ _dbus_string_free (&addr);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ bytes = _dbus_string_get_length (&addr);
+ if (_dbus_pipe_write (print_addr_pipe, &addr, 0, bytes, error) != bytes)
+ {
+ /* pipe write returns an error on failure but not short write */
+ if (error != NULL && !dbus_error_is_set (error))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Printing message bus address: did not write all bytes\n");
+ }
+ _dbus_string_free (&addr);
+ goto failed;
+ }
+
+ if (!_dbus_pipe_is_stdout_or_stderr (print_addr_pipe))
+ _dbus_pipe_close (print_addr_pipe, NULL);
+
+ _dbus_string_free (&addr);
+ }
+
+ context->connections = bus_connections_new (context);
+ if (context->connections == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ context->matchmaker = bus_matchmaker_new ();
+ if (context->matchmaker == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ /* check user before we fork */
+ if (context->user != NULL)
+ {
+ if (!_dbus_verify_daemon_user (context->user))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not get UID and GID for username \"%s\"",
+ context->user);
+ goto failed;
+ }
+ }
+
+ /* Now become a daemon if appropriate and write out pid file in any case */
+ {
+ DBusString u;
+
+ if (context->pidfile)
+ _dbus_string_init_const (&u, context->pidfile);
+
+ if ((force_fork != FORK_NEVER && context->fork) || force_fork == FORK_ALWAYS)
+ {
+ _dbus_verbose ("Forking and becoming daemon\n");
+
+ if (!_dbus_become_daemon (context->pidfile ? &u : NULL,
+ print_pid_pipe,
+ error,
+ context->keep_umask))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ }
+ else
+ {
+ _dbus_verbose ("Fork not requested\n");
+
+ /* Need to write PID file and to PID pipe for ourselves,
+ * not for the child process. This is a no-op if the pidfile
+ * is NULL and print_pid_pipe is NULL.
+ */
+ if (!_dbus_write_pid_to_file_and_pipe (context->pidfile ? &u : NULL,
+ print_pid_pipe,
+ _dbus_getpid (),
+ error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ }
+ }
+
+ if (print_pid_pipe && _dbus_pipe_is_valid (print_pid_pipe) &&
+ !_dbus_pipe_is_stdout_or_stderr (print_pid_pipe))
+ _dbus_pipe_close (print_pid_pipe, NULL);
+
+ if (!bus_selinux_full_init ())
+ {
+ bus_context_log (context, DBUS_SYSTEM_LOG_FATAL, "SELinux enabled but AVC initialization failed; check system log\n");
+ }
+
+ if (!process_config_postinit (context, parser, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (parser != NULL)
+ {
+ bus_config_parser_unref (parser);
+ parser = NULL;
+ }
+
+ /* Here we change our credentials if required,
+ * as soon as we've set up our sockets and pidfile
+ */
+ if (context->user != NULL)
+ {
+ if (!_dbus_change_to_daemon_user (context->user, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+#ifdef HAVE_SELINUX
+ /* FIXME - why not just put this in full_init() below? */
+ bus_selinux_audit_init ();
+#endif
+ }
+
+ dbus_server_free_data_slot (&server_data_slot);
+
+ return context;
+
+ failed:
+ if (parser != NULL)
+ bus_config_parser_unref (parser);
+ if (context != NULL)
+ bus_context_unref (context);
+
+ if (server_data_slot >= 0)
+ dbus_server_free_data_slot (&server_data_slot);
+
+ return NULL;
+}
+
+dbus_bool_t
+bus_context_get_id (BusContext *context,
+ DBusString *uuid)
+{
+ return _dbus_uuid_encode (&context->uuid, uuid);
+}
+
+dbus_bool_t
+bus_context_reload_config (BusContext *context,
+ DBusError *error)
+{
+ BusConfigParser *parser;
+ DBusString config_file;
+ dbus_bool_t ret;
+
+ /* Flush the user database cache */
+ _dbus_flush_caches ();
+
+ ret = FALSE;
+ _dbus_string_init_const (&config_file, context->config_file);
+ parser = bus_config_load (&config_file, TRUE, NULL, error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (!process_config_every_time (context, parser, TRUE, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ if (!process_config_postinit (context, parser, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ ret = TRUE;
+
+ bus_context_log (context, DBUS_SYSTEM_LOG_INFO, "Reloaded configuration");
+ failed:
+ if (!ret)
+ bus_context_log (context, DBUS_SYSTEM_LOG_INFO, "Unable to reload configuration: %s", error->message);
+ if (parser != NULL)
+ bus_config_parser_unref (parser);
+ return ret;
+}
+
+static void
+shutdown_server (BusContext *context,
+ DBusServer *server)
+{
+ if (server == NULL ||
+ !dbus_server_get_is_connected (server))
+ return;
+
+ if (!dbus_server_set_watch_functions (server,
+ NULL, NULL, NULL,
+ context,
+ NULL))
+ _dbus_assert_not_reached ("setting watch functions to NULL failed");
+
+ if (!dbus_server_set_timeout_functions (server,
+ NULL, NULL, NULL,
+ context,
+ NULL))
+ _dbus_assert_not_reached ("setting timeout functions to NULL failed");
+
+ dbus_server_disconnect (server);
+}
+
+void
+bus_context_shutdown (BusContext *context)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&context->servers);
+ while (link != NULL)
+ {
+ shutdown_server (context, link->data);
+
+ link = _dbus_list_get_next_link (&context->servers, link);
+ }
+}
+
+BusContext *
+bus_context_ref (BusContext *context)
+{
+ _dbus_assert (context->refcount > 0);
+ context->refcount += 1;
+
+ return context;
+}
+
+void
+bus_context_unref (BusContext *context)
+{
+ _dbus_assert (context->refcount > 0);
+ context->refcount -= 1;
+
+ if (context->refcount == 0)
+ {
+ DBusList *link;
+
+ _dbus_verbose ("Finalizing bus context %p\n", context);
+
+ bus_context_shutdown (context);
+
+ if (context->connections)
+ {
+ bus_connections_unref (context->connections);
+ context->connections = NULL;
+ }
+
+ if (context->registry)
+ {
+ bus_registry_unref (context->registry);
+ context->registry = NULL;
+ }
+
+ if (context->activation)
+ {
+ bus_activation_unref (context->activation);
+ context->activation = NULL;
+ }
+
+ link = _dbus_list_get_first_link (&context->servers);
+ while (link != NULL)
+ {
+ dbus_server_unref (link->data);
+
+ link = _dbus_list_get_next_link (&context->servers, link);
+ }
+ _dbus_list_clear (&context->servers);
+
+ if (context->policy)
+ {
+ bus_policy_unref (context->policy);
+ context->policy = NULL;
+ }
+
+ if (context->loop)
+ {
+ _dbus_loop_unref (context->loop);
+ context->loop = NULL;
+ }
+
+ if (context->matchmaker)
+ {
+ bus_matchmaker_unref (context->matchmaker);
+ context->matchmaker = NULL;
+ }
+
+ dbus_free (context->config_file);
+ dbus_free (context->log_prefix);
+ dbus_free (context->type);
+ dbus_free (context->address);
+ dbus_free (context->user);
+ dbus_free (context->servicehelper);
+
+ if (context->pidfile)
+ {
+ DBusString u;
+ _dbus_string_init_const (&u, context->pidfile);
+
+ /* Deliberately ignore errors here, since there's not much
+ * we can do about it, and we're exiting anyways.
+ */
+ _dbus_delete_file (&u, NULL);
+
+ dbus_free (context->pidfile);
+ }
+ dbus_free (context);
+
+ dbus_server_free_data_slot (&server_data_slot);
+ }
+}
+
+/* type may be NULL */
+const char*
+bus_context_get_type (BusContext *context)
+{
+ return context->type;
+}
+
+const char*
+bus_context_get_address (BusContext *context)
+{
+ return context->address;
+}
+
+const char*
+bus_context_get_servicehelper (BusContext *context)
+{
+ return context->servicehelper;
+}
+
+BusRegistry*
+bus_context_get_registry (BusContext *context)
+{
+ return context->registry;
+}
+
+BusConnections*
+bus_context_get_connections (BusContext *context)
+{
+ return context->connections;
+}
+
+BusActivation*
+bus_context_get_activation (BusContext *context)
+{
+ return context->activation;
+}
+
+BusMatchmaker*
+bus_context_get_matchmaker (BusContext *context)
+{
+ return context->matchmaker;
+}
+
+DBusLoop*
+bus_context_get_loop (BusContext *context)
+{
+ return context->loop;
+}
+
+dbus_bool_t
+bus_context_allow_unix_user (BusContext *context,
+ unsigned long uid)
+{
+ return bus_policy_allow_unix_user (context->policy,
+ uid);
+}
+
+/* For now this is never actually called because the default
+ * DBusConnection behavior of 'same user that owns the bus can connect'
+ * is all it would do.
+ */
+dbus_bool_t
+bus_context_allow_windows_user (BusContext *context,
+ const char *windows_sid)
+{
+ return bus_policy_allow_windows_user (context->policy,
+ windows_sid);
+}
+
+BusPolicy *
+bus_context_get_policy (BusContext *context)
+{
+ return context->policy;
+}
+
+BusClientPolicy*
+bus_context_create_client_policy (BusContext *context,
+ DBusConnection *connection,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return bus_policy_create_client_policy (context->policy, connection,
+ error);
+}
+
+int
+bus_context_get_activation_timeout (BusContext *context)
+{
+
+ return context->limits.activation_timeout;
+}
+
+int
+bus_context_get_auth_timeout (BusContext *context)
+{
+ return context->limits.auth_timeout;
+}
+
+int
+bus_context_get_max_completed_connections (BusContext *context)
+{
+ return context->limits.max_completed_connections;
+}
+
+int
+bus_context_get_max_incomplete_connections (BusContext *context)
+{
+ return context->limits.max_incomplete_connections;
+}
+
+int
+bus_context_get_max_connections_per_user (BusContext *context)
+{
+ return context->limits.max_connections_per_user;
+}
+
+int
+bus_context_get_max_pending_activations (BusContext *context)
+{
+ return context->limits.max_pending_activations;
+}
+
+int
+bus_context_get_max_services_per_connection (BusContext *context)
+{
+ return context->limits.max_services_per_connection;
+}
+
+int
+bus_context_get_max_match_rules_per_connection (BusContext *context)
+{
+ return context->limits.max_match_rules_per_connection;
+}
+
+int
+bus_context_get_max_replies_per_connection (BusContext *context)
+{
+ return context->limits.max_replies_per_connection;
+}
+
+int
+bus_context_get_reply_timeout (BusContext *context)
+{
+ return context->limits.reply_timeout;
+}
+
+void
+bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, ...) _DBUS_GNUC_PRINTF (3, 4);
+
+void
+bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, ...)
+{
+ va_list args;
+
+ if (!context->syslog)
+ return;
+
+ va_start (args, msg);
+
+ if (context->log_prefix)
+ {
+ DBusString full_msg;
+
+ if (!_dbus_string_init (&full_msg))
+ goto out;
+ if (!_dbus_string_append (&full_msg, context->log_prefix))
+ goto oom_out;
+ if (!_dbus_string_append_printf_valist (&full_msg, msg, args))
+ goto oom_out;
+
+ _dbus_system_log (severity, "%s", _dbus_string_get_const_data (&full_msg));
+ oom_out:
+ _dbus_string_free (&full_msg);
+ }
+ else
+ _dbus_system_logv (severity, msg, args);
+
+out:
+ va_end (args);
+}
+
+/*
+ * addressed_recipient is the recipient specified in the message.
+ *
+ * proposed_recipient is the recipient we're considering sending
+ * to right this second, and may be an eavesdropper.
+ *
+ * sender is the sender of the message.
+ *
+ * NULL for proposed_recipient or sender definitely means the bus driver.
+ *
+ * NULL for addressed_recipient may mean the bus driver, or may mean
+ * no destination was specified in the message (e.g. a signal).
+ */
+dbus_bool_t
+bus_context_check_security_policy (BusContext *context,
+ BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *dest;
+ BusClientPolicy *sender_policy;
+ BusClientPolicy *recipient_policy;
+ dbus_int32_t toggles;
+ dbus_bool_t log;
+ int type;
+ dbus_bool_t requested_reply;
+ const char *sender_name;
+ const char *sender_loginfo;
+ const char *proposed_recipient_loginfo;
+
+ type = dbus_message_get_type (message);
+ dest = dbus_message_get_destination (message);
+
+ /* dispatch.c was supposed to ensure these invariants */
+ _dbus_assert (dest != NULL ||
+ type == DBUS_MESSAGE_TYPE_SIGNAL ||
+ (sender == NULL && !bus_connection_is_active (proposed_recipient)));
+ _dbus_assert (type == DBUS_MESSAGE_TYPE_SIGNAL ||
+ addressed_recipient != NULL ||
+ strcmp (dest, DBUS_SERVICE_DBUS) == 0);
+
+ /* Used in logging below */
+ if (sender != NULL)
+ {
+ sender_name = bus_connection_get_name (sender);
+ sender_loginfo = bus_connection_get_loginfo (sender);
+ }
+ else
+ {
+ sender_name = NULL;
+ sender_loginfo = "(bus)";
+ }
+
+ if (proposed_recipient != NULL)
+ proposed_recipient_loginfo = bus_connection_get_loginfo (proposed_recipient);
+ else
+ proposed_recipient_loginfo = "bus";
+
+ switch (type)
+ {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ case DBUS_MESSAGE_TYPE_ERROR:
+ break;
+
+ default:
+ _dbus_verbose ("security check disallowing message of unknown type %d\n",
+ type);
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Message bus will not accept messages of unknown type\n");
+
+ return FALSE;
+ }
+
+ requested_reply = FALSE;
+
+ if (sender != NULL)
+ {
+ /* First verify the SELinux access controls. If allowed then
+ * go on with the standard checks.
+ */
+ if (!bus_selinux_allows_send (sender, proposed_recipient,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_error_name (message),
+ dest ? dest : DBUS_SERVICE_DBUS, error))
+ {
+ if (error != NULL && !dbus_error_is_set (error))
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "An SELinux policy prevents this sender "
+ "from sending this message to this recipient "
+ "(rejected message had sender \"%s\" interface \"%s\" "
+ "member \"%s\" error name \"%s\" destination \"%s\")",
+ sender_name ? sender_name : "(unset)",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dest ? dest : DBUS_SERVICE_DBUS);
+ _dbus_verbose ("SELinux security check denying send to service\n");
+ }
+
+ return FALSE;
+ }
+
+ if (bus_connection_is_active (sender))
+ {
+ sender_policy = bus_connection_get_policy (sender);
+ _dbus_assert (sender_policy != NULL);
+
+ /* Fill in requested_reply variable with TRUE if this is a
+ * reply and the reply was pending.
+ */
+ if (dbus_message_get_reply_serial (message) != 0)
+ {
+ if (proposed_recipient != NULL /* not to the bus driver */ &&
+ addressed_recipient == proposed_recipient /* not eavesdropping */)
+ {
+ DBusError error2;
+
+ dbus_error_init (&error2);
+ requested_reply = bus_connections_check_reply (bus_connection_get_connections (sender),
+ transaction,
+ sender, addressed_recipient, message,
+ &error2);
+ if (dbus_error_is_set (&error2))
+ {
+ dbus_move_error (&error2, error);
+ return FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Policy for inactive connections is that they can only send
+ * the hello message to the bus driver
+ */
+ if (proposed_recipient == NULL &&
+ dbus_message_is_method_call (message,
+ DBUS_INTERFACE_DBUS,
+ "Hello"))
+ {
+ _dbus_verbose ("security check allowing %s message\n",
+ "Hello");
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("security check disallowing non-%s message\n",
+ "Hello");
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Client tried to send a message other than %s without being registered",
+ "Hello");
+
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ sender_policy = NULL;
+
+ /* If the sender is the bus driver, we assume any reply was a
+ * requested reply as bus driver won't send bogus ones
+ */
+ if (addressed_recipient == proposed_recipient /* not eavesdropping */ &&
+ dbus_message_get_reply_serial (message) != 0)
+ requested_reply = TRUE;
+ }
+
+ _dbus_assert ((sender != NULL && sender_policy != NULL) ||
+ (sender == NULL && sender_policy == NULL));
+
+ if (proposed_recipient != NULL)
+ {
+ /* only the bus driver can send to an inactive recipient (as it
+ * owns no services, so other apps can't address it). Inactive
+ * recipients can receive any message.
+ */
+ if (bus_connection_is_active (proposed_recipient))
+ {
+ recipient_policy = bus_connection_get_policy (proposed_recipient);
+ _dbus_assert (recipient_policy != NULL);
+ }
+ else if (sender == NULL)
+ {
+ _dbus_verbose ("security check using NULL recipient policy for message from bus\n");
+ recipient_policy = NULL;
+ }
+ else
+ {
+ _dbus_assert_not_reached ("a message was somehow sent to an inactive recipient from a source other than the message bus\n");
+ recipient_policy = NULL;
+ }
+ }
+ else
+ recipient_policy = NULL;
+
+ _dbus_assert ((proposed_recipient != NULL && recipient_policy != NULL) ||
+ (proposed_recipient != NULL && sender == NULL && recipient_policy == NULL) ||
+ (proposed_recipient == NULL && recipient_policy == NULL));
+
+ log = FALSE;
+ if (sender_policy &&
+ !bus_client_policy_check_can_send (sender_policy,
+ context->registry,
+ requested_reply,
+ proposed_recipient,
+ message, &toggles, &log))
+ {
+ const char *msg = "Rejected send message, %d matched rules; "
+ "type=\"%s\", sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" requested_reply=%d destination=\"%s\" (%s))";
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, msg,
+ toggles,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ sender_name ? sender_name : "(unset)",
+ sender_loginfo,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ requested_reply,
+ dest ? dest : DBUS_SERVICE_DBUS,
+ proposed_recipient_loginfo);
+ /* Needs to be duplicated to avoid calling malloc and having to handle OOM */
+ if (addressed_recipient == proposed_recipient)
+ bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY, msg,
+ toggles,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ sender_name ? sender_name : "(unset)",
+ sender_loginfo,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ requested_reply,
+ dest ? dest : DBUS_SERVICE_DBUS,
+ proposed_recipient_loginfo);
+ _dbus_verbose ("security policy disallowing message due to sender policy\n");
+ return FALSE;
+ }
+
+ if (log)
+ bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY,
+ "Would reject message, %d matched rules; "
+ "type=\"%s\", sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" requested_reply=%d destination=\"%s\" (%s))",
+ toggles,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ sender_name ? sender_name : "(unset)",
+ sender_loginfo,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ requested_reply,
+ dest ? dest : DBUS_SERVICE_DBUS,
+ proposed_recipient_loginfo);
+
+ if (recipient_policy &&
+ !bus_client_policy_check_can_receive (recipient_policy,
+ context->registry,
+ requested_reply,
+ sender,
+ addressed_recipient, proposed_recipient,
+ message, &toggles))
+ {
+ const char *msg = "Rejected receive message, %d matched rules; "
+ "type=\"%s\" sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" reply serial=%u requested_reply=%d destination=\"%s\" (%s))";
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, msg,
+ toggles,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ sender_name ? sender_name : "(unset)",
+ sender_loginfo,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dbus_message_get_reply_serial (message),
+ requested_reply,
+ dest ? dest : DBUS_SERVICE_DBUS,
+ proposed_recipient_loginfo);
+ /* Needs to be duplicated to avoid calling malloc and having to handle OOM */
+ if (addressed_recipient == proposed_recipient)
+ bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY, msg,
+ toggles,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ sender_name ? sender_name : "(unset)",
+ sender_loginfo,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dbus_message_get_reply_serial (message),
+ requested_reply,
+ dest ? dest : DBUS_SERVICE_DBUS,
+ proposed_recipient_loginfo);
+ _dbus_verbose ("security policy disallowing message due to recipient policy\n");
+ return FALSE;
+ }
+
+ /* See if limits on size have been exceeded */
+ if (proposed_recipient &&
+ dbus_connection_get_outgoing_size (proposed_recipient) >
+ context->limits.max_outgoing_bytes)
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The destination service \"%s\" has a full message queue",
+ dest ? dest : (proposed_recipient ?
+ bus_connection_get_name (proposed_recipient) :
+ DBUS_SERVICE_DBUS));
+ _dbus_verbose ("security policy disallowing message due to full message queue\n");
+ return FALSE;
+ }
+
+ /* Record that we will allow a reply here in the future (don't
+ * bother if the recipient is the bus or this is an eavesdropping
+ * connection). Only the addressed recipient may reply.
+ */
+ if (type == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+ sender &&
+ addressed_recipient &&
+ addressed_recipient == proposed_recipient && /* not eavesdropping */
+ !bus_connections_expect_reply (bus_connection_get_connections (sender),
+ transaction,
+ sender, addressed_recipient,
+ message, error))
+ {
+ _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n");
+ return FALSE;
+ }
+
+ _dbus_verbose ("security policy allowing message\n");
+ return TRUE;
+}
diff --git a/bus/bus.h b/bus/bus.h
new file mode 100644
index 00000000..8a04daa1
--- /dev/null
+++ b/bus/bus.h
@@ -0,0 +1,122 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* bus.h message bus context object
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_BUS_H
+#define BUS_BUS_H
+
+#include <config.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-mainloop.h>
+
+typedef struct BusActivation BusActivation;
+typedef struct BusConnections BusConnections;
+typedef struct BusContext BusContext;
+typedef struct BusPolicy BusPolicy;
+typedef struct BusClientPolicy BusClientPolicy;
+typedef struct BusPolicyRule BusPolicyRule;
+typedef struct BusRegistry BusRegistry;
+typedef struct BusSELinuxID BusSELinuxID;
+typedef struct BusService BusService;
+typedef struct BusOwner BusOwner;
+typedef struct BusTransaction BusTransaction;
+typedef struct BusMatchmaker BusMatchmaker;
+typedef struct BusMatchRule BusMatchRule;
+
+typedef struct
+{
+ long max_incoming_bytes; /**< How many incoming message bytes for a single connection */
+ long max_outgoing_bytes; /**< How many outgoing bytes can be queued for a single connection */
+ long max_message_size; /**< Max size of a single message in bytes */
+ int activation_timeout; /**< How long to wait for an activation to time out */
+ int auth_timeout; /**< How long to wait for an authentication to time out */
+ int max_completed_connections; /**< Max number of authorized connections */
+ int max_incomplete_connections; /**< Max number of incomplete connections */
+ int max_connections_per_user; /**< Max number of connections auth'd as same user */
+ int max_pending_activations; /**< Max number of pending activations for the entire bus */
+ int max_services_per_connection; /**< Max number of owned services for a single connection */
+ int max_match_rules_per_connection; /**< Max number of match rules for a single connection */
+ int max_replies_per_connection; /**< Max number of replies that can be pending for each connection */
+ int reply_timeout; /**< How long to wait before timing out a reply */
+} BusLimits;
+
+typedef enum
+{
+ FORK_FOLLOW_CONFIG_FILE,
+ FORK_ALWAYS,
+ FORK_NEVER
+} ForceForkSetting;
+
+BusContext* bus_context_new (const DBusString *config_file,
+ ForceForkSetting force_fork,
+ DBusPipe *print_addr_pipe,
+ DBusPipe *print_pid_pipe,
+ DBusError *error);
+dbus_bool_t bus_context_reload_config (BusContext *context,
+ DBusError *error);
+void bus_context_shutdown (BusContext *context);
+BusContext* bus_context_ref (BusContext *context);
+void bus_context_unref (BusContext *context);
+dbus_bool_t bus_context_get_id (BusContext *context,
+ DBusString *uuid);
+const char* bus_context_get_type (BusContext *context);
+const char* bus_context_get_address (BusContext *context);
+const char* bus_context_get_servicehelper (BusContext *context);
+BusRegistry* bus_context_get_registry (BusContext *context);
+BusConnections* bus_context_get_connections (BusContext *context);
+BusActivation* bus_context_get_activation (BusContext *context);
+BusMatchmaker* bus_context_get_matchmaker (BusContext *context);
+DBusLoop* bus_context_get_loop (BusContext *context);
+dbus_bool_t bus_context_allow_unix_user (BusContext *context,
+ unsigned long uid);
+dbus_bool_t bus_context_allow_windows_user (BusContext *context,
+ const char *windows_sid);
+BusPolicy* bus_context_get_policy (BusContext *context);
+
+BusClientPolicy* bus_context_create_client_policy (BusContext *context,
+ DBusConnection *connection,
+ DBusError *error);
+int bus_context_get_activation_timeout (BusContext *context);
+int bus_context_get_auth_timeout (BusContext *context);
+int bus_context_get_max_completed_connections (BusContext *context);
+int bus_context_get_max_incomplete_connections (BusContext *context);
+int bus_context_get_max_connections_per_user (BusContext *context);
+int bus_context_get_max_pending_activations (BusContext *context);
+int bus_context_get_max_services_per_connection (BusContext *context);
+int bus_context_get_max_match_rules_per_connection (BusContext *context);
+int bus_context_get_max_replies_per_connection (BusContext *context);
+int bus_context_get_reply_timeout (BusContext *context);
+void bus_context_log (BusContext *context,
+ DBusSystemLogSeverity severity,
+ const char *msg,
+ ...);
+dbus_bool_t bus_context_check_security_policy (BusContext *context,
+ BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ DBusError *error);
+
+#endif /* BUS_BUS_H */
diff --git a/bus/config-loader-expat.c b/bus/config-loader-expat.c
new file mode 100644
index 00000000..c0620aed
--- /dev/null
+++ b/bus/config-loader-expat.c
@@ -0,0 +1,294 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-loader-expat.c expat XML loader
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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-parser.h"
+#include <dbus/dbus-internals.h>
+#include <expat.h>
+
+static XML_Memory_Handling_Suite memsuite =
+{
+ dbus_malloc,
+ dbus_realloc,
+ dbus_free
+};
+
+typedef struct
+{
+ BusConfigParser *parser;
+ const char *filename;
+ DBusString content;
+ DBusError *error;
+ dbus_bool_t failed;
+} ExpatParseContext;
+
+static dbus_bool_t
+process_content (ExpatParseContext *context)
+{
+ if (context->failed)
+ return FALSE;
+
+ if (_dbus_string_get_length (&context->content) > 0)
+ {
+ if (!bus_config_parser_content (context->parser,
+ &context->content,
+ context->error))
+ {
+ context->failed = TRUE;
+ return FALSE;
+ }
+ _dbus_string_set_length (&context->content, 0);
+ }
+
+ return TRUE;
+}
+
+static void
+expat_StartElementHandler (void *userData,
+ const XML_Char *name,
+ const XML_Char **atts)
+{
+ ExpatParseContext *context = userData;
+ int i;
+ char **names;
+ char **values;
+
+ /* Expat seems to suck and can't abort the parse if we
+ * throw an error. Expat 2.0 is supposed to fix this.
+ */
+ if (context->failed)
+ return;
+
+ if (!process_content (context))
+ return;
+
+ /* "atts" is key, value, key, value, NULL */
+ for (i = 0; atts[i] != NULL; ++i)
+ ; /* nothing */
+
+ _dbus_assert (i % 2 == 0);
+ names = dbus_new0 (char *, i / 2 + 1);
+ values = dbus_new0 (char *, i / 2 + 1);
+
+ if (names == NULL || values == NULL)
+ {
+ dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
+ context->failed = TRUE;
+ dbus_free (names);
+ dbus_free (values);
+ return;
+ }
+
+ i = 0;
+ while (atts[i] != NULL)
+ {
+ _dbus_assert (i % 2 == 0);
+ names [i / 2] = (char*) atts[i];
+ values[i / 2] = (char*) atts[i+1];
+
+ i += 2;
+ }
+
+ if (!bus_config_parser_start_element (context->parser,
+ name,
+ (const char **) names,
+ (const char **) values,
+ context->error))
+ {
+ dbus_free (names);
+ dbus_free (values);
+ context->failed = TRUE;
+ return;
+ }
+
+ dbus_free (names);
+ dbus_free (values);
+}
+
+static void
+expat_EndElementHandler (void *userData,
+ const XML_Char *name)
+{
+ ExpatParseContext *context = userData;
+
+ if (!process_content (context))
+ return;
+
+ if (!bus_config_parser_end_element (context->parser,
+ name,
+ context->error))
+ {
+ context->failed = TRUE;
+ return;
+ }
+}
+
+/* s is not 0 terminated. */
+static void
+expat_CharacterDataHandler (void *userData,
+ const XML_Char *s,
+ int len)
+{
+ ExpatParseContext *context = userData;
+ if (context->failed)
+ return;
+
+ if (!_dbus_string_append_len (&context->content,
+ s, len))
+ {
+ dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL);
+ context->failed = TRUE;
+ return;
+ }
+}
+
+
+BusConfigParser*
+bus_config_load (const DBusString *file,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent,
+ DBusError *error)
+{
+ XML_Parser expat;
+ const char *filename;
+ BusConfigParser *parser;
+ ExpatParseContext context;
+ DBusString dirname;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ parser = NULL;
+ expat = NULL;
+ context.error = error;
+ context.failed = FALSE;
+
+ filename = _dbus_string_get_const_data (file);
+
+ if (!_dbus_string_init (&context.content))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&dirname))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ _dbus_string_free (&context.content);
+ return NULL;
+ }
+
+ expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
+ if (expat == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (!_dbus_string_get_dirname (file, &dirname))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ parser = bus_config_parser_new (&dirname, is_toplevel, parent);
+ if (parser == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+ context.parser = parser;
+
+ XML_SetUserData (expat, &context);
+ XML_SetElementHandler (expat,
+ expat_StartElementHandler,
+ expat_EndElementHandler);
+ XML_SetCharacterDataHandler (expat,
+ expat_CharacterDataHandler);
+
+ {
+ DBusString data;
+ const char *data_str;
+
+ if (!_dbus_string_init (&data))
+ {
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ goto failed;
+ }
+
+ if (!_dbus_file_get_contents (&data, file, error))
+ {
+ _dbus_string_free (&data);
+ goto failed;
+ }
+
+ data_str = _dbus_string_get_const_data (&data);
+
+ if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE))
+ {
+ if (context.error != NULL &&
+ !dbus_error_is_set (context.error))
+ {
+ enum XML_Error e;
+
+ e = XML_GetErrorCode (expat);
+ if (e == XML_ERROR_NO_MEMORY)
+ dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
+ else
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Error in file %s, line %d, column %d: %s\n",
+ filename,
+ XML_GetCurrentLineNumber (expat),
+ XML_GetCurrentColumnNumber (expat),
+ XML_ErrorString (e));
+ }
+
+ _dbus_string_free (&data);
+ goto failed;
+ }
+
+ _dbus_string_free (&data);
+
+ if (context.failed)
+ goto failed;
+ }
+
+ if (!bus_config_parser_finished (parser, error))
+ goto failed;
+
+ _dbus_string_free (&dirname);
+ _dbus_string_free (&context.content);
+ XML_ParserFree (expat);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return parser;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ _dbus_string_free (&dirname);
+ _dbus_string_free (&context.content);
+ if (expat)
+ XML_ParserFree (expat);
+ if (parser)
+ bus_config_parser_unref (parser);
+ return NULL;
+}
diff --git a/bus/config-loader-libxml.c b/bus/config-loader-libxml.c
new file mode 100644
index 00000000..3d82a633
--- /dev/null
+++ b/bus/config-loader-libxml.c
@@ -0,0 +1,323 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-loader-libxml.c libxml2 XML loader
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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-parser.h"
+#include <dbus/dbus-internals.h>
+#include <libxml/xmlreader.h>
+#include <libxml/parser.h>
+#include <libxml/globals.h>
+#include <libxml/xmlmemory.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <string.h>
+
+/* About the error handling:
+ * - setup a "structured" error handler that catches structural
+ * errors and some oom errors
+ * - assume that a libxml function returning an error code means
+ * out-of-memory
+ */
+#define _DBUS_MAYBE_SET_OOM(e) (dbus_error_is_set(e) ? (void)0 : _DBUS_SET_OOM(e))
+
+
+static dbus_bool_t
+xml_text_start_element (BusConfigParser *parser,
+ xmlTextReader *reader,
+ DBusError *error)
+{
+ const char *name;
+ int n_attributes;
+ const char **attribute_names, **attribute_values;
+ dbus_bool_t ret;
+ int i, status, is_empty;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ ret = FALSE;
+ attribute_names = NULL;
+ attribute_values = NULL;
+
+ name = xmlTextReaderConstName (reader);
+ n_attributes = xmlTextReaderAttributeCount (reader);
+ is_empty = xmlTextReaderIsEmptyElement (reader);
+
+ if (name == NULL || n_attributes < 0 || is_empty == -1)
+ {
+ _DBUS_MAYBE_SET_OOM (error);
+ goto out;
+ }
+
+ attribute_names = dbus_new0 (const char *, n_attributes + 1);
+ attribute_values = dbus_new0 (const char *, n_attributes + 1);
+ if (attribute_names == NULL || attribute_values == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto out;
+ }
+ i = 0;
+ while ((status = xmlTextReaderMoveToNextAttribute (reader)) == 1)
+ {
+ _dbus_assert (i < n_attributes);
+ attribute_names[i] = xmlTextReaderConstName (reader);
+ attribute_values[i] = xmlTextReaderConstValue (reader);
+ if (attribute_names[i] == NULL || attribute_values[i] == NULL)
+ {
+ _DBUS_MAYBE_SET_OOM (error);
+ goto out;
+ }
+ i++;
+ }
+ if (status == -1)
+ {
+ _DBUS_MAYBE_SET_OOM (error);
+ goto out;
+ }
+ _dbus_assert (i == n_attributes);
+
+ ret = bus_config_parser_start_element (parser, name,
+ attribute_names, attribute_values,
+ error);
+ if (ret && is_empty == 1)
+ ret = bus_config_parser_end_element (parser, name, error);
+
+ out:
+ dbus_free (attribute_names);
+ dbus_free (attribute_values);
+
+ return ret;
+}
+
+static void xml_shut_up (void *ctx, const char *msg, ...)
+{
+ return;
+}
+
+static void
+xml_text_reader_error (void *arg, xmlErrorPtr xml_error)
+{
+ DBusError *error = arg;
+
+#if 0
+ _dbus_verbose ("XML_ERROR level=%d, domain=%d, code=%d, msg=%s\n",
+ xml_error->level, xml_error->domain,
+ xml_error->code, xml_error->message);
+#endif
+
+ if (!dbus_error_is_set (error))
+ {
+ if (xml_error->code == XML_ERR_NO_MEMORY)
+ _DBUS_SET_OOM (error);
+ else if (xml_error->level == XML_ERR_ERROR ||
+ xml_error->level == XML_ERR_FATAL)
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Error loading config file: '%s'",
+ xml_error->message);
+ }
+}
+
+
+BusConfigParser*
+bus_config_load (const DBusString *file,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent,
+ DBusError *error)
+
+{
+ xmlTextReader *reader;
+ BusConfigParser *parser;
+ DBusString dirname, data;
+ DBusError tmp_error;
+ int ret;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ parser = NULL;
+ reader = NULL;
+
+ if (!_dbus_string_init (&dirname))
+ {
+ _DBUS_SET_OOM (error);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&data))
+ {
+ _DBUS_SET_OOM (error);
+ _dbus_string_free (&dirname);
+ return NULL;
+ }
+
+ if (is_toplevel)
+ {
+ /* xmlMemSetup only fails if one of the functions is NULL */
+ xmlMemSetup (dbus_free,
+ dbus_malloc,
+ dbus_realloc,
+ _dbus_strdup);
+ xmlInitParser ();
+ xmlSetGenericErrorFunc (NULL, xml_shut_up);
+ }
+
+ if (!_dbus_string_get_dirname (file, &dirname))
+ {
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ parser = bus_config_parser_new (&dirname, is_toplevel, parent);
+ if (parser == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_file_get_contents (&data, file, error))
+ goto failed;
+
+ reader = xmlReaderForMemory (_dbus_string_get_const_data (&data),
+ _dbus_string_get_length (&data),
+ NULL, NULL, 0);
+ if (reader == NULL)
+ {
+ _DBUS_SET_OOM (error);
+ goto failed;
+ }
+
+ xmlTextReaderSetParserProp (reader, XML_PARSER_SUBST_ENTITIES, 1);
+
+ dbus_error_init (&tmp_error);
+ xmlTextReaderSetStructuredErrorHandler (reader, xml_text_reader_error, &tmp_error);
+
+ while ((ret = xmlTextReaderRead (reader)) == 1)
+ {
+ int type;
+
+ if (dbus_error_is_set (&tmp_error))
+ goto reader_out;
+
+ type = xmlTextReaderNodeType (reader);
+ if (type == -1)
+ {
+ _DBUS_MAYBE_SET_OOM (&tmp_error);
+ goto reader_out;
+ }
+
+ switch ((xmlReaderTypes) type) {
+ case XML_READER_TYPE_ELEMENT:
+ xml_text_start_element (parser, reader, &tmp_error);
+ break;
+
+ case XML_READER_TYPE_TEXT:
+ case XML_READER_TYPE_CDATA:
+ {
+ DBusString content;
+ const char *value;
+ value = xmlTextReaderConstValue (reader);
+ if (value != NULL)
+ {
+ _dbus_string_init_const (&content, value);
+ bus_config_parser_content (parser, &content, &tmp_error);
+ }
+ else
+ _DBUS_MAYBE_SET_OOM (&tmp_error);
+ break;
+ }
+
+ case XML_READER_TYPE_DOCUMENT_TYPE:
+ {
+ const char *name;
+ name = xmlTextReaderConstName (reader);
+ if (name != NULL)
+ bus_config_parser_check_doctype (parser, name, &tmp_error);
+ else
+ _DBUS_MAYBE_SET_OOM (&tmp_error);
+ break;
+ }
+
+ case XML_READER_TYPE_END_ELEMENT:
+ {
+ const char *name;
+ name = xmlTextReaderConstName (reader);
+ if (name != NULL)
+ bus_config_parser_end_element (parser, name, &tmp_error);
+ else
+ _DBUS_MAYBE_SET_OOM (&tmp_error);
+ break;
+ }
+
+ case XML_READER_TYPE_DOCUMENT:
+ case XML_READER_TYPE_DOCUMENT_FRAGMENT:
+ case XML_READER_TYPE_PROCESSING_INSTRUCTION:
+ case XML_READER_TYPE_COMMENT:
+ case XML_READER_TYPE_ENTITY:
+ case XML_READER_TYPE_NOTATION:
+ case XML_READER_TYPE_WHITESPACE:
+ case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+ case XML_READER_TYPE_END_ENTITY:
+ case XML_READER_TYPE_XML_DECLARATION:
+ /* nothing to do, just read on */
+ break;
+
+ case XML_READER_TYPE_NONE:
+ case XML_READER_TYPE_ATTRIBUTE:
+ case XML_READER_TYPE_ENTITY_REFERENCE:
+ _dbus_assert_not_reached ("unexpected nodes in XML");
+ }
+
+ if (dbus_error_is_set (&tmp_error))
+ goto reader_out;
+ }
+
+ if (ret == -1)
+ _DBUS_MAYBE_SET_OOM (&tmp_error);
+
+ reader_out:
+ xmlFreeTextReader (reader);
+ reader = NULL;
+ if (dbus_error_is_set (&tmp_error))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto failed;
+ }
+
+ if (!bus_config_parser_finished (parser, error))
+ goto failed;
+ _dbus_string_free (&dirname);
+ _dbus_string_free (&data);
+ if (is_toplevel)
+ xmlCleanupParser();
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return parser;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_free (&dirname);
+ _dbus_string_free (&data);
+ if (is_toplevel)
+ xmlCleanupParser();
+ if (parser)
+ bus_config_parser_unref (parser);
+ _dbus_assert (reader == NULL); /* must go to reader_out first */
+ return NULL;
+}
diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c
new file mode 100644
index 00000000..54a67468
--- /dev/null
+++ b/bus/config-parser-common.c
@@ -0,0 +1,183 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser-common.c Common defines and routines for config file parsing
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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 <dbus/dbus-internals.h>
+#include <string.h>
+
+#include "config-parser-common.h"
+#include "utils.h"
+
+ElementType
+bus_config_parser_element_name_to_type (const char *name)
+{
+ if (strcmp (name, "none") == 0)
+ {
+ return ELEMENT_NONE;
+ }
+ else if (strcmp (name, "busconfig") == 0)
+ {
+ return ELEMENT_BUSCONFIG;
+ }
+ else if (strcmp (name, "user") == 0)
+ {
+ return ELEMENT_USER;
+ }
+ else if (strcmp (name, "auth") == 0)
+ {
+ return ELEMENT_AUTH;
+ }
+ else if (strcmp (name, "type") == 0)
+ {
+ return ELEMENT_TYPE;
+ }
+ else if (strcmp (name, "fork") == 0)
+ {
+ return ELEMENT_FORK;
+ }
+ else if (strcmp (name, "pidfile") == 0)
+ {
+ return ELEMENT_PIDFILE;
+ }
+ else if (strcmp (name, "listen") == 0)
+ {
+ return ELEMENT_LISTEN;
+ }
+ else if (strcmp (name, "auth") == 0)
+ {
+ return ELEMENT_AUTH;
+ }
+ else if (strcmp (name, "allow") == 0)
+ {
+ return ELEMENT_ALLOW;
+ }
+ else if (strcmp (name, "deny") == 0)
+ {
+ return ELEMENT_DENY;
+ }
+ else if (strcmp (name, "servicehelper") == 0)
+ {
+ return ELEMENT_SERVICEHELPER;
+ }
+ else if (strcmp (name, "includedir") == 0)
+ {
+ return ELEMENT_INCLUDEDIR;
+ }
+ else if (strcmp (name, "standard_session_servicedirs") == 0)
+ {
+ return ELEMENT_STANDARD_SESSION_SERVICEDIRS;
+ }
+ else if (strcmp (name, "standard_system_servicedirs") == 0)
+ {
+ return ELEMENT_STANDARD_SYSTEM_SERVICEDIRS;
+ }
+ else if (strcmp (name, "servicedir") == 0)
+ {
+ return ELEMENT_SERVICEDIR;
+ }
+ else if (strcmp (name, "include") == 0)
+ {
+ return ELEMENT_INCLUDE;
+ }
+ else if (strcmp (name, "policy") == 0)
+ {
+ return ELEMENT_POLICY;
+ }
+ else if (strcmp (name, "limit") == 0)
+ {
+ return ELEMENT_LIMIT;
+ }
+ else if (strcmp (name, "selinux") == 0)
+ {
+ return ELEMENT_SELINUX;
+ }
+ else if (strcmp (name, "associate") == 0)
+ {
+ return ELEMENT_ASSOCIATE;
+ }
+ else if (strcmp (name, "syslog") == 0)
+ {
+ return ELEMENT_SYSLOG;
+ }
+ else if (strcmp (name, "keep_umask") == 0)
+ {
+ return ELEMENT_KEEP_UMASK;
+ }
+ return ELEMENT_NONE;
+}
+
+const char*
+bus_config_parser_element_type_to_name (ElementType type)
+{
+ switch (type)
+ {
+ case ELEMENT_NONE:
+ return NULL;
+ case ELEMENT_BUSCONFIG:
+ return "busconfig";
+ case ELEMENT_INCLUDE:
+ return "include";
+ case ELEMENT_USER:
+ return "user";
+ case ELEMENT_LISTEN:
+ return "listen";
+ case ELEMENT_AUTH:
+ return "auth";
+ case ELEMENT_POLICY:
+ return "policy";
+ case ELEMENT_LIMIT:
+ return "limit";
+ case ELEMENT_ALLOW:
+ return "allow";
+ case ELEMENT_DENY:
+ return "deny";
+ case ELEMENT_FORK:
+ return "fork";
+ case ELEMENT_PIDFILE:
+ return "pidfile";
+ case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
+ return "standard_session_servicedirs";
+ case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
+ return "standard_system_servicedirs";
+ case ELEMENT_SERVICEDIR:
+ return "servicedir";
+ case ELEMENT_SERVICEHELPER:
+ return "servicehelper";
+ case ELEMENT_INCLUDEDIR:
+ return "includedir";
+ case ELEMENT_TYPE:
+ return "type";
+ case ELEMENT_SELINUX:
+ return "selinux";
+ case ELEMENT_ASSOCIATE:
+ return "associate";
+ case ELEMENT_SYSLOG:
+ return "syslog";
+ case ELEMENT_KEEP_UMASK:
+ return "keep_umask";
+ }
+
+ _dbus_assert_not_reached ("bad element type");
+
+ return NULL;
+}
+
diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h
new file mode 100644
index 00000000..8451ce08
--- /dev/null
+++ b/bus/config-parser-common.h
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser-common.h Common defines and routines for config file parsing
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_CONFIG_PARSER_COMMON_H
+#define BUS_CONFIG_PARSER_COMMON_H
+
+#include <config.h>
+
+typedef enum
+{
+ ELEMENT_NONE,
+ ELEMENT_BUSCONFIG,
+ ELEMENT_INCLUDE,
+ ELEMENT_USER,
+ ELEMENT_LISTEN,
+ ELEMENT_AUTH,
+ ELEMENT_POLICY,
+ ELEMENT_LIMIT,
+ ELEMENT_ALLOW,
+ ELEMENT_DENY,
+ ELEMENT_FORK,
+ ELEMENT_PIDFILE,
+ ELEMENT_SERVICEDIR,
+ ELEMENT_SERVICEHELPER,
+ ELEMENT_INCLUDEDIR,
+ ELEMENT_TYPE,
+ ELEMENT_SELINUX,
+ ELEMENT_ASSOCIATE,
+ ELEMENT_STANDARD_SESSION_SERVICEDIRS,
+ ELEMENT_STANDARD_SYSTEM_SERVICEDIRS,
+ ELEMENT_SYSLOG,
+ ELEMENT_KEEP_UMASK
+} ElementType;
+
+ElementType bus_config_parser_element_name_to_type (const char *element_name);
+const char* bus_config_parser_element_type_to_name (ElementType type);
+
+#endif /* BUS_CONFIG_PARSER_COMMON_H */
+
diff --git a/bus/config-parser-trivial.c b/bus/config-parser-trivial.c
new file mode 100644
index 00000000..fd016a84
--- /dev/null
+++ b/bus/config-parser-trivial.c
@@ -0,0 +1,696 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser-trivial.c XML-library-agnostic configuration file parser
+ * Does not do includes or anything remotely complicated.
+ *
+ * Copyright (C) 2003, 2004, 2007 Red Hat, Inc.
+ *
+ * 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-parser-common.h"
+#include "config-parser-trivial.h"
+#include "utils.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-internals.h>
+#include <string.h>
+
+/**
+ * TRIVIAL parser for bus configuration file.
+ */
+struct BusConfigParser
+{
+ ElementType type;
+ DBusString user; /**< User the dbus-daemon runs as */
+ DBusString bus_type; /**< Message bus type */
+ DBusString service_helper; /**< Location of the setuid helper */
+ DBusList *service_dirs; /**< Directories to look for services in */
+};
+
+static dbus_bool_t
+service_dirs_find_dir (DBusList **service_dirs,
+ const char *dir)
+{
+ DBusList *link;
+
+ _dbus_assert (dir != NULL);
+
+ for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
+ {
+ const char *link_dir;
+
+ link_dir = (const char *)link->data;
+ if (strcmp (dir, link_dir) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+service_dirs_append_link_unique_or_free (DBusList **service_dirs,
+ DBusList *dir_link)
+{
+ if (!service_dirs_find_dir (service_dirs, dir_link->data))
+ {
+ _dbus_list_append_link (service_dirs, dir_link);
+ }
+ else
+ {
+ dbus_free (dir_link->data);
+ _dbus_list_free_link (dir_link);
+ }
+}
+
+BusConfigParser*
+bus_config_parser_new (const DBusString *basedir,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent)
+{
+ BusConfigParser *parser;
+
+ parser = dbus_new0 (BusConfigParser, 1);
+ if (parser == NULL)
+ goto failed;
+
+ parser->type = ELEMENT_NONE;
+
+ /* init the lists */
+ parser->service_dirs = NULL;
+
+ /* init the strings */
+ if (!_dbus_string_init (&parser->user))
+ goto failed_parser;
+ if (!_dbus_string_init (&parser->bus_type))
+ goto failed_type;
+ if (!_dbus_string_init (&parser->service_helper))
+ goto failed_helper;
+
+ /* woot! */
+ return parser;
+
+/* argh. we have do do this carefully because of OOM */
+failed_helper:
+ _dbus_string_free (&parser->bus_type);
+failed_type:
+ _dbus_string_free (&parser->user);
+failed_parser:
+ dbus_free (parser);
+failed:
+ return NULL;
+}
+
+void
+bus_config_parser_unref (BusConfigParser *parser)
+{
+ _dbus_string_free (&parser->user);
+ _dbus_string_free (&parser->service_helper);
+ _dbus_string_free (&parser->bus_type);
+
+ _dbus_list_foreach (&parser->service_dirs,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->service_dirs);
+
+ dbus_free (parser);
+}
+
+dbus_bool_t
+bus_config_parser_start_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ /* we don't do processing of attribute names, we don't need to */
+ parser->type = bus_config_parser_element_name_to_type (element_name);
+
+ switch (parser->type)
+ {
+ case ELEMENT_SERVICEHELPER:
+ case ELEMENT_USER:
+ case ELEMENT_TYPE:
+ /* content about to be handled */
+ break;
+
+ case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
+ {
+ DBusList *link;
+ DBusList *dirs;
+ dirs = NULL;
+
+ if (!_dbus_get_standard_system_servicedirs (&dirs))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
+ break;
+ }
+
+ default:
+ {
+ /* we really don't care about the others... */
+ _dbus_verbose (" START We dont care about '%s' type '%i'\n", element_name, parser->type);
+ break;
+ }
+ }
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_end_element (BusConfigParser *parser,
+ const char *element_name,
+ DBusError *error)
+{
+ /* we don't care */
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_content (BusConfigParser *parser,
+ const DBusString *content,
+ DBusError *error)
+{
+ DBusString content_sane;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&content_sane))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+ if (!_dbus_string_copy (content, 0, &content_sane, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+
+ /* rip out white space */
+ _dbus_string_chop_white (&content_sane);
+ if (_dbus_string_get_length (&content_sane) == 0)
+ {
+ /* optimise, there is no content */
+ retval = TRUE;
+ goto out_content;
+ }
+
+ switch (parser->type)
+ {
+ case ELEMENT_SERVICEDIR:
+ {
+ char *cpath;
+
+ /* copy the sane data into a char array */
+ if (!_dbus_string_copy_data(&content_sane, &cpath))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+
+ /* append the dynamic char string to service dirs */
+ if (!_dbus_list_append (&parser->service_dirs, cpath))
+ {
+ dbus_free (cpath);
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_SERVICEHELPER:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_USER:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE:
+ {
+ if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0))
+ {
+ BUS_SET_OOM (error);
+ goto out_content;
+ }
+ }
+ break;
+ default:
+ {
+ /* we don't care about the others... really */
+ _dbus_verbose (" CONTENTS We dont care '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type);
+ break;
+ }
+ }
+
+ /* woot! */
+ retval = TRUE;
+
+out_content:
+ _dbus_string_free (&content_sane);
+out:
+ return retval;
+}
+
+dbus_bool_t
+bus_config_parser_finished (BusConfigParser *parser,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_verbose ("finished scanning!\n");
+ return TRUE;
+}
+
+const char*
+bus_config_parser_get_user (BusConfigParser *parser)
+{
+ return _dbus_string_get_const_data (&parser->user);
+}
+
+const char*
+bus_config_parser_get_type (BusConfigParser *parser)
+{
+ return _dbus_string_get_const_data (&parser->bus_type);
+}
+
+DBusList**
+bus_config_parser_get_service_dirs (BusConfigParser *parser)
+{
+ return &parser->service_dirs;
+}
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdio.h>
+#include "test.h"
+
+typedef enum
+{
+ VALID,
+ INVALID,
+ UNKNOWN
+} Validity;
+
+static dbus_bool_t
+check_return_values (const DBusString *full_path)
+{
+ BusConfigParser *parser;
+ DBusError error;
+ dbus_bool_t retval;
+ const char *user;
+ const char *type;
+ DBusList **dirs;
+
+ dbus_error_init (&error);
+ retval = FALSE;
+
+ printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path));
+
+ parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ _dbus_verbose ("Failed to load valid file due to OOM\n");
+ goto finish;
+ }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+
+ /* check user return value is okay */
+ user = bus_config_parser_get_user (parser);
+ if (user == NULL)
+ {
+ _dbus_warn ("User was NULL!\n");
+ goto finish;
+ }
+#if 0
+ /* the username can be configured in configure.in so this test doesn't work */
+ if (strcmp (user, "dbus") != 0)
+ {
+ _dbus_warn ("User was invalid; '%s'!\n", user);
+ goto finish;
+ }
+ printf (" <user>dbus</user> OKAY!\n");
+#endif
+
+ /* check type return value is okay */
+ type = bus_config_parser_get_type (parser);
+ if (type == NULL)
+ {
+ _dbus_warn ("Type was NULL!\n");
+ goto finish;
+ }
+ if (strcmp (type, "system") != 0)
+ {
+ _dbus_warn ("Type was invalid; '%s'!\n", user);
+ goto finish;
+ }
+ printf (" <type>system</type> OKAY!\n");
+
+ /* check dirs return value is okay */
+ dirs = bus_config_parser_get_service_dirs (parser);
+ if (dirs == NULL)
+ {
+ _dbus_warn ("Service dirs are NULL!\n");
+ goto finish;
+ }
+ printf (" <standard_system_service_dirs/> OKAY!\n");
+ /* NOTE: We tested the specific return values in the config-parser tests */
+
+ /* woohoo! */
+ retval = TRUE;
+finish:
+ if (parser != NULL)
+ bus_config_parser_unref (parser);
+ dbus_error_free (&error);
+ return retval;
+}
+
+static dbus_bool_t
+do_load (const DBusString *full_path,
+ Validity validity,
+ dbus_bool_t oom_possible)
+{
+ BusConfigParser *parser;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (oom_possible &&
+ dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("Failed to load valid file due to OOM\n");
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ else if (validity == VALID)
+ {
+ _dbus_warn ("Failed to load valid file but still had memory: %s\n",
+ error.message);
+
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+
+ bus_config_parser_unref (parser);
+
+ if (validity == INVALID)
+ {
+ _dbus_warn ("Accepted invalid file\n");
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+typedef struct
+{
+ const DBusString *full_path;
+ Validity validity;
+} LoaderOomData;
+
+static dbus_bool_t
+check_loader_oom_func (void *data)
+{
+ LoaderOomData *d = data;
+
+ return do_load (d->full_path, d->validity, TRUE);
+}
+
+static dbus_bool_t
+process_test_valid_subdir (const DBusString *test_base_dir,
+ const char *subdir,
+ Validity validity)
+{
+ DBusString test_directory;
+ DBusString filename;
+ DBusDirIter *dir;
+ dbus_bool_t retval;
+ DBusError error;
+
+ retval = FALSE;
+ dir = NULL;
+
+ if (!_dbus_string_init (&test_directory))
+ _dbus_assert_not_reached ("didn't allocate test_directory\n");
+
+ _dbus_string_init_const (&filename, subdir);
+
+ if (!_dbus_string_copy (test_base_dir, 0,
+ &test_directory, 0))
+ _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+
+ if (!_dbus_concat_dir_and_file (&test_directory, &filename))
+ _dbus_assert_not_reached ("couldn't allocate full path");
+
+ _dbus_string_free (&filename);
+ if (!_dbus_string_init (&filename))
+ _dbus_assert_not_reached ("didn't allocate filename string\n");
+
+ dbus_error_init (&error);
+ dir = _dbus_directory_open (&test_directory, &error);
+ if (dir == NULL)
+ {
+ _dbus_warn ("Could not open %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ if (validity == VALID)
+ printf ("Testing valid files:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid files:\n");
+ else
+ printf ("Testing unknown files:\n");
+
+ next:
+ while (_dbus_directory_get_next_file (dir, &filename, &error))
+ {
+ DBusString full_path;
+ LoaderOomData d;
+
+ if (!_dbus_string_init (&full_path))
+ _dbus_assert_not_reached ("couldn't init string");
+
+ if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+ _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ _dbus_assert_not_reached ("couldn't concat file to dir");
+
+ if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
+ {
+ _dbus_verbose ("Skipping non-.conf file %s\n",
+ _dbus_string_get_const_data (&filename));
+ _dbus_string_free (&full_path);
+ goto next;
+ }
+
+ printf (" %s\n", _dbus_string_get_const_data (&filename));
+
+ _dbus_verbose (" expecting %s\n",
+ validity == VALID ? "valid" :
+ (validity == INVALID ? "invalid" :
+ (validity == UNKNOWN ? "unknown" : "???")));
+
+ d.full_path = &full_path;
+ d.validity = validity;
+
+ /* FIXME hackaround for an expat problem, see
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
+ * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
+ */
+ /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
+ if (!check_loader_oom_func (&d))
+ _dbus_assert_not_reached ("test failed");
+
+ _dbus_string_free (&full_path);
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ _dbus_warn ("Could not get next file in %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ retval = TRUE;
+
+ failed:
+
+ if (dir)
+ _dbus_directory_close (dir);
+ _dbus_string_free (&test_directory);
+ _dbus_string_free (&filename);
+
+ return retval;
+}
+
+/* convenience function, do not reuse outside of TEST */
+static dbus_bool_t
+make_full_path (const DBusString *test_data_dir,
+ const char *subdir,
+ const char *file,
+ DBusString *full_path)
+{
+ DBusString filename;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (full_path))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ if (!_dbus_string_copy (test_data_dir, 0, full_path, 0))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ _dbus_string_init_const (&filename, subdir);
+ if (!_dbus_concat_dir_and_file (full_path, &filename))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+ _dbus_string_free (&filename);
+
+ _dbus_string_init_const (&filename, file);
+ if (!_dbus_concat_dir_and_file (full_path, &filename))
+ {
+ _dbus_warn ("couldn't allocate full path");
+ goto finish;
+ }
+
+ /* woot! */
+ retval = TRUE;
+
+finish:
+ _dbus_string_free (&filename);
+ return retval;
+}
+
+static dbus_bool_t
+check_file_valid (DBusString *full_path,
+ Validity validity)
+{
+ dbus_bool_t retval;
+
+ if (validity == VALID)
+ printf ("Testing valid file:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid file:\n");
+ else
+ printf ("Testing unknown file:\n");
+
+ /* print the filename, just so we match the other output */
+ printf (" %s\n", _dbus_string_get_const_data (full_path));
+
+ /* only test one file */
+ retval = do_load (full_path, validity, TRUE);
+
+ return retval;
+}
+
+dbus_bool_t
+bus_config_parser_trivial_test (const DBusString *test_data_dir)
+{
+ DBusString full_path;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (test_data_dir == NULL ||
+ _dbus_string_get_length (test_data_dir) == 0)
+ {
+ printf ("No test data\n");
+ return TRUE;
+ }
+
+ /* We already test default_session_servicedirs and default_system_servicedirs
+ * in bus_config_parser_test() */
+ if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
+ goto finish;
+
+ /* we don't process all the invalid files, as the trivial parser can't hope
+ * to validate them all for all different syntaxes. We just check one broken
+ * file to see if junk is received */
+ if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
+ goto finish;
+ if (!check_file_valid (&full_path, INVALID))
+ goto finish;
+ _dbus_string_free (&full_path);
+
+ /* just test if the check_file_valid works okay and we got sane values */
+ if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path))
+ goto finish;
+ if (!check_file_valid (&full_path, VALID))
+ goto finish;
+ /* check to see if we got the correct values from the parser */
+ if (!check_return_values (&full_path))
+ goto finish;
+
+ /* woot! */
+ retval = TRUE;
+
+finish:
+ _dbus_string_free (&full_path);
+
+ /* we don't process equiv-config-files as we don't handle <include> */
+ return retval;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
diff --git a/bus/config-parser-trivial.h b/bus/config-parser-trivial.h
new file mode 100644
index 00000000..ce542bfb
--- /dev/null
+++ b/bus/config-parser-trivial.h
@@ -0,0 +1,71 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser-trivial.h XML-library-agnostic configuration file parser
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_CONFIG_PARSER_TRIVIAL_H
+#define BUS_CONFIG_PARSER_TRIVIAL_H
+
+#include <config.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
+
+/* Whatever XML library we're using just pushes data into this API */
+
+typedef struct BusConfigParser BusConfigParser;
+
+BusConfigParser* bus_config_parser_new (const DBusString *basedir,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent);
+
+BusConfigParser* bus_config_parser_ref (BusConfigParser *parser);
+void bus_config_parser_unref (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_start_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error);
+dbus_bool_t bus_config_parser_end_element (BusConfigParser *parser,
+ const char *element_name,
+ DBusError *error);
+dbus_bool_t bus_config_parser_content (BusConfigParser *parser,
+ const DBusString *content,
+ DBusError *error);
+dbus_bool_t bus_config_parser_finished (BusConfigParser *parser,
+ DBusError *error);
+
+/* Functions for extracting the parse results */
+const char* bus_config_parser_get_user (BusConfigParser *parser);
+const char* bus_config_parser_get_type (BusConfigParser *parser);
+DBusList** bus_config_parser_get_service_dirs (BusConfigParser *parser);
+
+/* Loader functions (backended off one of the XML parsers). Returns a
+ * finished ConfigParser.
+ */
+BusConfigParser* bus_config_load (const DBusString *file,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent,
+ DBusError *error);
+
+#endif /* BUS_CONFIG_PARSER_TRIVIAL_H */
diff --git a/bus/config-parser.c b/bus/config-parser.c
new file mode 100644
index 00000000..5218910b
--- /dev/null
+++ b/bus/config-parser.c
@@ -0,0 +1,3466 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser.c XML-library-agnostic configuration file parser
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * 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-parser-common.h"
+#include "config-parser.h"
+#include "test.h"
+#include "utils.h"
+#include "policy.h"
+#include "selinux.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-internals.h>
+#include <string.h>
+
+typedef enum
+{
+ /* we ignore policies for unknown groups/users */
+ POLICY_IGNORED,
+
+ /* non-ignored */
+ POLICY_DEFAULT,
+ POLICY_MANDATORY,
+ POLICY_USER,
+ POLICY_GROUP,
+ POLICY_CONSOLE
+} PolicyType;
+
+typedef struct
+{
+ ElementType type;
+
+ unsigned int had_content : 1;
+
+ union
+ {
+ struct
+ {
+ unsigned int ignore_missing : 1;
+ unsigned int if_selinux_enabled : 1;
+ unsigned int selinux_root_relative : 1;
+ } include;
+
+ struct
+ {
+ PolicyType type;
+ unsigned long gid_uid_or_at_console;
+ } policy;
+
+ struct
+ {
+ char *name;
+ long value;
+ } limit;
+
+ } d;
+
+} Element;
+
+/**
+ * Parser for bus configuration file.
+ */
+struct BusConfigParser
+{
+ int refcount; /**< Reference count */
+
+ DBusString basedir; /**< Directory we resolve paths relative to */
+
+ DBusList *stack; /**< stack of Element */
+
+ char *user; /**< user to run as */
+
+ char *servicehelper; /**< location of the setuid helper */
+
+ char *bus_type; /**< Message bus type */
+
+ DBusList *listen_on; /**< List of addresses to listen to */
+
+ DBusList *mechanisms; /**< Auth mechanisms */
+
+ DBusList *service_dirs; /**< Directories to look for session services in */
+
+ DBusList *conf_dirs; /**< Directories to look for policy configuration in */
+
+ BusPolicy *policy; /**< Security policy */
+
+ BusLimits limits; /**< Limits */
+
+ char *pidfile; /**< PID file */
+
+ DBusList *included_files; /**< Included files stack */
+
+ DBusHashTable *service_context_table; /**< Map service names to SELinux contexts */
+
+ unsigned int fork : 1; /**< TRUE to fork into daemon mode */
+
+ unsigned int syslog : 1; /**< TRUE to enable syslog */
+ unsigned int keep_umask : 1; /**< TRUE to keep original umask when forking */
+
+ unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
+};
+
+static Element*
+push_element (BusConfigParser *parser,
+ ElementType type)
+{
+ Element *e;
+
+ _dbus_assert (type != ELEMENT_NONE);
+
+ e = dbus_new0 (Element, 1);
+ if (e == NULL)
+ return NULL;
+
+ if (!_dbus_list_append (&parser->stack, e))
+ {
+ dbus_free (e);
+ return NULL;
+ }
+
+ e->type = type;
+
+ return e;
+}
+
+static void
+element_free (Element *e)
+{
+ if (e->type == ELEMENT_LIMIT)
+ dbus_free (e->d.limit.name);
+
+ dbus_free (e);
+}
+
+static void
+pop_element (BusConfigParser *parser)
+{
+ Element *e;
+
+ e = _dbus_list_pop_last (&parser->stack);
+
+ element_free (e);
+}
+
+static Element*
+peek_element (BusConfigParser *parser)
+{
+ Element *e;
+
+ e = _dbus_list_get_last (&parser->stack);
+
+ return e;
+}
+
+static ElementType
+top_element_type (BusConfigParser *parser)
+{
+ Element *e;
+
+ e = _dbus_list_get_last (&parser->stack);
+
+ if (e)
+ return e->type;
+ else
+ return ELEMENT_NONE;
+}
+
+static dbus_bool_t
+merge_service_context_hash (DBusHashTable *dest,
+ DBusHashTable *from)
+{
+ DBusHashIter iter;
+ char *service_copy;
+ char *context_copy;
+
+ service_copy = NULL;
+ context_copy = NULL;
+
+ _dbus_hash_iter_init (from, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ const char *service = _dbus_hash_iter_get_string_key (&iter);
+ const char *context = _dbus_hash_iter_get_value (&iter);
+
+ service_copy = _dbus_strdup (service);
+ if (service_copy == NULL)
+ goto fail;
+ context_copy = _dbus_strdup (context);
+ if (context_copy == NULL)
+ goto fail;
+
+ if (!_dbus_hash_table_insert_string (dest, service_copy, context_copy))
+ goto fail;
+
+ service_copy = NULL;
+ context_copy = NULL;
+ }
+
+ return TRUE;
+
+ fail:
+ if (service_copy)
+ dbus_free (service_copy);
+
+ if (context_copy)
+ dbus_free (context_copy);
+
+ return FALSE;
+}
+
+static dbus_bool_t
+service_dirs_find_dir (DBusList **service_dirs,
+ const char *dir)
+{
+ DBusList *link;
+
+ _dbus_assert (dir != NULL);
+
+ for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
+ {
+ const char *link_dir;
+
+ link_dir = (const char *)link->data;
+ if (strcmp (dir, link_dir) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static dbus_bool_t
+service_dirs_append_unique_or_free (DBusList **service_dirs,
+ char *dir)
+{
+ if (!service_dirs_find_dir (service_dirs, dir))
+ return _dbus_list_append (service_dirs, dir);
+
+ dbus_free (dir);
+ return TRUE;
+}
+
+static void
+service_dirs_append_link_unique_or_free (DBusList **service_dirs,
+ DBusList *dir_link)
+{
+ if (!service_dirs_find_dir (service_dirs, dir_link->data))
+ {
+ _dbus_list_append_link (service_dirs, dir_link);
+ }
+ else
+ {
+ dbus_free (dir_link->data);
+ _dbus_list_free_link (dir_link);
+ }
+}
+
+static dbus_bool_t
+merge_included (BusConfigParser *parser,
+ BusConfigParser *included,
+ DBusError *error)
+{
+ DBusList *link;
+
+ if (!bus_policy_merge (parser->policy,
+ included->policy))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!merge_service_context_hash (parser->service_context_table,
+ included->service_context_table))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (included->user != NULL)
+ {
+ dbus_free (parser->user);
+ parser->user = included->user;
+ included->user = NULL;
+ }
+
+ if (included->bus_type != NULL)
+ {
+ dbus_free (parser->bus_type);
+ parser->bus_type = included->bus_type;
+ included->bus_type = NULL;
+ }
+
+ if (included->fork)
+ parser->fork = TRUE;
+
+ if (included->keep_umask)
+ parser->keep_umask = TRUE;
+
+ if (included->pidfile != NULL)
+ {
+ dbus_free (parser->pidfile);
+ parser->pidfile = included->pidfile;
+ included->pidfile = NULL;
+ }
+
+ while ((link = _dbus_list_pop_first_link (&included->listen_on)))
+ _dbus_list_append_link (&parser->listen_on, link);
+
+ while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
+ _dbus_list_append_link (&parser->mechanisms, link);
+
+ while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
+ service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
+
+ while ((link = _dbus_list_pop_first_link (&included->conf_dirs)))
+ _dbus_list_append_link (&parser->conf_dirs, link);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+seen_include (BusConfigParser *parser,
+ const DBusString *file)
+{
+ DBusList *iter;
+
+ iter = parser->included_files;
+ while (iter != NULL)
+ {
+ if (! strcmp (_dbus_string_get_const_data (file), iter->data))
+ return TRUE;
+
+ iter = _dbus_list_get_next_link (&parser->included_files, iter);
+ }
+
+ return FALSE;
+}
+
+BusConfigParser*
+bus_config_parser_new (const DBusString *basedir,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent)
+{
+ BusConfigParser *parser;
+
+ parser = dbus_new0 (BusConfigParser, 1);
+ if (parser == NULL)
+ return NULL;
+
+ parser->is_toplevel = !!is_toplevel;
+
+ if (!_dbus_string_init (&parser->basedir))
+ {
+ dbus_free (parser);
+ return NULL;
+ }
+
+ if (((parser->policy = bus_policy_new ()) == NULL) ||
+ !_dbus_string_copy (basedir, 0, &parser->basedir, 0) ||
+ ((parser->service_context_table = _dbus_hash_table_new (DBUS_HASH_STRING,
+ dbus_free,
+ dbus_free)) == NULL))
+ {
+ if (parser->policy)
+ bus_policy_unref (parser->policy);
+
+ _dbus_string_free (&parser->basedir);
+
+ dbus_free (parser);
+ return NULL;
+ }
+
+ if (parent != NULL)
+ {
+ /* Initialize the parser's limits from the parent. */
+ parser->limits = parent->limits;
+
+ /* Use the parent's list of included_files to avoid
+ circular inclusions. */
+ parser->included_files = parent->included_files;
+ }
+ else
+ {
+
+ /* Make up some numbers! woot! */
+ parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 127;
+ parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 127;
+ parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32;
+
+ /* Making this long means the user has to wait longer for an error
+ * message if something screws up, but making it too short means
+ * they might see a false failure.
+ */
+ parser->limits.activation_timeout = 25000; /* 25 seconds */
+
+ /* Making this long risks making a DOS attack easier, but too short
+ * and legitimate auth will fail. If interactive auth (ask user for
+ * password) is allowed, then potentially it has to be quite long.
+ */
+ parser->limits.auth_timeout = 30000; /* 30 seconds */
+
+ parser->limits.max_incomplete_connections = 64;
+ parser->limits.max_connections_per_user = 256;
+
+ /* Note that max_completed_connections / max_connections_per_user
+ * is the number of users that would have to work together to
+ * DOS all the other users.
+ */
+ parser->limits.max_completed_connections = 2048;
+
+ parser->limits.max_pending_activations = 512;
+ parser->limits.max_services_per_connection = 512;
+
+ /* For this one, keep in mind that it isn't only the memory used
+ * by the match rules, but slowdown from linearly walking a big
+ * list of them. A client adding more than this is almost
+ * certainly a bad idea for that reason, and should change to a
+ * smaller number of wider-net match rules - getting every last
+ * message to the bus is probably better than having a thousand
+ * match rules.
+ */
+ parser->limits.max_match_rules_per_connection = 512;
+
+ parser->limits.reply_timeout = -1; /* never */
+
+ /* this is effectively a limit on message queue size for messages
+ * that require a reply
+ */
+ parser->limits.max_replies_per_connection = 1024*8;
+ }
+
+ parser->refcount = 1;
+
+ return parser;
+}
+
+BusConfigParser *
+bus_config_parser_ref (BusConfigParser *parser)
+{
+ _dbus_assert (parser->refcount > 0);
+
+ parser->refcount += 1;
+
+ return parser;
+}
+
+void
+bus_config_parser_unref (BusConfigParser *parser)
+{
+ _dbus_assert (parser->refcount > 0);
+
+ parser->refcount -= 1;
+
+ if (parser->refcount == 0)
+ {
+ while (parser->stack != NULL)
+ pop_element (parser);
+
+ dbus_free (parser->user);
+ dbus_free (parser->servicehelper);
+ dbus_free (parser->bus_type);
+ dbus_free (parser->pidfile);
+
+ _dbus_list_foreach (&parser->listen_on,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->listen_on);
+
+ _dbus_list_foreach (&parser->service_dirs,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->service_dirs);
+
+ _dbus_list_foreach (&parser->conf_dirs,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->conf_dirs);
+
+ _dbus_list_foreach (&parser->mechanisms,
+ (DBusForeachFunction) dbus_free,
+ NULL);
+
+ _dbus_list_clear (&parser->mechanisms);
+
+ _dbus_string_free (&parser->basedir);
+
+ if (parser->policy)
+ bus_policy_unref (parser->policy);
+
+ if (parser->service_context_table)
+ _dbus_hash_table_unref (parser->service_context_table);
+
+ dbus_free (parser);
+ }
+}
+
+dbus_bool_t
+bus_config_parser_check_doctype (BusConfigParser *parser,
+ const char *doctype,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (strcmp (doctype, "busconfig") != 0)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Configuration file has the wrong document type %s",
+ doctype);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+typedef struct
+{
+ const char *name;
+ const char **retloc;
+} LocateAttr;
+
+static dbus_bool_t
+locate_attributes (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error,
+ const char *first_attribute_name,
+ const char **first_attribute_retloc,
+ ...)
+{
+ va_list args;
+ const char *name;
+ const char **retloc;
+ int n_attrs;
+#define MAX_ATTRS 24
+ LocateAttr attrs[MAX_ATTRS];
+ dbus_bool_t retval;
+ int i;
+
+ _dbus_assert (first_attribute_name != NULL);
+ _dbus_assert (first_attribute_retloc != NULL);
+
+ retval = TRUE;
+
+ n_attrs = 1;
+ attrs[0].name = first_attribute_name;
+ attrs[0].retloc = first_attribute_retloc;
+ *first_attribute_retloc = NULL;
+
+ va_start (args, first_attribute_retloc);
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+
+ while (name != NULL)
+ {
+ _dbus_assert (retloc != NULL);
+ _dbus_assert (n_attrs < MAX_ATTRS);
+
+ attrs[n_attrs].name = name;
+ attrs[n_attrs].retloc = retloc;
+ n_attrs += 1;
+ *retloc = NULL;
+
+ name = va_arg (args, const char*);
+ retloc = va_arg (args, const char**);
+ }
+
+ va_end (args);
+
+ i = 0;
+ while (attribute_names[i])
+ {
+ int j;
+ dbus_bool_t found;
+
+ found = FALSE;
+ j = 0;
+ while (j < n_attrs)
+ {
+ if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+ {
+ retloc = attrs[j].retloc;
+
+ if (*retloc != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Attribute \"%s\" repeated twice on the same <%s> element",
+ attrs[j].name, element_name);
+ retval = FALSE;
+ goto out;
+ }
+
+ *retloc = attribute_values[i];
+ found = TRUE;
+ }
+
+ ++j;
+ }
+
+ if (!found)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Attribute \"%s\" is invalid on <%s> element in this context",
+ attribute_names[i], element_name);
+ retval = FALSE;
+ goto out;
+ }
+
+ ++i;
+ }
+
+ out:
+ return retval;
+}
+
+static dbus_bool_t
+check_no_attributes (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ if (attribute_names[0] != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Attribute \"%s\" is invalid on <%s> element in this context",
+ attribute_names[0], element_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+start_busconfig_child (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ ElementType element_type;
+
+ element_type = bus_config_parser_element_name_to_type (element_name);
+
+ if (element_type == ELEMENT_USER)
+ {
+ if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_USER) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_TYPE)
+ {
+ if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_TYPE) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_FORK)
+ {
+ if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_FORK) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ parser->fork = TRUE;
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_SYSLOG)
+ {
+ if (!check_no_attributes (parser, "syslog", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_SYSLOG) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ parser->syslog = TRUE;
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_KEEP_UMASK)
+ {
+ if (!check_no_attributes (parser, "keep_umask", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_KEEP_UMASK) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ parser->keep_umask = TRUE;
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_PIDFILE)
+ {
+ if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_PIDFILE) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_LISTEN)
+ {
+ if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_LISTEN) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_AUTH)
+ {
+ if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_AUTH) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_SERVICEHELPER)
+ {
+ if (!check_no_attributes (parser, "servicehelper", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_SERVICEHELPER) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_INCLUDEDIR)
+ {
+ if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_STANDARD_SESSION_SERVICEDIRS)
+ {
+ DBusList *link;
+ DBusList *dirs;
+ dirs = NULL;
+
+ if (!check_no_attributes (parser, "standard_session_servicedirs", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_STANDARD_SESSION_SERVICEDIRS) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_get_standard_session_servicedirs (&dirs))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_STANDARD_SYSTEM_SERVICEDIRS)
+ {
+ DBusList *link;
+ DBusList *dirs;
+ dirs = NULL;
+
+ if (!check_no_attributes (parser, "standard_system_servicedirs", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_STANDARD_SYSTEM_SERVICEDIRS) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_get_standard_system_servicedirs (&dirs))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_SERVICEDIR)
+ {
+ if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_INCLUDE)
+ {
+ Element *e;
+ const char *if_selinux_enabled;
+ const char *ignore_missing;
+ const char *selinux_root_relative;
+
+ if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ e->d.include.ignore_missing = FALSE;
+ e->d.include.if_selinux_enabled = FALSE;
+ e->d.include.selinux_root_relative = FALSE;
+
+ if (!locate_attributes (parser, "include",
+ attribute_names,
+ attribute_values,
+ error,
+ "ignore_missing", &ignore_missing,
+ "if_selinux_enabled", &if_selinux_enabled,
+ "selinux_root_relative", &selinux_root_relative,
+ NULL))
+ return FALSE;
+
+ if (ignore_missing != NULL)
+ {
+ if (strcmp (ignore_missing, "yes") == 0)
+ e->d.include.ignore_missing = TRUE;
+ else if (strcmp (ignore_missing, "no") == 0)
+ e->d.include.ignore_missing = FALSE;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "ignore_missing attribute must have value \"yes\" or \"no\"");
+ return FALSE;
+ }
+ }
+
+ if (if_selinux_enabled != NULL)
+ {
+ if (strcmp (if_selinux_enabled, "yes") == 0)
+ e->d.include.if_selinux_enabled = TRUE;
+ else if (strcmp (if_selinux_enabled, "no") == 0)
+ e->d.include.if_selinux_enabled = FALSE;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "if_selinux_enabled attribute must have value"
+ " \"yes\" or \"no\"");
+ return FALSE;
+ }
+ }
+
+ if (selinux_root_relative != NULL)
+ {
+ if (strcmp (selinux_root_relative, "yes") == 0)
+ e->d.include.selinux_root_relative = TRUE;
+ else if (strcmp (selinux_root_relative, "no") == 0)
+ e->d.include.selinux_root_relative = FALSE;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "selinux_root_relative attribute must have value"
+ " \"yes\" or \"no\"");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_POLICY)
+ {
+ Element *e;
+ const char *context;
+ const char *user;
+ const char *group;
+ const char *at_console;
+
+ if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ e->d.policy.type = POLICY_IGNORED;
+
+ if (!locate_attributes (parser, "policy",
+ attribute_names,
+ attribute_values,
+ error,
+ "context", &context,
+ "user", &user,
+ "group", &group,
+ "at_console", &at_console,
+ NULL))
+ return FALSE;
+
+ if (((context && user) ||
+ (context && group) ||
+ (context && at_console)) ||
+ ((user && group) ||
+ (user && at_console)) ||
+ (group && at_console) ||
+ !(context || user || group || at_console))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<policy> element must have exactly one of (context|user|group|at_console) attributes");
+ return FALSE;
+ }
+
+ if (context != NULL)
+ {
+ if (strcmp (context, "default") == 0)
+ {
+ e->d.policy.type = POLICY_DEFAULT;
+ }
+ else if (strcmp (context, "mandatory") == 0)
+ {
+ e->d.policy.type = POLICY_MANDATORY;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
+ context);
+ return FALSE;
+ }
+ }
+ else if (user != NULL)
+ {
+ DBusString username;
+ _dbus_string_init_const (&username, user);
+
+ if (_dbus_parse_unix_user_from_config (&username,
+ &e->d.policy.gid_uid_or_at_console))
+ e->d.policy.type = POLICY_USER;
+ else
+ _dbus_warn ("Unknown username \"%s\" in message bus configuration file\n",
+ user);
+ }
+ else if (group != NULL)
+ {
+ DBusString group_name;
+ _dbus_string_init_const (&group_name, group);
+
+ if (_dbus_parse_unix_group_from_config (&group_name,
+ &e->d.policy.gid_uid_or_at_console))
+ e->d.policy.type = POLICY_GROUP;
+ else
+ _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
+ group);
+ }
+ else if (at_console != NULL)
+ {
+ dbus_bool_t t;
+ t = (strcmp (at_console, "true") == 0);
+ if (t || strcmp (at_console, "false") == 0)
+ {
+ e->d.policy.gid_uid_or_at_console = t;
+ e->d.policy.type = POLICY_CONSOLE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Unknown value \"%s\" for at_console in message bus configuration file",
+ at_console);
+
+ return FALSE;
+ }
+ }
+ else
+ {
+ _dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_LIMIT)
+ {
+ Element *e;
+ const char *name;
+
+ if ((e = push_element (parser, ELEMENT_LIMIT)) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!locate_attributes (parser, "limit",
+ attribute_names,
+ attribute_values,
+ error,
+ "name", &name,
+ NULL))
+ return FALSE;
+
+ if (name == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<limit> element must have a \"name\" attribute");
+ return FALSE;
+ }
+
+ e->d.limit.name = _dbus_strdup (name);
+ if (e->d.limit.name == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (element_type == ELEMENT_SELINUX)
+ {
+ if (!check_no_attributes (parser, "selinux", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_SELINUX) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> not allowed inside <%s> in configuration file",
+ element_name, "busconfig");
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+append_rule_from_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ dbus_bool_t allow,
+ DBusError *error)
+{
+ const char *log;
+ const char *send_interface;
+ const char *send_member;
+ const char *send_error;
+ const char *send_destination;
+ const char *send_path;
+ const char *send_type;
+ const char *receive_interface;
+ const char *receive_member;
+ const char *receive_error;
+ const char *receive_sender;
+ const char *receive_path;
+ const char *receive_type;
+ const char *eavesdrop;
+ const char *send_requested_reply;
+ const char *receive_requested_reply;
+ const char *own;
+ const char *user;
+ const char *group;
+
+ BusPolicyRule *rule;
+
+ if (!locate_attributes (parser, element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ "send_interface", &send_interface,
+ "send_member", &send_member,
+ "send_error", &send_error,
+ "send_destination", &send_destination,
+ "send_path", &send_path,
+ "send_type", &send_type,
+ "receive_interface", &receive_interface,
+ "receive_member", &receive_member,
+ "receive_error", &receive_error,
+ "receive_sender", &receive_sender,
+ "receive_path", &receive_path,
+ "receive_type", &receive_type,
+ "eavesdrop", &eavesdrop,
+ "send_requested_reply", &send_requested_reply,
+ "receive_requested_reply", &receive_requested_reply,
+ "own", &own,
+ "user", &user,
+ "group", &group,
+ "log", &log,
+ NULL))
+ return FALSE;
+
+ if (!(send_interface || send_member || send_error || send_destination ||
+ send_type || send_path ||
+ receive_interface || receive_member || receive_error || receive_sender ||
+ receive_type || receive_path || eavesdrop ||
+ send_requested_reply || receive_requested_reply ||
+ own || user || group))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> must have one or more attributes",
+ element_name);
+ return FALSE;
+ }
+
+ if ((send_member && (send_interface == NULL && send_path == NULL)) ||
+ (receive_member && (receive_interface == NULL && receive_path == NULL)))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "On element <%s>, if you specify a member you must specify an interface or a path. Keep in mind that not all messages have an interface field.",
+ element_name);
+ return FALSE;
+ }
+
+ /* Allowed combinations of elements are:
+ *
+ * base, must be all send or all receive:
+ * nothing
+ * interface
+ * interface + member
+ * error
+ *
+ * base send_ can combine with send_destination, send_path, send_type, send_requested_reply
+ * base receive_ with receive_sender, receive_path, receive_type, receive_requested_reply, eavesdrop
+ *
+ * user, group, own must occur alone
+ *
+ * Pretty sure the below stuff is broken, FIXME think about it more.
+ */
+
+ if (((send_interface && send_error) ||
+ (send_interface && receive_interface) ||
+ (send_interface && receive_member) ||
+ (send_interface && receive_error) ||
+ (send_interface && receive_sender) ||
+ (send_interface && receive_requested_reply) ||
+ (send_interface && own) ||
+ (send_interface && user) ||
+ (send_interface && group)) ||
+
+ ((send_member && send_error) ||
+ (send_member && receive_interface) ||
+ (send_member && receive_member) ||
+ (send_member && receive_error) ||
+ (send_member && receive_sender) ||
+ (send_member && receive_requested_reply) ||
+ (send_member && own) ||
+ (send_member && user) ||
+ (send_member && group)) ||
+
+ ((send_error && receive_interface) ||
+ (send_error && receive_member) ||
+ (send_error && receive_error) ||
+ (send_error && receive_sender) ||
+ (send_error && receive_requested_reply) ||
+ (send_error && own) ||
+ (send_error && user) ||
+ (send_error && group)) ||
+
+ ((send_destination && receive_interface) ||
+ (send_destination && receive_member) ||
+ (send_destination && receive_error) ||
+ (send_destination && receive_sender) ||
+ (send_destination && receive_requested_reply) ||
+ (send_destination && own) ||
+ (send_destination && user) ||
+ (send_destination && group)) ||
+
+ ((send_type && receive_interface) ||
+ (send_type && receive_member) ||
+ (send_type && receive_error) ||
+ (send_type && receive_sender) ||
+ (send_type && receive_requested_reply) ||
+ (send_type && own) ||
+ (send_type && user) ||
+ (send_type && group)) ||
+
+ ((send_path && receive_interface) ||
+ (send_path && receive_member) ||
+ (send_path && receive_error) ||
+ (send_path && receive_sender) ||
+ (send_path && receive_requested_reply) ||
+ (send_path && own) ||
+ (send_path && user) ||
+ (send_path && group)) ||
+
+ ((send_requested_reply && receive_interface) ||
+ (send_requested_reply && receive_member) ||
+ (send_requested_reply && receive_error) ||
+ (send_requested_reply && receive_sender) ||
+ (send_requested_reply && receive_requested_reply) ||
+ (send_requested_reply && own) ||
+ (send_requested_reply && user) ||
+ (send_requested_reply && group)) ||
+
+ ((receive_interface && receive_error) ||
+ (receive_interface && own) ||
+ (receive_interface && user) ||
+ (receive_interface && group)) ||
+
+ ((receive_member && receive_error) ||
+ (receive_member && own) ||
+ (receive_member && user) ||
+ (receive_member && group)) ||
+
+ ((receive_error && own) ||
+ (receive_error && user) ||
+ (receive_error && group)) ||
+
+ ((eavesdrop && own) ||
+ (eavesdrop && user) ||
+ (eavesdrop && group)) ||
+
+ ((receive_requested_reply && own) ||
+ (receive_requested_reply && user) ||
+ (receive_requested_reply && group)) ||
+
+ ((own && user) ||
+ (own && group)) ||
+
+ ((user && group)))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Invalid combination of attributes on element <%s>",
+ element_name);
+ return FALSE;
+ }
+
+ rule = NULL;
+
+ /* In BusPolicyRule, NULL represents wildcard.
+ * In the config file, '*' represents it.
+ */
+#define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0')
+
+ if (send_interface || send_member || send_error || send_destination ||
+ send_path || send_type || send_requested_reply)
+ {
+ int message_type;
+
+ if (IS_WILDCARD (send_interface))
+ send_interface = NULL;
+ if (IS_WILDCARD (send_member))
+ send_member = NULL;
+ if (IS_WILDCARD (send_error))
+ send_error = NULL;
+ if (IS_WILDCARD (send_destination))
+ send_destination = NULL;
+ if (IS_WILDCARD (send_path))
+ send_path = NULL;
+ if (IS_WILDCARD (send_type))
+ send_type = NULL;
+
+ message_type = DBUS_MESSAGE_TYPE_INVALID;
+ if (send_type != NULL)
+ {
+ message_type = dbus_message_type_from_string (send_type);
+ if (message_type == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad message type \"%s\"",
+ send_type);
+ return FALSE;
+ }
+ }
+
+ if (eavesdrop &&
+ !(strcmp (eavesdrop, "true") == 0 ||
+ strcmp (eavesdrop, "false") == 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad value \"%s\" for %s attribute, must be true or false",
+ "eavesdrop", eavesdrop);
+ return FALSE;
+ }
+
+ if (send_requested_reply &&
+ !(strcmp (send_requested_reply, "true") == 0 ||
+ strcmp (send_requested_reply, "false") == 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad value \"%s\" for %s attribute, must be true or false",
+ "send_requested_reply", send_requested_reply);
+ return FALSE;
+ }
+
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (eavesdrop)
+ rule->d.send.eavesdrop = (strcmp (eavesdrop, "true") == 0);
+
+ if (log)
+ rule->d.send.log = (strcmp (log, "true") == 0);
+
+ if (send_requested_reply)
+ rule->d.send.requested_reply = (strcmp (send_requested_reply, "true") == 0);
+
+ rule->d.send.message_type = message_type;
+ rule->d.send.path = _dbus_strdup (send_path);
+ rule->d.send.interface = _dbus_strdup (send_interface);
+ rule->d.send.member = _dbus_strdup (send_member);
+ rule->d.send.error = _dbus_strdup (send_error);
+ rule->d.send.destination = _dbus_strdup (send_destination);
+ if (send_path && rule->d.send.path == NULL)
+ goto nomem;
+ if (send_interface && rule->d.send.interface == NULL)
+ goto nomem;
+ if (send_member && rule->d.send.member == NULL)
+ goto nomem;
+ if (send_error && rule->d.send.error == NULL)
+ goto nomem;
+ if (send_destination && rule->d.send.destination == NULL)
+ goto nomem;
+ }
+ else if (receive_interface || receive_member || receive_error || receive_sender ||
+ receive_path || receive_type || eavesdrop || receive_requested_reply)
+ {
+ int message_type;
+
+ if (IS_WILDCARD (receive_interface))
+ receive_interface = NULL;
+ if (IS_WILDCARD (receive_member))
+ receive_member = NULL;
+ if (IS_WILDCARD (receive_error))
+ receive_error = NULL;
+ if (IS_WILDCARD (receive_sender))
+ receive_sender = NULL;
+ if (IS_WILDCARD (receive_path))
+ receive_path = NULL;
+ if (IS_WILDCARD (receive_type))
+ receive_type = NULL;
+
+ message_type = DBUS_MESSAGE_TYPE_INVALID;
+ if (receive_type != NULL)
+ {
+ message_type = dbus_message_type_from_string (receive_type);
+ if (message_type == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad message type \"%s\"",
+ receive_type);
+ return FALSE;
+ }
+ }
+
+
+ if (eavesdrop &&
+ !(strcmp (eavesdrop, "true") == 0 ||
+ strcmp (eavesdrop, "false") == 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad value \"%s\" for %s attribute, must be true or false",
+ "eavesdrop", eavesdrop);
+ return FALSE;
+ }
+
+ if (receive_requested_reply &&
+ !(strcmp (receive_requested_reply, "true") == 0 ||
+ strcmp (receive_requested_reply, "false") == 0))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Bad value \"%s\" for %s attribute, must be true or false",
+ "receive_requested_reply", receive_requested_reply);
+ return FALSE;
+ }
+
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (eavesdrop)
+ rule->d.receive.eavesdrop = (strcmp (eavesdrop, "true") == 0);
+
+ if (receive_requested_reply)
+ rule->d.receive.requested_reply = (strcmp (receive_requested_reply, "true") == 0);
+
+ rule->d.receive.message_type = message_type;
+ rule->d.receive.path = _dbus_strdup (receive_path);
+ rule->d.receive.interface = _dbus_strdup (receive_interface);
+ rule->d.receive.member = _dbus_strdup (receive_member);
+ rule->d.receive.error = _dbus_strdup (receive_error);
+ rule->d.receive.origin = _dbus_strdup (receive_sender);
+
+ if (receive_path && rule->d.receive.path == NULL)
+ goto nomem;
+ if (receive_interface && rule->d.receive.interface == NULL)
+ goto nomem;
+ if (receive_member && rule->d.receive.member == NULL)
+ goto nomem;
+ if (receive_error && rule->d.receive.error == NULL)
+ goto nomem;
+ if (receive_sender && rule->d.receive.origin == NULL)
+ goto nomem;
+ }
+ else if (own)
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (IS_WILDCARD (own))
+ own = NULL;
+
+ rule->d.own.service_name = _dbus_strdup (own);
+ if (own && rule->d.own.service_name == NULL)
+ goto nomem;
+ }
+ else if (user)
+ {
+ if (IS_WILDCARD (user))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.user.uid = DBUS_UID_UNSET;
+ }
+ else
+ {
+ DBusString username;
+ dbus_uid_t uid;
+
+ _dbus_string_init_const (&username, user);
+
+ if (_dbus_parse_unix_user_from_config (&username, &uid))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.user.uid = uid;
+ }
+ else
+ {
+ _dbus_warn ("Unknown username \"%s\" on element <%s>\n",
+ user, element_name);
+ }
+ }
+ }
+ else if (group)
+ {
+ if (IS_WILDCARD (group))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.group.gid = DBUS_GID_UNSET;
+ }
+ else
+ {
+ DBusString groupname;
+ dbus_gid_t gid;
+
+ _dbus_string_init_const (&groupname, group);
+
+ if (_dbus_parse_unix_group_from_config (&groupname, &gid))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.group.gid = gid;
+ }
+ else
+ {
+ _dbus_warn ("Unknown group \"%s\" on element <%s>\n",
+ group, element_name);
+ }
+ }
+ }
+ else
+ _dbus_assert_not_reached ("Did not handle some combination of attributes on <allow> or <deny>");
+
+ if (rule != NULL)
+ {
+ Element *pe;
+
+ pe = peek_element (parser);
+ _dbus_assert (pe != NULL);
+ _dbus_assert (pe->type == ELEMENT_POLICY);
+
+ switch (pe->d.policy.type)
+ {
+ case POLICY_IGNORED:
+ /* drop the rule on the floor */
+ break;
+
+ case POLICY_DEFAULT:
+ if (!bus_policy_append_default_rule (parser->policy, rule))
+ goto nomem;
+ break;
+ case POLICY_MANDATORY:
+ if (!bus_policy_append_mandatory_rule (parser->policy, rule))
+ goto nomem;
+ break;
+ case POLICY_USER:
+ if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<%s> rule cannot be per-user because it has bus-global semantics",
+ element_name);
+ goto failed;
+ }
+
+ if (!bus_policy_append_user_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
+ rule))
+ goto nomem;
+ break;
+ case POLICY_GROUP:
+ if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<%s> rule cannot be per-group because it has bus-global semantics",
+ element_name);
+ goto failed;
+ }
+
+ if (!bus_policy_append_group_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
+ rule))
+ goto nomem;
+ break;
+
+
+ case POLICY_CONSOLE:
+ if (!bus_policy_append_console_rule (parser->policy, pe->d.policy.gid_uid_or_at_console,
+ rule))
+ goto nomem;
+ break;
+ }
+
+ bus_policy_rule_unref (rule);
+ rule = NULL;
+ }
+
+ return TRUE;
+
+ nomem:
+ BUS_SET_OOM (error);
+ failed:
+ if (rule)
+ bus_policy_rule_unref (rule);
+ return FALSE;
+}
+
+static dbus_bool_t
+start_policy_child (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ if (strcmp (element_name, "allow") == 0)
+ {
+ if (!append_rule_from_element (parser, element_name,
+ attribute_names, attribute_values,
+ TRUE, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_ALLOW) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else if (strcmp (element_name, "deny") == 0)
+ {
+ if (!append_rule_from_element (parser, element_name,
+ attribute_names, attribute_values,
+ FALSE, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_DENY) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> not allowed inside <%s> in configuration file",
+ element_name, "policy");
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+start_selinux_child (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ char *own_copy;
+ char *context_copy;
+
+ own_copy = NULL;
+ context_copy = NULL;
+
+ if (strcmp (element_name, "associate") == 0)
+ {
+ const char *own;
+ const char *context;
+
+ if (!locate_attributes (parser, "associate",
+ attribute_names,
+ attribute_values,
+ error,
+ "own", &own,
+ "context", &context,
+ NULL))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_ASSOCIATE) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (own == NULL || context == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <associate> must have attributes own=\"<servicename>\" and context=\"<selinux context>\"");
+ return FALSE;
+ }
+
+ own_copy = _dbus_strdup (own);
+ if (own_copy == NULL)
+ goto oom;
+ context_copy = _dbus_strdup (context);
+ if (context_copy == NULL)
+ goto oom;
+
+ if (!_dbus_hash_table_insert_string (parser->service_context_table,
+ own_copy, context_copy))
+ goto oom;
+
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> not allowed inside <%s> in configuration file",
+ element_name, "selinux");
+ return FALSE;
+ }
+
+ oom:
+ if (own_copy)
+ dbus_free (own_copy);
+
+ if (context_copy)
+ dbus_free (context_copy);
+
+ BUS_SET_OOM (error);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_config_parser_start_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error)
+{
+ ElementType t;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* printf ("START: %s\n", element_name); */
+
+ t = top_element_type (parser);
+
+ if (t == ELEMENT_NONE)
+ {
+ if (strcmp (element_name, "busconfig") == 0)
+ {
+ if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Unknown element <%s> at root of configuration file",
+ element_name);
+ return FALSE;
+ }
+ }
+ else if (t == ELEMENT_BUSCONFIG)
+ {
+ return start_busconfig_child (parser, element_name,
+ attribute_names, attribute_values,
+ error);
+ }
+ else if (t == ELEMENT_POLICY)
+ {
+ return start_policy_child (parser, element_name,
+ attribute_names, attribute_values,
+ error);
+ }
+ else if (t == ELEMENT_SELINUX)
+ {
+ return start_selinux_child (parser, element_name,
+ attribute_names, attribute_values,
+ error);
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> is not allowed in this context",
+ element_name);
+ return FALSE;
+ }
+}
+
+static dbus_bool_t
+set_limit (BusConfigParser *parser,
+ const char *name,
+ long value,
+ DBusError *error)
+{
+ dbus_bool_t must_be_positive;
+ dbus_bool_t must_be_int;
+
+ must_be_int = FALSE;
+ must_be_positive = FALSE;
+
+ if (strcmp (name, "max_incoming_bytes") == 0)
+ {
+ must_be_positive = TRUE;
+ parser->limits.max_incoming_bytes = value;
+ }
+ else if (strcmp (name, "max_outgoing_bytes") == 0)
+ {
+ must_be_positive = TRUE;
+ parser->limits.max_outgoing_bytes = value;
+ }
+ else if (strcmp (name, "max_message_size") == 0)
+ {
+ must_be_positive = TRUE;
+ parser->limits.max_message_size = value;
+ }
+ else if (strcmp (name, "service_start_timeout") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.activation_timeout = value;
+ }
+ else if (strcmp (name, "auth_timeout") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.auth_timeout = value;
+ }
+ else if (strcmp (name, "reply_timeout") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.reply_timeout = value;
+ }
+ else if (strcmp (name, "max_completed_connections") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_completed_connections = value;
+ }
+ else if (strcmp (name, "max_incomplete_connections") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_incomplete_connections = value;
+ }
+ else if (strcmp (name, "max_connections_per_user") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_connections_per_user = value;
+ }
+ else if (strcmp (name, "max_pending_service_starts") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_pending_activations = value;
+ }
+ else if (strcmp (name, "max_names_per_connection") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_services_per_connection = value;
+ }
+ else if (strcmp (name, "max_match_rules_per_connection") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_match_rules_per_connection = value;
+ }
+ else if (strcmp (name, "max_replies_per_connection") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_replies_per_connection = value;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "There is no limit called \"%s\"\n",
+ name);
+ return FALSE;
+ }
+
+ if (must_be_positive && value < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<limit name=\"%s\"> must be a positive number\n",
+ name);
+ return FALSE;
+ }
+
+ if (must_be_int &&
+ (value < _DBUS_INT_MIN || value > _DBUS_INT_MAX))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<limit name=\"%s\"> value is too large\n",
+ name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_end_element (BusConfigParser *parser,
+ const char *element_name,
+ DBusError *error)
+{
+ ElementType t;
+ const char *n;
+ Element *e;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* printf ("END: %s\n", element_name); */
+
+ t = top_element_type (parser);
+
+ if (t == ELEMENT_NONE)
+ {
+ /* should probably be an assertion failure but
+ * being paranoid about XML parsers
+ */
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "XML parser ended element with no element on the stack");
+ return FALSE;
+ }
+
+ n = bus_config_parser_element_type_to_name (t);
+ _dbus_assert (n != NULL);
+ if (strcmp (n, element_name) != 0)
+ {
+ /* should probably be an assertion failure but
+ * being paranoid about XML parsers
+ */
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "XML element <%s> ended but topmost element on the stack was <%s>",
+ element_name, n);
+ return FALSE;
+ }
+
+ e = peek_element (parser);
+ _dbus_assert (e != NULL);
+
+ switch (e->type)
+ {
+ case ELEMENT_NONE:
+ _dbus_assert_not_reached ("element in stack has no type");
+ break;
+
+ case ELEMENT_INCLUDE:
+ case ELEMENT_USER:
+ case ELEMENT_TYPE:
+ case ELEMENT_LISTEN:
+ case ELEMENT_PIDFILE:
+ case ELEMENT_AUTH:
+ case ELEMENT_SERVICEDIR:
+ case ELEMENT_SERVICEHELPER:
+ case ELEMENT_INCLUDEDIR:
+ case ELEMENT_LIMIT:
+ if (!e->had_content)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "XML element <%s> was expected to have content inside it",
+ bus_config_parser_element_type_to_name (e->type));
+ return FALSE;
+ }
+
+ if (e->type == ELEMENT_LIMIT)
+ {
+ if (!set_limit (parser, e->d.limit.name, e->d.limit.value,
+ error))
+ return FALSE;
+ }
+ break;
+
+ case ELEMENT_BUSCONFIG:
+ case ELEMENT_POLICY:
+ case ELEMENT_ALLOW:
+ case ELEMENT_DENY:
+ case ELEMENT_FORK:
+ case ELEMENT_SYSLOG:
+ case ELEMENT_KEEP_UMASK:
+ case ELEMENT_SELINUX:
+ case ELEMENT_ASSOCIATE:
+ case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
+ case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
+ break;
+ }
+
+ pop_element (parser);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+all_whitespace (const DBusString *str)
+{
+ int i;
+
+ _dbus_string_skip_white (str, 0, &i);
+
+ return i == _dbus_string_get_length (str);
+}
+
+static dbus_bool_t
+make_full_path (const DBusString *basedir,
+ const DBusString *filename,
+ DBusString *full_path)
+{
+ if (_dbus_path_is_absolute (filename))
+ {
+ return _dbus_string_copy (filename, 0, full_path, 0);
+ }
+ else
+ {
+ if (!_dbus_string_copy (basedir, 0, full_path, 0))
+ return FALSE;
+
+ if (!_dbus_concat_dir_and_file (full_path, filename))
+ return FALSE;
+
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+include_file (BusConfigParser *parser,
+ const DBusString *filename,
+ dbus_bool_t ignore_missing,
+ DBusError *error)
+{
+ /* FIXME good test case for this would load each config file in the
+ * test suite both alone, and as an include, and check
+ * that the result is the same
+ */
+ BusConfigParser *included;
+ const char *filename_str;
+ DBusError tmp_error;
+
+ dbus_error_init (&tmp_error);
+
+ filename_str = _dbus_string_get_const_data (filename);
+
+ /* Check to make sure this file hasn't already been included. */
+ if (seen_include (parser, filename))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Circular inclusion of file '%s'",
+ filename_str);
+ return FALSE;
+ }
+
+ if (! _dbus_list_append (&parser->included_files, (void *) filename_str))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* Since parser is passed in as the parent, included
+ inherits parser's limits. */
+ included = bus_config_load (filename, FALSE, parser, &tmp_error);
+
+ _dbus_list_pop_last (&parser->included_files);
+
+ if (included == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
+
+ if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
+ ignore_missing)
+ {
+ dbus_error_free (&tmp_error);
+ return TRUE;
+ }
+ else
+ {
+ dbus_move_error (&tmp_error, error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
+
+ if (!merge_included (parser, included, error))
+ {
+ bus_config_parser_unref (included);
+ return FALSE;
+ }
+
+ /* Copy included's limits back to parser. */
+ parser->limits = included->limits;
+
+ bus_config_parser_unref (included);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+servicehelper_path (BusConfigParser *parser,
+ const DBusString *filename,
+ DBusError *error)
+{
+ const char *filename_str;
+ char *servicehelper;
+
+ filename_str = _dbus_string_get_const_data (filename);
+
+ /* copy to avoid overwriting with NULL on OOM */
+ servicehelper = _dbus_strdup (filename_str);
+
+ /* check for OOM */
+ if (servicehelper == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* save the latest servicehelper only if not OOM */
+ dbus_free (parser->servicehelper);
+ parser->servicehelper = servicehelper;
+
+ /* We don't check whether the helper exists; instead we
+ * would just fail to ever activate anything if it doesn't.
+ * This allows an admin to fix the problem if it doesn't exist.
+ * It also allows the parser test suite to successfully parse
+ * test cases without installing the helper. ;-)
+ */
+
+ return TRUE;
+}
+
+static dbus_bool_t
+include_dir (BusConfigParser *parser,
+ const DBusString *dirname,
+ DBusError *error)
+{
+ DBusString filename;
+ dbus_bool_t retval;
+ DBusError tmp_error;
+ DBusDirIter *dir;
+ char *s;
+
+ if (!_dbus_string_init (&filename))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ retval = FALSE;
+
+ dir = _dbus_directory_open (dirname, error);
+
+ if (dir == NULL)
+ goto failed;
+
+ dbus_error_init (&tmp_error);
+ while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
+ {
+ DBusString full_path;
+
+ if (!_dbus_string_init (&full_path))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_string_copy (dirname, 0, &full_path, 0))
+ {
+ BUS_SET_OOM (error);
+ _dbus_string_free (&full_path);
+ goto failed;
+ }
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ BUS_SET_OOM (error);
+ _dbus_string_free (&full_path);
+ goto failed;
+ }
+
+ if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
+ {
+ if (!include_file (parser, &full_path, TRUE, error))
+ {
+ _dbus_string_free (&full_path);
+ goto failed;
+ }
+ }
+
+ _dbus_string_free (&full_path);
+ }
+
+ if (dbus_error_is_set (&tmp_error))
+ {
+ dbus_move_error (&tmp_error, error);
+ goto failed;
+ }
+
+
+ if (!_dbus_string_copy_data (dirname, &s))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_list_append (&parser->conf_dirs, s))
+ {
+ dbus_free (s);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ retval = TRUE;
+
+ failed:
+ _dbus_string_free (&filename);
+
+ if (dir)
+ _dbus_directory_close (dir);
+
+ return retval;
+}
+
+dbus_bool_t
+bus_config_parser_content (BusConfigParser *parser,
+ const DBusString *content,
+ DBusError *error)
+{
+ Element *e;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+#if 0
+ {
+ const char *c_str;
+
+ _dbus_string_get_const_data (content, &c_str);
+
+ printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
+ }
+#endif
+
+ e = peek_element (parser);
+ if (e == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Text content outside of any XML element in configuration file");
+ return FALSE;
+ }
+ else if (e->had_content)
+ {
+ _dbus_assert_not_reached ("Element had multiple content blocks");
+ return FALSE;
+ }
+
+ switch (top_element_type (parser))
+ {
+ case ELEMENT_NONE:
+ _dbus_assert_not_reached ("element at top of stack has no type");
+ return FALSE;
+
+ case ELEMENT_BUSCONFIG:
+ case ELEMENT_POLICY:
+ case ELEMENT_ALLOW:
+ case ELEMENT_DENY:
+ case ELEMENT_FORK:
+ case ELEMENT_SYSLOG:
+ case ELEMENT_KEEP_UMASK:
+ case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
+ case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
+ case ELEMENT_SELINUX:
+ case ELEMENT_ASSOCIATE:
+ if (all_whitespace (content))
+ return TRUE;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "No text content expected inside XML element %s in configuration file",
+ bus_config_parser_element_type_to_name (top_element_type (parser)));
+ return FALSE;
+ }
+
+ case ELEMENT_PIDFILE:
+ {
+ char *s;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_copy_data (content, &s))
+ goto nomem;
+
+ dbus_free (parser->pidfile);
+ parser->pidfile = s;
+ }
+ break;
+
+ case ELEMENT_INCLUDE:
+ {
+ DBusString full_path, selinux_policy_root;
+
+ e->had_content = TRUE;
+
+ if (e->d.include.if_selinux_enabled
+ && !bus_selinux_enabled ())
+ break;
+
+ if (!_dbus_string_init (&full_path))
+ goto nomem;
+
+ if (e->d.include.selinux_root_relative)
+ {
+ if (!bus_selinux_get_policy_root ())
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not determine SELinux policy root for relative inclusion");
+ _dbus_string_free (&full_path);
+ return FALSE;
+ }
+ _dbus_string_init_const (&selinux_policy_root,
+ bus_selinux_get_policy_root ());
+ if (!make_full_path (&selinux_policy_root, content, &full_path))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+ }
+ else if (!make_full_path (&parser->basedir, content, &full_path))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+
+ if (!include_file (parser, &full_path,
+ e->d.include.ignore_missing, error))
+ {
+ _dbus_string_free (&full_path);
+ return FALSE;
+ }
+
+ _dbus_string_free (&full_path);
+ }
+ break;
+
+ case ELEMENT_SERVICEHELPER:
+ {
+ DBusString full_path;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_init (&full_path))
+ goto nomem;
+
+ if (!make_full_path (&parser->basedir, content, &full_path))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+
+ if (!servicehelper_path (parser, &full_path, error))
+ {
+ _dbus_string_free (&full_path);
+ return FALSE;
+ }
+
+ _dbus_string_free (&full_path);
+ }
+ break;
+
+ case ELEMENT_INCLUDEDIR:
+ {
+ DBusString full_path;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_init (&full_path))
+ goto nomem;
+
+ if (!make_full_path (&parser->basedir, content, &full_path))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+
+ if (!include_dir (parser, &full_path, error))
+ {
+ _dbus_string_free (&full_path);
+ return FALSE;
+ }
+
+ _dbus_string_free (&full_path);
+ }
+ break;
+
+ case ELEMENT_USER:
+ {
+ char *s;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_copy_data (content, &s))
+ goto nomem;
+
+ dbus_free (parser->user);
+ parser->user = s;
+ }
+ break;
+
+ case ELEMENT_TYPE:
+ {
+ char *s;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_copy_data (content, &s))
+ goto nomem;
+
+ dbus_free (parser->bus_type);
+ parser->bus_type = s;
+ }
+ break;
+
+ case ELEMENT_LISTEN:
+ {
+ char *s;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_copy_data (content, &s))
+ goto nomem;
+
+ if (!_dbus_list_append (&parser->listen_on,
+ s))
+ {
+ dbus_free (s);
+ goto nomem;
+ }
+ }
+ break;
+
+ case ELEMENT_AUTH:
+ {
+ char *s;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_copy_data (content, &s))
+ goto nomem;
+
+ if (!_dbus_list_append (&parser->mechanisms,
+ s))
+ {
+ dbus_free (s);
+ goto nomem;
+ }
+ }
+ break;
+
+ case ELEMENT_SERVICEDIR:
+ {
+ char *s;
+ DBusString full_path;
+
+ e->had_content = TRUE;
+
+ if (!_dbus_string_init (&full_path))
+ goto nomem;
+
+ if (!make_full_path (&parser->basedir, content, &full_path))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+
+ if (!_dbus_string_copy_data (&full_path, &s))
+ {
+ _dbus_string_free (&full_path);
+ goto nomem;
+ }
+
+ /* _only_ extra session directories can be specified */
+ if (!service_dirs_append_unique_or_free (&parser->service_dirs, s))
+ {
+ _dbus_string_free (&full_path);
+ dbus_free (s);
+ goto nomem;
+ }
+
+ _dbus_string_free (&full_path);
+ }
+ break;
+
+ case ELEMENT_LIMIT:
+ {
+ long val;
+
+ e->had_content = TRUE;
+
+ val = 0;
+ if (!_dbus_string_parse_int (content, 0, &val, NULL))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<limit name=\"%s\"> element has invalid value (could not parse as integer)",
+ e->d.limit.name);
+ return FALSE;
+ }
+
+ e->d.limit.value = val;
+
+ _dbus_verbose ("Loaded value %ld for limit %s\n",
+ e->d.limit.value,
+ e->d.limit.name);
+ }
+ break;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return TRUE;
+
+ nomem:
+ BUS_SET_OOM (error);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_config_parser_finished (BusConfigParser *parser,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (parser->stack != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> was not closed in configuration file",
+ bus_config_parser_element_type_to_name (top_element_type (parser)));
+
+ return FALSE;
+ }
+
+ if (parser->is_toplevel && parser->listen_on == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Configuration file needs one or more <listen> elements giving addresses");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+const char*
+bus_config_parser_get_user (BusConfigParser *parser)
+{
+ return parser->user;
+}
+
+const char*
+bus_config_parser_get_type (BusConfigParser *parser)
+{
+ return parser->bus_type;
+}
+
+DBusList**
+bus_config_parser_get_addresses (BusConfigParser *parser)
+{
+ return &parser->listen_on;
+}
+
+DBusList**
+bus_config_parser_get_mechanisms (BusConfigParser *parser)
+{
+ return &parser->mechanisms;
+}
+
+DBusList**
+bus_config_parser_get_service_dirs (BusConfigParser *parser)
+{
+ return &parser->service_dirs;
+}
+
+DBusList**
+bus_config_parser_get_conf_dirs (BusConfigParser *parser)
+{
+ return &parser->conf_dirs;
+}
+
+dbus_bool_t
+bus_config_parser_get_fork (BusConfigParser *parser)
+{
+ return parser->fork;
+}
+
+dbus_bool_t
+bus_config_parser_get_syslog (BusConfigParser *parser)
+{
+ return parser->syslog;
+}
+
+dbus_bool_t
+bus_config_parser_get_keep_umask (BusConfigParser *parser)
+{
+ return parser->keep_umask;
+}
+
+const char *
+bus_config_parser_get_pidfile (BusConfigParser *parser)
+{
+ return parser->pidfile;
+}
+
+const char *
+bus_config_parser_get_servicehelper (BusConfigParser *parser)
+{
+ return parser->servicehelper;
+}
+
+BusPolicy*
+bus_config_parser_steal_policy (BusConfigParser *parser)
+{
+ BusPolicy *policy;
+
+ _dbus_assert (parser->policy != NULL); /* can only steal the policy 1 time */
+
+ policy = parser->policy;
+
+ parser->policy = NULL;
+
+ return policy;
+}
+
+/* Overwrite any limits that were set in the configuration file */
+void
+bus_config_parser_get_limits (BusConfigParser *parser,
+ BusLimits *limits)
+{
+ *limits = parser->limits;
+}
+
+DBusHashTable*
+bus_config_parser_steal_service_context_table (BusConfigParser *parser)
+{
+ DBusHashTable *table;
+
+ _dbus_assert (parser->service_context_table != NULL); /* can only steal once */
+
+ table = parser->service_context_table;
+
+ parser->service_context_table = NULL;
+
+ return table;
+}
+
+#ifdef DBUS_BUILD_TESTS
+#include <stdio.h>
+
+typedef enum
+{
+ VALID,
+ INVALID,
+ UNKNOWN
+} Validity;
+
+static dbus_bool_t
+do_load (const DBusString *full_path,
+ Validity validity,
+ dbus_bool_t oom_possible)
+{
+ BusConfigParser *parser;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (oom_possible &&
+ dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("Failed to load valid file due to OOM\n");
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ else if (validity == VALID)
+ {
+ _dbus_warn ("Failed to load valid file but still had memory: %s\n",
+ error.message);
+
+ dbus_error_free (&error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_error_free (&error);
+ return TRUE;
+ }
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+
+ bus_config_parser_unref (parser);
+
+ if (validity == INVALID)
+ {
+ _dbus_warn ("Accepted invalid file\n");
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+typedef struct
+{
+ const DBusString *full_path;
+ Validity validity;
+} LoaderOomData;
+
+static dbus_bool_t
+check_loader_oom_func (void *data)
+{
+ LoaderOomData *d = data;
+
+ return do_load (d->full_path, d->validity, TRUE);
+}
+
+static dbus_bool_t
+process_test_valid_subdir (const DBusString *test_base_dir,
+ const char *subdir,
+ Validity validity)
+{
+ DBusString test_directory;
+ DBusString filename;
+ DBusDirIter *dir;
+ dbus_bool_t retval;
+ DBusError error;
+
+ retval = FALSE;
+ dir = NULL;
+
+ if (!_dbus_string_init (&test_directory))
+ _dbus_assert_not_reached ("didn't allocate test_directory\n");
+
+ _dbus_string_init_const (&filename, subdir);
+
+ if (!_dbus_string_copy (test_base_dir, 0,
+ &test_directory, 0))
+ _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+
+ if (!_dbus_concat_dir_and_file (&test_directory, &filename))
+ _dbus_assert_not_reached ("couldn't allocate full path");
+
+ _dbus_string_free (&filename);
+ if (!_dbus_string_init (&filename))
+ _dbus_assert_not_reached ("didn't allocate filename string\n");
+
+ dbus_error_init (&error);
+ dir = _dbus_directory_open (&test_directory, &error);
+ if (dir == NULL)
+ {
+ _dbus_warn ("Could not open %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ if (validity == VALID)
+ printf ("Testing valid files:\n");
+ else if (validity == INVALID)
+ printf ("Testing invalid files:\n");
+ else
+ printf ("Testing unknown files:\n");
+
+ next:
+ while (_dbus_directory_get_next_file (dir, &filename, &error))
+ {
+ DBusString full_path;
+ LoaderOomData d;
+
+ if (!_dbus_string_init (&full_path))
+ _dbus_assert_not_reached ("couldn't init string");
+
+ if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+ _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ _dbus_assert_not_reached ("couldn't concat file to dir");
+
+ if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
+ {
+ _dbus_verbose ("Skipping non-.conf file %s\n",
+ _dbus_string_get_const_data (&filename));
+ _dbus_string_free (&full_path);
+ goto next;
+ }
+
+ printf (" %s\n", _dbus_string_get_const_data (&filename));
+
+ _dbus_verbose (" expecting %s\n",
+ validity == VALID ? "valid" :
+ (validity == INVALID ? "invalid" :
+ (validity == UNKNOWN ? "unknown" : "???")));
+
+ d.full_path = &full_path;
+ d.validity = validity;
+
+ /* FIXME hackaround for an expat problem, see
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
+ * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
+ */
+ /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
+ if (!check_loader_oom_func (&d))
+ _dbus_assert_not_reached ("test failed");
+
+ _dbus_string_free (&full_path);
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ _dbus_warn ("Could not get next file in %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto failed;
+ }
+
+ retval = TRUE;
+
+ failed:
+
+ if (dir)
+ _dbus_directory_close (dir);
+ _dbus_string_free (&test_directory);
+ _dbus_string_free (&filename);
+
+ return retval;
+}
+
+static dbus_bool_t
+bools_equal (dbus_bool_t a,
+ dbus_bool_t b)
+{
+ return a ? b : !b;
+}
+
+static dbus_bool_t
+strings_equal_or_both_null (const char *a,
+ const char *b)
+{
+ if (a == NULL || b == NULL)
+ return a == b;
+ else
+ return !strcmp (a, b);
+}
+
+static dbus_bool_t
+elements_equal (const Element *a,
+ const Element *b)
+{
+ if (a->type != b->type)
+ return FALSE;
+
+ if (!bools_equal (a->had_content, b->had_content))
+ return FALSE;
+
+ switch (a->type)
+ {
+
+ case ELEMENT_INCLUDE:
+ if (!bools_equal (a->d.include.ignore_missing,
+ b->d.include.ignore_missing))
+ return FALSE;
+ break;
+
+ case ELEMENT_POLICY:
+ if (a->d.policy.type != b->d.policy.type)
+ return FALSE;
+ if (a->d.policy.gid_uid_or_at_console != b->d.policy.gid_uid_or_at_console)
+ return FALSE;
+ break;
+
+ case ELEMENT_LIMIT:
+ if (strcmp (a->d.limit.name, b->d.limit.name))
+ return FALSE;
+ if (a->d.limit.value != b->d.limit.value)
+ return FALSE;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ return TRUE;
+
+}
+
+static dbus_bool_t
+lists_of_elements_equal (DBusList *a,
+ DBusList *b)
+{
+ DBusList *ia;
+ DBusList *ib;
+
+ ia = a;
+ ib = b;
+
+ while (ia != NULL && ib != NULL)
+ {
+ if (elements_equal (ia->data, ib->data))
+ return FALSE;
+ ia = _dbus_list_get_next_link (&a, ia);
+ ib = _dbus_list_get_next_link (&b, ib);
+ }
+
+ return ia == NULL && ib == NULL;
+}
+
+static dbus_bool_t
+lists_of_c_strings_equal (DBusList *a,
+ DBusList *b)
+{
+ DBusList *ia;
+ DBusList *ib;
+
+ ia = a;
+ ib = b;
+
+ while (ia != NULL && ib != NULL)
+ {
+ if (strcmp (ia->data, ib->data))
+ return FALSE;
+ ia = _dbus_list_get_next_link (&a, ia);
+ ib = _dbus_list_get_next_link (&b, ib);
+ }
+
+ return ia == NULL && ib == NULL;
+}
+
+static dbus_bool_t
+limits_equal (const BusLimits *a,
+ const BusLimits *b)
+{
+ return
+ (a->max_incoming_bytes == b->max_incoming_bytes
+ || a->max_outgoing_bytes == b->max_outgoing_bytes
+ || a->max_message_size == b->max_message_size
+ || a->activation_timeout == b->activation_timeout
+ || a->auth_timeout == b->auth_timeout
+ || a->max_completed_connections == b->max_completed_connections
+ || a->max_incomplete_connections == b->max_incomplete_connections
+ || a->max_connections_per_user == b->max_connections_per_user
+ || a->max_pending_activations == b->max_pending_activations
+ || a->max_services_per_connection == b->max_services_per_connection
+ || a->max_match_rules_per_connection == b->max_match_rules_per_connection
+ || a->max_replies_per_connection == b->max_replies_per_connection
+ || a->reply_timeout == b->reply_timeout);
+}
+
+static dbus_bool_t
+config_parsers_equal (const BusConfigParser *a,
+ const BusConfigParser *b)
+{
+ if (!_dbus_string_equal (&a->basedir, &b->basedir))
+ return FALSE;
+
+ if (!lists_of_elements_equal (a->stack, b->stack))
+ return FALSE;
+
+ if (!strings_equal_or_both_null (a->user, b->user))
+ return FALSE;
+
+ if (!lists_of_c_strings_equal (a->listen_on, b->listen_on))
+ return FALSE;
+
+ if (!lists_of_c_strings_equal (a->mechanisms, b->mechanisms))
+ return FALSE;
+
+ if (!lists_of_c_strings_equal (a->service_dirs, b->service_dirs))
+ return FALSE;
+
+ /* FIXME: compare policy */
+
+ /* FIXME: compare service selinux ID table */
+
+ if (! limits_equal (&a->limits, &b->limits))
+ return FALSE;
+
+ if (!strings_equal_or_both_null (a->pidfile, b->pidfile))
+ return FALSE;
+
+ if (! bools_equal (a->fork, b->fork))
+ return FALSE;
+
+ if (! bools_equal (a->keep_umask, b->keep_umask))
+ return FALSE;
+
+ if (! bools_equal (a->is_toplevel, b->is_toplevel))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+all_are_equiv (const DBusString *target_directory)
+{
+ DBusString filename;
+ DBusDirIter *dir;
+ BusConfigParser *first_parser;
+ BusConfigParser *parser;
+ DBusError error;
+ dbus_bool_t equal;
+ dbus_bool_t retval;
+
+ dir = NULL;
+ first_parser = NULL;
+ parser = NULL;
+ retval = FALSE;
+
+ if (!_dbus_string_init (&filename))
+ _dbus_assert_not_reached ("didn't allocate filename string");
+
+ dbus_error_init (&error);
+ dir = _dbus_directory_open (target_directory, &error);
+ if (dir == NULL)
+ {
+ _dbus_warn ("Could not open %s: %s\n",
+ _dbus_string_get_const_data (target_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto finished;
+ }
+
+ printf ("Comparing equivalent files:\n");
+
+ next:
+ while (_dbus_directory_get_next_file (dir, &filename, &error))
+ {
+ DBusString full_path;
+
+ if (!_dbus_string_init (&full_path))
+ _dbus_assert_not_reached ("couldn't init string");
+
+ if (!_dbus_string_copy (target_directory, 0, &full_path, 0))
+ _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ _dbus_assert_not_reached ("couldn't concat file to dir");
+
+ if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
+ {
+ _dbus_verbose ("Skipping non-.conf file %s\n",
+ _dbus_string_get_const_data (&filename));
+ _dbus_string_free (&full_path);
+ goto next;
+ }
+
+ printf (" %s\n", _dbus_string_get_const_data (&filename));
+
+ parser = bus_config_load (&full_path, TRUE, NULL, &error);
+
+ if (parser == NULL)
+ {
+ _dbus_warn ("Could not load file %s: %s\n",
+ _dbus_string_get_const_data (&full_path),
+ error.message);
+ _dbus_string_free (&full_path);
+ dbus_error_free (&error);
+ goto finished;
+ }
+ else if (first_parser == NULL)
+ {
+ _dbus_string_free (&full_path);
+ first_parser = parser;
+ }
+ else
+ {
+ _dbus_string_free (&full_path);
+ equal = config_parsers_equal (first_parser, parser);
+ bus_config_parser_unref (parser);
+ if (! equal)
+ goto finished;
+ }
+ }
+
+ retval = TRUE;
+
+ finished:
+ _dbus_string_free (&filename);
+ if (first_parser)
+ bus_config_parser_unref (first_parser);
+ if (dir)
+ _dbus_directory_close (dir);
+
+ return retval;
+
+}
+
+static dbus_bool_t
+process_test_equiv_subdir (const DBusString *test_base_dir,
+ const char *subdir)
+{
+ DBusString test_directory;
+ DBusString filename;
+ DBusDirIter *dir;
+ DBusError error;
+ dbus_bool_t equal;
+ dbus_bool_t retval;
+
+ dir = NULL;
+ retval = FALSE;
+
+ if (!_dbus_string_init (&test_directory))
+ _dbus_assert_not_reached ("didn't allocate test_directory");
+
+ _dbus_string_init_const (&filename, subdir);
+
+ if (!_dbus_string_copy (test_base_dir, 0,
+ &test_directory, 0))
+ _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
+
+ if (!_dbus_concat_dir_and_file (&test_directory, &filename))
+ _dbus_assert_not_reached ("couldn't allocate full path");
+
+ _dbus_string_free (&filename);
+ if (!_dbus_string_init (&filename))
+ _dbus_assert_not_reached ("didn't allocate filename string");
+
+ dbus_error_init (&error);
+ dir = _dbus_directory_open (&test_directory, &error);
+ if (dir == NULL)
+ {
+ _dbus_warn ("Could not open %s: %s\n",
+ _dbus_string_get_const_data (&test_directory),
+ error.message);
+ dbus_error_free (&error);
+ goto finished;
+ }
+
+ while (_dbus_directory_get_next_file (dir, &filename, &error))
+ {
+ DBusString full_path;
+
+ /* Skip CVS's magic directories! */
+ if (_dbus_string_equal_c_str (&filename, "CVS"))
+ continue;
+
+ if (!_dbus_string_init (&full_path))
+ _dbus_assert_not_reached ("couldn't init string");
+
+ if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
+ _dbus_assert_not_reached ("couldn't copy dir to full_path");
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ _dbus_assert_not_reached ("couldn't concat file to dir");
+
+ equal = all_are_equiv (&full_path);
+ _dbus_string_free (&full_path);
+
+ if (!equal)
+ goto finished;
+ }
+
+ retval = TRUE;
+
+ finished:
+ _dbus_string_free (&test_directory);
+ _dbus_string_free (&filename);
+ if (dir)
+ _dbus_directory_close (dir);
+
+ return retval;
+
+}
+
+static const char *test_session_service_dir_matches[] =
+ {
+#ifdef DBUS_UNIX
+ "/testusr/testlocal/testshare/dbus-1/services",
+ "/testusr/testshare/dbus-1/services",
+#endif
+ DBUS_DATADIR"/dbus-1/services",
+#ifdef DBUS_UNIX
+ "/testhome/foo/.testlocal/testshare/dbus-1/services",
+#endif
+ NULL
+ };
+
+static dbus_bool_t
+test_default_session_servicedirs (void)
+{
+ DBusList *dirs;
+ DBusList *link;
+ DBusString progs;
+ const char *common_progs;
+ int i;
+
+ /* On Unix we don't actually use this variable, but it's easier to handle the
+ * deallocation if we always allocate it, whether needed or not */
+ if (!_dbus_string_init (&progs))
+ _dbus_assert_not_reached ("OOM allocating progs");
+
+ common_progs = _dbus_getenv ("CommonProgramFiles");
+#ifndef DBUS_UNIX
+ if (common_progs)
+ {
+ if (!_dbus_string_append (&progs, common_progs))
+ {
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&progs, "/dbus-1/services"))
+ {
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+ test_session_service_dir_matches[1] = _dbus_string_get_const_data(&progs);
+ }
+#endif
+ dirs = NULL;
+
+ printf ("Testing retrieving the default session service directories\n");
+ if (!_dbus_get_standard_session_servicedirs (&dirs))
+ _dbus_assert_not_reached ("couldn't get stardard dirs");
+
+ /* make sure our defaults end with share/dbus-1/service */
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ {
+ DBusString path;
+
+ printf (" default service dir: %s\n", (char *)link->data);
+ _dbus_string_init_const (&path, (char *)link->data);
+ if (!_dbus_string_ends_with_c_str (&path, "dbus-1/services"))
+ {
+ printf ("error with default session service directories\n");
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ }
+
+#ifdef DBUS_UNIX
+ if (!_dbus_setenv ("XDG_DATA_HOME", "/testhome/foo/.testlocal/testshare"))
+ _dbus_assert_not_reached ("couldn't setenv XDG_DATA_HOME");
+
+ if (!_dbus_setenv ("XDG_DATA_DIRS", ":/testusr/testlocal/testshare: :/testusr/testshare:"))
+ _dbus_assert_not_reached ("couldn't setenv XDG_DATA_DIRS");
+#endif
+ if (!_dbus_get_standard_session_servicedirs (&dirs))
+ _dbus_assert_not_reached ("couldn't get stardard dirs");
+
+ /* make sure we read and parse the env variable correctly */
+ i = 0;
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ {
+ printf (" test service dir: %s\n", (char *)link->data);
+ if (test_session_service_dir_matches[i] == NULL)
+ {
+ printf ("more directories parsed than in match set\n");
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ if (strcmp (test_session_service_dir_matches[i],
+ (char *)link->data) != 0)
+ {
+ printf ("%s directory does not match %s in the match set\n",
+ (char *)link->data,
+ test_session_service_dir_matches[i]);
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ ++i;
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ }
+
+ if (test_session_service_dir_matches[i] != NULL)
+ {
+ printf ("extra data %s in the match set was not matched\n",
+ test_session_service_dir_matches[i]);
+
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ _dbus_string_free (&progs);
+ return TRUE;
+}
+
+static const char *test_system_service_dir_matches[] =
+ {
+#ifdef DBUS_UNIX
+ "/testusr/testlocal/testshare/dbus-1/system-services",
+ "/testusr/testshare/dbus-1/system-services",
+#endif
+ DBUS_DATADIR"/dbus-1/system-services",
+ NULL
+ };
+
+static dbus_bool_t
+test_default_system_servicedirs (void)
+{
+ DBusList *dirs;
+ DBusList *link;
+ DBusString progs;
+ const char *common_progs;
+ int i;
+
+ /* On Unix we don't actually use this variable, but it's easier to handle the
+ * deallocation if we always allocate it, whether needed or not */
+ if (!_dbus_string_init (&progs))
+ _dbus_assert_not_reached ("OOM allocating progs");
+
+ common_progs = _dbus_getenv ("CommonProgramFiles");
+#ifndef DBUS_UNIX
+ if (common_progs)
+ {
+ if (!_dbus_string_append (&progs, common_progs))
+ {
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ if (!_dbus_string_append (&progs, "/dbus-1/system-services"))
+ {
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+ test_system_service_dir_matches[1] = _dbus_string_get_const_data(&progs);
+ }
+#endif
+ dirs = NULL;
+
+ printf ("Testing retrieving the default system service directories\n");
+ if (!_dbus_get_standard_system_servicedirs (&dirs))
+ _dbus_assert_not_reached ("couldn't get stardard dirs");
+
+ /* make sure our defaults end with share/dbus-1/system-service */
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ {
+ DBusString path;
+
+ printf (" default service dir: %s\n", (char *)link->data);
+ _dbus_string_init_const (&path, (char *)link->data);
+ if (!_dbus_string_ends_with_c_str (&path, "dbus-1/system-services"))
+ {
+ printf ("error with default system service directories\n");
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ }
+
+#ifdef DBUS_UNIX
+ if (!_dbus_setenv ("XDG_DATA_HOME", "/testhome/foo/.testlocal/testshare"))
+ _dbus_assert_not_reached ("couldn't setenv XDG_DATA_HOME");
+
+ if (!_dbus_setenv ("XDG_DATA_DIRS", ":/testusr/testlocal/testshare: :/testusr/testshare:"))
+ _dbus_assert_not_reached ("couldn't setenv XDG_DATA_DIRS");
+#endif
+ if (!_dbus_get_standard_system_servicedirs (&dirs))
+ _dbus_assert_not_reached ("couldn't get stardard dirs");
+
+ /* make sure we read and parse the env variable correctly */
+ i = 0;
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ {
+ printf (" test service dir: %s\n", (char *)link->data);
+ if (test_system_service_dir_matches[i] == NULL)
+ {
+ printf ("more directories parsed than in match set\n");
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ if (strcmp (test_system_service_dir_matches[i],
+ (char *)link->data) != 0)
+ {
+ printf ("%s directory does not match %s in the match set\n",
+ (char *)link->data,
+ test_system_service_dir_matches[i]);
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ ++i;
+
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ }
+
+ if (test_system_service_dir_matches[i] != NULL)
+ {
+ printf ("extra data %s in the match set was not matched\n",
+ test_system_service_dir_matches[i]);
+
+ _dbus_string_free (&progs);
+ return FALSE;
+ }
+
+ _dbus_string_free (&progs);
+ return TRUE;
+}
+
+dbus_bool_t
+bus_config_parser_test (const DBusString *test_data_dir)
+{
+ if (test_data_dir == NULL ||
+ _dbus_string_get_length (test_data_dir) == 0)
+ {
+ printf ("No test data\n");
+ return TRUE;
+ }
+
+ if (!test_default_session_servicedirs())
+ return FALSE;
+
+ if (!test_default_system_servicedirs())
+ return FALSE;
+
+ if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
+ return FALSE;
+
+ if (!process_test_valid_subdir (test_data_dir, "invalid-config-files", INVALID))
+ return FALSE;
+
+ if (!process_test_equiv_subdir (test_data_dir, "equiv-config-files"))
+ return FALSE;
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
diff --git a/bus/config-parser.h b/bus/config-parser.h
new file mode 100644
index 00000000..3aac1ed3
--- /dev/null
+++ b/bus/config-parser.h
@@ -0,0 +1,89 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* config-parser.h XML-library-agnostic configuration file parser
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_CONFIG_PARSER_H
+#define BUS_CONFIG_PARSER_H
+
+#include <config.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
+#include "bus.h"
+
+/* Whatever XML library we're using just pushes data into this API */
+
+typedef struct BusConfigParser BusConfigParser;
+
+BusConfigParser* bus_config_parser_new (const DBusString *basedir,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent);
+
+BusConfigParser* bus_config_parser_ref (BusConfigParser *parser);
+void bus_config_parser_unref (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_check_doctype (BusConfigParser *parser,
+ const char *doctype,
+ DBusError *error);
+dbus_bool_t bus_config_parser_start_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ DBusError *error);
+dbus_bool_t bus_config_parser_end_element (BusConfigParser *parser,
+ const char *element_name,
+ DBusError *error);
+dbus_bool_t bus_config_parser_content (BusConfigParser *parser,
+ const DBusString *content,
+ DBusError *error);
+dbus_bool_t bus_config_parser_finished (BusConfigParser *parser,
+ DBusError *error);
+
+/* Functions for extracting the parse results */
+const char* bus_config_parser_get_user (BusConfigParser *parser);
+const char* bus_config_parser_get_type (BusConfigParser *parser);
+DBusList** bus_config_parser_get_addresses (BusConfigParser *parser);
+DBusList** bus_config_parser_get_mechanisms (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_get_fork (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_get_allow_anonymous (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_get_syslog (BusConfigParser *parser);
+dbus_bool_t bus_config_parser_get_keep_umask (BusConfigParser *parser);
+const char* bus_config_parser_get_pidfile (BusConfigParser *parser);
+const char* bus_config_parser_get_servicehelper (BusConfigParser *parser);
+DBusList** bus_config_parser_get_service_dirs (BusConfigParser *parser);
+DBusList** bus_config_parser_get_conf_dirs (BusConfigParser *parser);
+BusPolicy* bus_config_parser_steal_policy (BusConfigParser *parser);
+void bus_config_parser_get_limits (BusConfigParser *parser,
+ BusLimits *limits);
+
+DBusHashTable* bus_config_parser_steal_service_context_table (BusConfigParser *parser);
+
+/* Loader functions (backended off one of the XML parsers). Returns a
+ * finished ConfigParser.
+ */
+BusConfigParser* bus_config_load (const DBusString *file,
+ dbus_bool_t is_toplevel,
+ const BusConfigParser *parent,
+ DBusError *error);
+
+#endif /* BUS_CONFIG_PARSER_H */
diff --git a/bus/connection.c b/bus/connection.c
new file mode 100644
index 00000000..50807f1a
--- /dev/null
+++ b/bus/connection.c
@@ -0,0 +1,2303 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* connection.c Client connections
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "connection.h"
+#include "dispatch.h"
+#include "policy.h"
+#include "services.h"
+#include "utils.h"
+#include "signals.h"
+#include "expirelist.h"
+#include "selinux.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-timeout.h>
+
+/* Trim executed commands to this length; we want to keep logs readable */
+#define MAX_LOG_COMMAND_LEN 50
+
+static void bus_connection_remove_transactions (DBusConnection *connection);
+
+typedef struct
+{
+ BusExpireItem expire_item;
+
+ DBusConnection *will_get_reply;
+ DBusConnection *will_send_reply;
+
+ dbus_uint32_t reply_serial;
+
+} BusPendingReply;
+
+struct BusConnections
+{
+ int refcount;
+ DBusList *completed; /**< List of all completed connections */
+ int n_completed; /**< Length of completed list */
+ DBusList *incomplete; /**< List of all not-yet-active connections */
+ int n_incomplete; /**< Length of incomplete list */
+ BusContext *context;
+ DBusHashTable *completed_by_user; /**< Number of completed connections for each UID */
+ DBusTimeout *expire_timeout; /**< Timeout for expiring incomplete connections. */
+ int stamp; /**< Incrementing number */
+ BusExpireList *pending_replies; /**< List of pending replies */
+};
+
+static dbus_int32_t connection_data_slot = -1;
+
+typedef struct
+{
+ BusConnections *connections;
+ DBusList *link_in_connection_list;
+ DBusConnection *connection;
+ DBusList *services_owned;
+ int n_services_owned;
+ DBusList *match_rules;
+ int n_match_rules;
+ char *name;
+ DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */
+ DBusMessage *oom_message;
+ DBusPreallocatedSend *oom_preallocated;
+ BusClientPolicy *policy;
+
+ char *cached_loginfo_string;
+ BusSELinuxID *selinux_id;
+
+ long connection_tv_sec; /**< Time when we connected (seconds component) */
+ long connection_tv_usec; /**< Time when we connected (microsec component) */
+ int stamp; /**< connections->stamp last time we were traversed */
+} BusConnectionData;
+
+static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
+ DBusList *link,
+ void *data);
+
+static void bus_connection_drop_pending_replies (BusConnections *connections,
+ DBusConnection *connection);
+
+static dbus_bool_t expire_incomplete_timeout (void *data);
+
+#define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
+
+static DBusLoop*
+connection_get_loop (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ return bus_context_get_loop (d->connections->context);
+}
+
+
+static int
+get_connections_for_uid (BusConnections *connections,
+ dbus_uid_t uid)
+{
+ void *val;
+ int current_count;
+
+ /* val is NULL is 0 when it isn't in the hash yet */
+
+ val = _dbus_hash_table_lookup_ulong (connections->completed_by_user,
+ uid);
+
+ current_count = _DBUS_POINTER_TO_INT (val);
+
+ return current_count;
+}
+
+static dbus_bool_t
+adjust_connections_for_uid (BusConnections *connections,
+ dbus_uid_t uid,
+ int adjustment)
+{
+ int current_count;
+
+ current_count = get_connections_for_uid (connections, uid);
+
+ _dbus_verbose ("Adjusting connection count for UID " DBUS_UID_FORMAT
+ ": was %d adjustment %d making %d\n",
+ uid, current_count, adjustment, current_count + adjustment);
+
+ _dbus_assert (current_count >= 0);
+
+ current_count += adjustment;
+
+ _dbus_assert (current_count >= 0);
+
+ if (current_count == 0)
+ {
+ _dbus_hash_table_remove_ulong (connections->completed_by_user, uid);
+ return TRUE;
+ }
+ else
+ {
+ dbus_bool_t retval;
+
+ retval = _dbus_hash_table_insert_ulong (connections->completed_by_user,
+ uid, _DBUS_INT_TO_POINTER (current_count));
+
+ /* only positive adjustment can fail as otherwise
+ * a hash entry should already exist
+ */
+ _dbus_assert (adjustment > 0 ||
+ (adjustment <= 0 && retval));
+
+ return retval;
+ }
+}
+
+void
+bus_connection_disconnected (DBusConnection *connection)
+{
+ BusConnectionData *d;
+ BusService *service;
+ BusMatchmaker *matchmaker;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n",
+ d->name ? d->name : "(inactive)");
+
+ /* Delete our match rules */
+ if (d->n_match_rules > 0)
+ {
+ matchmaker = bus_context_get_matchmaker (d->connections->context);
+ bus_matchmaker_disconnected (matchmaker, connection);
+ }
+
+ /* Drop any service ownership. Unfortunately, this requires
+ * memory allocation and there doesn't seem to be a good way to
+ * handle it other than sleeping; we can't "fail" the operation of
+ * disconnecting a client, and preallocating a broadcast "service is
+ * now gone" message for every client-service pair seems kind of
+ * involved.
+ */
+ while ((service = _dbus_list_get_last (&d->services_owned)))
+ {
+ BusTransaction *transaction;
+ DBusError error;
+
+ retry:
+
+ dbus_error_init (&error);
+
+ while ((transaction = bus_transaction_new (d->connections->context)) == NULL)
+ _dbus_wait_for_memory ();
+
+ if (!bus_service_remove_owner (service, connection,
+ transaction, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ bus_transaction_cancel_and_free (transaction);
+ _dbus_wait_for_memory ();
+ goto retry;
+ }
+ else
+ {
+ _dbus_verbose ("Failed to remove service owner: %s %s\n",
+ error.name, error.message);
+ _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
+ }
+ }
+
+ bus_transaction_execute_and_free (transaction);
+ }
+
+ bus_dispatch_remove_connection (connection);
+
+ /* no more watching */
+ if (!dbus_connection_set_watch_functions (connection,
+ NULL, NULL, NULL,
+ connection,
+ NULL))
+ _dbus_assert_not_reached ("setting watch functions to NULL failed");
+
+ if (!dbus_connection_set_timeout_functions (connection,
+ NULL, NULL, NULL,
+ connection,
+ NULL))
+ _dbus_assert_not_reached ("setting timeout functions to NULL failed");
+
+ dbus_connection_set_unix_user_function (connection,
+ NULL, NULL, NULL);
+ dbus_connection_set_windows_user_function (connection,
+ NULL, NULL, NULL);
+
+ dbus_connection_set_dispatch_status_function (connection,
+ NULL, NULL, NULL);
+
+ bus_connection_remove_transactions (connection);
+
+ if (d->link_in_connection_list != NULL)
+ {
+ if (d->name != NULL)
+ {
+ unsigned long uid;
+
+ _dbus_list_remove_link (&d->connections->completed, d->link_in_connection_list);
+ d->link_in_connection_list = NULL;
+ d->connections->n_completed -= 1;
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!adjust_connections_for_uid (d->connections,
+ uid, -1))
+ _dbus_assert_not_reached ("adjusting downward should never fail");
+ }
+ }
+ else
+ {
+ _dbus_list_remove_link (&d->connections->incomplete, d->link_in_connection_list);
+ d->link_in_connection_list = NULL;
+ d->connections->n_incomplete -= 1;
+ }
+
+ _dbus_assert (d->connections->n_incomplete >= 0);
+ _dbus_assert (d->connections->n_completed >= 0);
+ }
+
+ bus_connection_drop_pending_replies (d->connections, connection);
+
+ /* frees "d" as side effect */
+ dbus_connection_set_data (connection,
+ connection_data_slot,
+ NULL, NULL);
+
+ dbus_connection_unref (connection);
+}
+
+static dbus_bool_t
+connection_watch_callback (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ /* FIXME this can be done in dbus-mainloop.c
+ * if the code in activation.c for the babysitter
+ * watch handler is fixed.
+ */
+
+#if 0
+ _dbus_verbose ("Calling handle_watch\n");
+#endif
+ return dbus_watch_handle (watch, condition);
+}
+
+static dbus_bool_t
+add_connection_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ return _dbus_loop_add_watch (connection_get_loop (connection),
+ watch, connection_watch_callback, connection,
+ NULL);
+}
+
+static void
+remove_connection_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ _dbus_loop_remove_watch (connection_get_loop (connection),
+ watch, connection_watch_callback, connection);
+}
+
+static void
+connection_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ /* DBusConnection *connection = data; */
+
+ /* can return FALSE on OOM but we just let it fire again later */
+ dbus_timeout_handle (timeout);
+}
+
+static dbus_bool_t
+add_connection_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ return _dbus_loop_add_timeout (connection_get_loop (connection),
+ timeout, connection_timeout_callback, connection, NULL);
+}
+
+static void
+remove_connection_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ _dbus_loop_remove_timeout (connection_get_loop (connection),
+ timeout, connection_timeout_callback, connection);
+}
+
+static void
+dispatch_status_function (DBusConnection *connection,
+ DBusDispatchStatus new_status,
+ void *data)
+{
+ DBusLoop *loop = data;
+
+ if (new_status != DBUS_DISPATCH_COMPLETE)
+ {
+ while (!_dbus_loop_queue_dispatch (loop, connection))
+ _dbus_wait_for_memory ();
+ }
+}
+
+static dbus_bool_t
+allow_unix_user_function (DBusConnection *connection,
+ unsigned long uid,
+ void *data)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return bus_context_allow_unix_user (d->connections->context, uid);
+}
+
+static void
+free_connection_data (void *data)
+{
+ BusConnectionData *d = data;
+
+ /* services_owned should be NULL since we should be disconnected */
+ _dbus_assert (d->services_owned == NULL);
+ _dbus_assert (d->n_services_owned == 0);
+ /* similarly */
+ _dbus_assert (d->transaction_messages == NULL);
+
+ if (d->oom_preallocated)
+ dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
+
+ if (d->oom_message)
+ dbus_message_unref (d->oom_message);
+
+ if (d->policy)
+ bus_client_policy_unref (d->policy);
+
+ if (d->selinux_id)
+ bus_selinux_id_unref (d->selinux_id);
+
+ dbus_free (d->cached_loginfo_string);
+
+ dbus_free (d->name);
+
+ dbus_free (d);
+}
+
+static void
+call_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ /* can return FALSE on OOM but we just let it fire again later */
+ dbus_timeout_handle (timeout);
+}
+
+BusConnections*
+bus_connections_new (BusContext *context)
+{
+ BusConnections *connections;
+
+ if (!dbus_connection_allocate_data_slot (&connection_data_slot))
+ goto failed_0;
+
+ connections = dbus_new0 (BusConnections, 1);
+ if (connections == NULL)
+ goto failed_1;
+
+ connections->completed_by_user = _dbus_hash_table_new (DBUS_HASH_ULONG,
+ NULL, NULL);
+ if (connections->completed_by_user == NULL)
+ goto failed_2;
+
+ connections->expire_timeout = _dbus_timeout_new (100, /* irrelevant */
+ expire_incomplete_timeout,
+ connections, NULL);
+ if (connections->expire_timeout == NULL)
+ goto failed_3;
+
+ _dbus_timeout_set_enabled (connections->expire_timeout, FALSE);
+
+ connections->pending_replies = bus_expire_list_new (bus_context_get_loop (context),
+ bus_context_get_reply_timeout (context),
+ bus_pending_reply_expired,
+ connections);
+ if (connections->pending_replies == NULL)
+ goto failed_4;
+
+ if (!_dbus_loop_add_timeout (bus_context_get_loop (context),
+ connections->expire_timeout,
+ call_timeout_callback, NULL, NULL))
+ goto failed_5;
+
+ connections->refcount = 1;
+ connections->context = context;
+
+ return connections;
+
+ failed_5:
+ bus_expire_list_free (connections->pending_replies);
+ failed_4:
+ _dbus_timeout_unref (connections->expire_timeout);
+ failed_3:
+ _dbus_hash_table_unref (connections->completed_by_user);
+ failed_2:
+ dbus_free (connections);
+ failed_1:
+ dbus_connection_free_data_slot (&connection_data_slot);
+ failed_0:
+ return NULL;
+}
+
+BusConnections *
+bus_connections_ref (BusConnections *connections)
+{
+ _dbus_assert (connections->refcount > 0);
+ connections->refcount += 1;
+
+ return connections;
+}
+
+void
+bus_connections_unref (BusConnections *connections)
+{
+ _dbus_assert (connections->refcount > 0);
+ connections->refcount -= 1;
+ if (connections->refcount == 0)
+ {
+ /* drop all incomplete */
+ while (connections->incomplete != NULL)
+ {
+ DBusConnection *connection;
+
+ connection = connections->incomplete->data;
+
+ dbus_connection_ref (connection);
+ dbus_connection_close (connection);
+ bus_connection_disconnected (connection);
+ dbus_connection_unref (connection);
+ }
+
+ _dbus_assert (connections->n_incomplete == 0);
+
+ /* drop all real connections */
+ while (connections->completed != NULL)
+ {
+ DBusConnection *connection;
+
+ connection = connections->completed->data;
+
+ dbus_connection_ref (connection);
+ dbus_connection_close (connection);
+ bus_connection_disconnected (connection);
+ dbus_connection_unref (connection);
+ }
+
+ _dbus_assert (connections->n_completed == 0);
+
+ bus_expire_list_free (connections->pending_replies);
+
+ _dbus_loop_remove_timeout (bus_context_get_loop (connections->context),
+ connections->expire_timeout,
+ call_timeout_callback, NULL);
+
+ _dbus_timeout_unref (connections->expire_timeout);
+
+ _dbus_hash_table_unref (connections->completed_by_user);
+
+ dbus_free (connections);
+
+ dbus_connection_free_data_slot (&connection_data_slot);
+ }
+}
+
+/* Used for logging */
+static dbus_bool_t
+cache_peer_loginfo_string (BusConnectionData *d,
+ DBusConnection *connection)
+{
+ DBusString loginfo_buf;
+ unsigned long uid;
+ unsigned long pid;
+ char *windows_sid;
+ dbus_bool_t prev_added;
+
+ if (!_dbus_string_init (&loginfo_buf))
+ return FALSE;
+
+ prev_added = FALSE;
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!_dbus_string_append_printf (&loginfo_buf, "uid=%ld", uid))
+ goto oom;
+ else
+ prev_added = TRUE;
+ }
+
+ if (dbus_connection_get_unix_process_id (connection, &pid))
+ {
+ if (prev_added)
+ {
+ if (!_dbus_string_append_byte (&loginfo_buf, ' '))
+ goto oom;
+ }
+ if (!_dbus_string_append_printf (&loginfo_buf, "pid=%ld comm=\"", pid))
+ goto oom;
+ /* Ignore errors here; we may not have permissions to read the
+ * proc file. */
+ _dbus_command_for_pid (pid, &loginfo_buf, MAX_LOG_COMMAND_LEN, NULL);
+ if (!_dbus_string_append_byte (&loginfo_buf, '"'))
+ goto oom;
+ }
+
+ if (dbus_connection_get_windows_user (connection, &windows_sid))
+ {
+ if (!_dbus_string_append_printf (&loginfo_buf, "sid=\"%s\" ", windows_sid))
+ goto oom;
+ dbus_free (windows_sid);
+ }
+
+ if (!_dbus_string_steal_data (&loginfo_buf, &(d->cached_loginfo_string)))
+ goto oom;
+
+ _dbus_string_free (&loginfo_buf);
+
+ return TRUE;
+oom:
+ _dbus_string_free (&loginfo_buf);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_connections_setup_connection (BusConnections *connections,
+ DBusConnection *connection)
+{
+
+ BusConnectionData *d;
+ dbus_bool_t retval;
+ DBusError error;
+
+
+ d = dbus_new0 (BusConnectionData, 1);
+
+ if (d == NULL)
+ return FALSE;
+
+ d->connections = connections;
+ d->connection = connection;
+
+ _dbus_get_current_time (&d->connection_tv_sec,
+ &d->connection_tv_usec);
+
+ _dbus_assert (connection_data_slot >= 0);
+
+ if (!dbus_connection_set_data (connection,
+ connection_data_slot,
+ d, free_connection_data))
+ {
+ dbus_free (d);
+ return FALSE;
+ }
+
+ dbus_connection_set_route_peer_messages (connection, TRUE);
+
+ retval = FALSE;
+
+ dbus_error_init (&error);
+ d->selinux_id = bus_selinux_init_connection_id (connection,
+ &error);
+ if (dbus_error_is_set (&error))
+ {
+ /* This is a bit bogus because we pretend all errors
+ * are OOM; this is done because we know that in bus.c
+ * an OOM error disconnects the connection, which is
+ * the same thing we want on any other error.
+ */
+ dbus_error_free (&error);
+ goto out;
+ }
+
+ if (!dbus_connection_set_watch_functions (connection,
+ add_connection_watch,
+ remove_connection_watch,
+ NULL,
+ connection,
+ NULL))
+ goto out;
+
+ if (!dbus_connection_set_timeout_functions (connection,
+ add_connection_timeout,
+ remove_connection_timeout,
+ NULL,
+ connection, NULL))
+ goto out;
+
+ /* For now we don't need to set a Windows user function because
+ * there are no policies in the config file controlling what
+ * Windows users can connect. The default 'same user that owns the
+ * bus can connect' behavior of DBusConnection is fine on Windows.
+ */
+ dbus_connection_set_unix_user_function (connection,
+ allow_unix_user_function,
+ NULL, NULL);
+
+ dbus_connection_set_dispatch_status_function (connection,
+ dispatch_status_function,
+ bus_context_get_loop (connections->context),
+ NULL);
+
+ d->link_in_connection_list = _dbus_list_alloc_link (connection);
+ if (d->link_in_connection_list == NULL)
+ goto out;
+
+ /* Setup the connection with the dispatcher */
+ if (!bus_dispatch_add_connection (connection))
+ goto out;
+
+ if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
+ {
+ if (!_dbus_loop_queue_dispatch (bus_context_get_loop (connections->context), connection))
+ {
+ bus_dispatch_remove_connection (connection);
+ goto out;
+ }
+ }
+
+ _dbus_list_append_link (&connections->incomplete, d->link_in_connection_list);
+ connections->n_incomplete += 1;
+
+ dbus_connection_ref (connection);
+
+ /* Note that we might disconnect ourselves here, but it only takes
+ * effect on return to the main loop. We call this to free up
+ * expired connections if possible, and to queue the timeout for our
+ * own expiration.
+ */
+ bus_connections_expire_incomplete (connections);
+
+ /* And we might also disconnect ourselves here, but again it
+ * only takes effect on return to main loop.
+ */
+ if (connections->n_incomplete >
+ bus_context_get_max_incomplete_connections (connections->context))
+ {
+ _dbus_verbose ("Number of incomplete connections exceeds max, dropping oldest one\n");
+
+ _dbus_assert (connections->incomplete != NULL);
+ /* Disconnect the oldest unauthenticated connection. FIXME
+ * would it be more secure to drop a *random* connection? This
+ * algorithm seems to mean that if someone can create new
+ * connections quickly enough, they can keep anyone else from
+ * completing authentication. But random may or may not really
+ * help with that, a more elaborate solution might be required.
+ */
+ dbus_connection_close (connections->incomplete->data);
+ }
+
+ retval = TRUE;
+
+ out:
+ if (!retval)
+ {
+ if (d->selinux_id)
+ bus_selinux_id_unref (d->selinux_id);
+ d->selinux_id = NULL;
+
+ if (!dbus_connection_set_watch_functions (connection,
+ NULL, NULL, NULL,
+ connection,
+ NULL))
+ _dbus_assert_not_reached ("setting watch functions to NULL failed");
+
+ if (!dbus_connection_set_timeout_functions (connection,
+ NULL, NULL, NULL,
+ connection,
+ NULL))
+ _dbus_assert_not_reached ("setting timeout functions to NULL failed");
+
+ dbus_connection_set_unix_user_function (connection,
+ NULL, NULL, NULL);
+
+ dbus_connection_set_windows_user_function (connection,
+ NULL, NULL, NULL);
+
+ dbus_connection_set_dispatch_status_function (connection,
+ NULL, NULL, NULL);
+
+ if (d->link_in_connection_list != NULL)
+ {
+ _dbus_assert (d->link_in_connection_list->next == NULL);
+ _dbus_assert (d->link_in_connection_list->prev == NULL);
+ _dbus_list_free_link (d->link_in_connection_list);
+ d->link_in_connection_list = NULL;
+ }
+
+ if (!dbus_connection_set_data (connection,
+ connection_data_slot,
+ NULL, NULL))
+ _dbus_assert_not_reached ("failed to set connection data to null");
+
+ /* "d" has now been freed */
+ }
+
+ return retval;
+}
+
+void
+bus_connections_expire_incomplete (BusConnections *connections)
+{
+ int next_interval;
+
+ next_interval = -1;
+
+ if (connections->incomplete != NULL)
+ {
+ long tv_sec, tv_usec;
+ DBusList *link;
+ int auth_timeout;
+
+ _dbus_get_current_time (&tv_sec, &tv_usec);
+ auth_timeout = bus_context_get_auth_timeout (connections->context);
+
+ link = _dbus_list_get_first_link (&connections->incomplete);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&connections->incomplete, link);
+ DBusConnection *connection;
+ BusConnectionData *d;
+ double elapsed;
+
+ connection = link->data;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ elapsed = ELAPSED_MILLISECONDS_SINCE (d->connection_tv_sec,
+ d->connection_tv_usec,
+ tv_sec, tv_usec);
+
+ if (elapsed >= (double) auth_timeout)
+ {
+ _dbus_verbose ("Timing out authentication for connection %p\n", connection);
+ dbus_connection_close (connection);
+ }
+ else
+ {
+ /* We can end the loop, since the connections are in oldest-first order */
+ next_interval = ((double)auth_timeout) - elapsed;
+ _dbus_verbose ("Connection %p authentication expires in %d milliseconds\n",
+ connection, next_interval);
+
+ break;
+ }
+
+ link = next;
+ }
+ }
+
+ bus_expire_timeout_set_interval (connections->expire_timeout,
+ next_interval);
+}
+
+static dbus_bool_t
+expire_incomplete_timeout (void *data)
+{
+ BusConnections *connections = data;
+
+ _dbus_verbose ("Running %s\n", _DBUS_FUNCTION_NAME);
+
+ /* note that this may remove the timeout */
+ bus_connections_expire_incomplete (connections);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_connection_get_unix_groups (DBusConnection *connection,
+ unsigned long **groups,
+ int *n_groups,
+ DBusError *error)
+{
+ BusConnectionData *d;
+ unsigned long uid;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ *groups = NULL;
+ *n_groups = 0;
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!_dbus_unix_groups_from_uid (uid, groups, n_groups))
+ {
+ _dbus_verbose ("Did not get any groups for UID %lu\n",
+ uid);
+ return FALSE;
+ }
+ else
+ {
+ _dbus_verbose ("Got %d groups for UID %lu\n",
+ *n_groups, uid);
+ return TRUE;
+ }
+ }
+ else
+ return TRUE; /* successfully got 0 groups */
+}
+
+dbus_bool_t
+bus_connection_is_in_unix_group (DBusConnection *connection,
+ unsigned long gid)
+{
+ int i;
+ unsigned long *group_ids;
+ int n_group_ids;
+
+ if (!bus_connection_get_unix_groups (connection, &group_ids, &n_group_ids,
+ NULL))
+ return FALSE;
+
+ i = 0;
+ while (i < n_group_ids)
+ {
+ if (group_ids[i] == gid)
+ {
+ dbus_free (group_ids);
+ return TRUE;
+ }
+ ++i;
+ }
+
+ dbus_free (group_ids);
+ return FALSE;
+}
+
+const char *
+bus_connection_get_loginfo (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ if (!bus_connection_is_active (connection))
+ return "inactive";
+ return d->cached_loginfo_string;
+}
+
+BusClientPolicy*
+bus_connection_get_policy (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+ _dbus_assert (d->policy != NULL);
+
+ return d->policy;
+}
+
+static dbus_bool_t
+foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&connections->completed);
+ while (link != NULL)
+ {
+ DBusConnection *connection = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connections->completed, link);
+
+ if (!(* function) (connection, data))
+ return FALSE;
+
+ link = next;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+foreach_inactive (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&connections->incomplete);
+ while (link != NULL)
+ {
+ DBusConnection *connection = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connections->incomplete, link);
+
+ if (!(* function) (connection, data))
+ return FALSE;
+
+ link = next;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Calls function on each active connection; if the function returns
+ * #FALSE, stops iterating. Active connections are authenticated
+ * and have sent a Hello message.
+ *
+ * @param connections the connections object
+ * @param function the function
+ * @param data data to pass to it as a second arg
+ */
+void
+bus_connections_foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ foreach_active (connections, function, data);
+}
+
+/**
+ * Calls function on each connection; if the function returns
+ * #FALSE, stops iterating.
+ *
+ * @param connections the connections object
+ * @param function the function
+ * @param data data to pass to it as a second arg
+ */
+void
+bus_connections_foreach (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ if (!foreach_active (connections, function, data))
+ return;
+
+ foreach_inactive (connections, function, data);
+}
+
+BusContext*
+bus_connections_get_context (BusConnections *connections)
+{
+ return connections->context;
+}
+
+/*
+ * This is used to avoid covering the same connection twice when
+ * traversing connections. Note that it assumes we will
+ * bus_connection_mark_stamp() each connection at least once per
+ * INT_MAX increments of the global stamp, or wraparound would break
+ * things.
+ */
+void
+bus_connections_increment_stamp (BusConnections *connections)
+{
+ connections->stamp += 1;
+}
+
+/* Mark connection with current stamp, return TRUE if it
+ * didn't already have that stamp
+ */
+dbus_bool_t
+bus_connection_mark_stamp (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ if (d->stamp == d->connections->stamp)
+ return FALSE;
+ else
+ {
+ d->stamp = d->connections->stamp;
+ return TRUE;
+ }
+}
+
+BusContext*
+bus_connection_get_context (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return d->connections->context;
+}
+
+BusConnections*
+bus_connection_get_connections (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return d->connections;
+}
+
+BusRegistry*
+bus_connection_get_registry (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return bus_context_get_registry (d->connections->context);
+}
+
+BusActivation*
+bus_connection_get_activation (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return bus_context_get_activation (d->connections->context);
+}
+
+BusMatchmaker*
+bus_connection_get_matchmaker (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return bus_context_get_matchmaker (d->connections->context);
+}
+
+BusSELinuxID*
+bus_connection_get_selinux_id (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ return d->selinux_id;
+}
+
+/**
+ * Checks whether the connection is registered with the message bus.
+ *
+ * @param connection the connection
+ * @returns #TRUE if we're an active message bus participant
+ */
+dbus_bool_t
+bus_connection_is_active (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ return d != NULL && d->name != NULL;
+}
+
+dbus_bool_t
+bus_connection_preallocate_oom_error (DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusPreallocatedSend *preallocated;
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ if (d->oom_preallocated != NULL)
+ return TRUE;
+
+ preallocated = dbus_connection_preallocate_send (connection);
+ if (preallocated == NULL)
+ return FALSE;
+
+ message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+
+ if (message == NULL)
+ {
+ dbus_connection_free_preallocated_send (connection, preallocated);
+ return FALSE;
+ }
+
+ /* d->name may be NULL, but that is OK */
+ if (!dbus_message_set_error_name (message, DBUS_ERROR_NO_MEMORY) ||
+ !dbus_message_set_destination (message, d->name) ||
+ !dbus_message_set_sender (message,
+ DBUS_SERVICE_DBUS))
+ {
+ dbus_connection_free_preallocated_send (connection, preallocated);
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ /* set reply serial to placeholder value just so space is already allocated
+ * for it.
+ */
+ if (!dbus_message_set_reply_serial (message, 14))
+ {
+ dbus_connection_free_preallocated_send (connection, preallocated);
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ d->oom_message = message;
+ d->oom_preallocated = preallocated;
+
+ return TRUE;
+}
+
+void
+bus_connection_send_oom_error (DBusConnection *connection,
+ DBusMessage *in_reply_to)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+ _dbus_assert (d->oom_message != NULL);
+
+ /* should always succeed since we set it to a placeholder earlier */
+ if (!dbus_message_set_reply_serial (d->oom_message,
+ dbus_message_get_serial (in_reply_to)))
+ _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
+
+ _dbus_assert (dbus_message_get_sender (d->oom_message) != NULL);
+
+ dbus_connection_send_preallocated (connection, d->oom_preallocated,
+ d->oom_message, NULL);
+
+ dbus_message_unref (d->oom_message);
+ d->oom_message = NULL;
+ d->oom_preallocated = NULL;
+}
+
+void
+bus_connection_add_match_rule_link (DBusConnection *connection,
+ DBusList *link)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ _dbus_list_append_link (&d->match_rules, link);
+
+ d->n_match_rules += 1;
+}
+
+dbus_bool_t
+bus_connection_add_match_rule (DBusConnection *connection,
+ BusMatchRule *rule)
+{
+ DBusList *link;
+
+ link = _dbus_list_alloc_link (rule);
+
+ if (link == NULL)
+ return FALSE;
+
+ bus_connection_add_match_rule_link (connection, link);
+
+ return TRUE;
+}
+
+void
+bus_connection_remove_match_rule (DBusConnection *connection,
+ BusMatchRule *rule)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ _dbus_list_remove_last (&d->match_rules, rule);
+
+ d->n_match_rules -= 1;
+ _dbus_assert (d->n_match_rules >= 0);
+}
+
+int
+bus_connection_get_n_match_rules (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ return d->n_match_rules;
+}
+
+void
+bus_connection_add_owned_service_link (DBusConnection *connection,
+ DBusList *link)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ _dbus_list_append_link (&d->services_owned, link);
+
+ d->n_services_owned += 1;
+}
+
+dbus_bool_t
+bus_connection_add_owned_service (DBusConnection *connection,
+ BusService *service)
+{
+ DBusList *link;
+
+ link = _dbus_list_alloc_link (service);
+
+ if (link == NULL)
+ return FALSE;
+
+ bus_connection_add_owned_service_link (connection, link);
+
+ return TRUE;
+}
+
+void
+bus_connection_remove_owned_service (DBusConnection *connection,
+ BusService *service)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ _dbus_list_remove_last (&d->services_owned, service);
+
+ d->n_services_owned -= 1;
+ _dbus_assert (d->n_services_owned >= 0);
+}
+
+int
+bus_connection_get_n_services_owned (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ return d->n_services_owned;
+}
+
+dbus_bool_t
+bus_connection_complete (DBusConnection *connection,
+ const DBusString *name,
+ DBusError *error)
+{
+ BusConnectionData *d;
+ unsigned long uid;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+ _dbus_assert (d->name == NULL);
+ _dbus_assert (d->policy == NULL);
+
+ _dbus_assert (!bus_connection_is_active (connection));
+
+ if (!_dbus_string_copy_data (name, &d->name))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_assert (d->name != NULL);
+
+ _dbus_verbose ("Name %s assigned to %p\n", d->name, connection);
+
+ d->policy = bus_context_create_client_policy (d->connections->context,
+ connection,
+ error);
+
+ /* we may have a NULL policy on OOM or error getting list of
+ * groups for a user. In the latter case we don't handle it so
+ * well currently, as it will just keep failing over and over.
+ */
+
+ if (d->policy == NULL)
+ {
+ _dbus_verbose ("Failed to create security policy for connection %p\n",
+ connection);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ dbus_free (d->name);
+ d->name = NULL;
+ return FALSE;
+ }
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!adjust_connections_for_uid (d->connections,
+ uid, 1))
+ goto fail;
+ }
+
+ /* Create and cache a string which holds information about the
+ * peer process; used for logging purposes.
+ */
+ if (!cache_peer_loginfo_string (d, connection))
+ goto fail;
+
+ /* Now the connection is active, move it between lists */
+ _dbus_list_unlink (&d->connections->incomplete,
+ d->link_in_connection_list);
+ d->connections->n_incomplete -= 1;
+ _dbus_list_append_link (&d->connections->completed,
+ d->link_in_connection_list);
+ d->connections->n_completed += 1;
+
+ _dbus_assert (d->connections->n_incomplete >= 0);
+ _dbus_assert (d->connections->n_completed > 0);
+
+ /* See if we can remove the timeout */
+ bus_connections_expire_incomplete (d->connections);
+
+ _dbus_assert (bus_connection_is_active (connection));
+
+ return TRUE;
+fail:
+ BUS_SET_OOM (error);
+ dbus_free (d->name);
+ d->name = NULL;
+ if (d->policy)
+ bus_client_policy_unref (d->policy);
+ d->policy = NULL;
+ return FALSE;
+}
+
+const char *
+bus_connection_get_name (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ return d->name;
+}
+
+/**
+ * Check whether completing the passed-in connection would
+ * exceed limits, and if so set error and return #FALSE
+ */
+dbus_bool_t
+bus_connections_check_limits (BusConnections *connections,
+ DBusConnection *requesting_completion,
+ DBusError *error)
+{
+ BusConnectionData *d;
+ unsigned long uid;
+
+ d = BUS_CONNECTION_DATA (requesting_completion);
+ _dbus_assert (d != NULL);
+
+ _dbus_assert (d->name == NULL);
+
+ if (connections->n_completed >=
+ bus_context_get_max_completed_connections (connections->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of active connections has been reached");
+ return FALSE;
+ }
+
+ if (dbus_connection_get_unix_user (requesting_completion, &uid))
+ {
+ if (get_connections_for_uid (connections, uid) >=
+ bus_context_get_max_connections_per_user (connections->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of active connections for UID %lu has been reached",
+ uid);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+bus_pending_reply_free (BusPendingReply *pending)
+{
+ _dbus_verbose ("Freeing pending reply %p, replier %p receiver %p serial %u\n",
+ pending,
+ pending->will_send_reply,
+ pending->will_get_reply,
+ pending->reply_serial);
+
+ dbus_free (pending);
+}
+
+static dbus_bool_t
+bus_pending_reply_send_no_reply (BusConnections *connections,
+ BusTransaction *transaction,
+ BusPendingReply *pending)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+ dbus_bool_t retval;
+ const char *errmsg;
+
+ retval = FALSE;
+
+ message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
+ if (message == NULL)
+ return FALSE;
+
+ dbus_message_set_no_reply (message, TRUE);
+
+ if (!dbus_message_set_reply_serial (message,
+ pending->reply_serial))
+ goto out;
+
+ if (!dbus_message_set_error_name (message,
+ DBUS_ERROR_NO_REPLY))
+ goto out;
+
+ errmsg = "Message did not receive a reply (timeout by message bus)";
+ dbus_message_iter_init_append (message, &iter);
+ if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &errmsg))
+ goto out;
+
+ if (!bus_transaction_send_from_driver (transaction, pending->will_get_reply,
+ message))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_message_unref (message);
+ return retval;
+}
+
+static dbus_bool_t
+bus_pending_reply_expired (BusExpireList *list,
+ DBusList *link,
+ void *data)
+{
+ BusPendingReply *pending = link->data;
+ BusConnections *connections = data;
+ BusTransaction *transaction;
+
+ /* No reply is forthcoming. So nuke it if we can. If not,
+ * leave it in the list to try expiring again later when we
+ * get more memory.
+ */
+
+ _dbus_verbose ("Expiring pending reply %p, replier %p receiver %p serial %u\n",
+ pending,
+ pending->will_send_reply,
+ pending->will_get_reply,
+ pending->reply_serial);
+
+ transaction = bus_transaction_new (connections->context);
+ if (transaction == NULL)
+ return FALSE;
+
+ if (!bus_pending_reply_send_no_reply (connections,
+ transaction,
+ pending))
+ {
+ bus_transaction_cancel_and_free (transaction);
+ return FALSE;
+ }
+
+ bus_expire_list_remove_link (connections->pending_replies, link);
+
+ bus_pending_reply_free (pending);
+ bus_transaction_execute_and_free (transaction);
+
+ return TRUE;
+}
+
+static void
+bus_connection_drop_pending_replies (BusConnections *connections,
+ DBusConnection *connection)
+{
+ /* The DBusConnection is almost 100% finalized here, so you can't
+ * do anything with it except check for pointer equality
+ */
+ DBusList *link;
+
+ _dbus_verbose ("Dropping pending replies that involve connection %p\n",
+ connection);
+
+ link = bus_expire_list_get_first_link (connections->pending_replies);
+ while (link != NULL)
+ {
+ DBusList *next;
+ BusPendingReply *pending;
+
+ next = bus_expire_list_get_next_link (connections->pending_replies,
+ link);
+ pending = link->data;
+
+ if (pending->will_get_reply == connection)
+ {
+ /* We don't need to track this pending reply anymore */
+
+ _dbus_verbose ("Dropping pending reply %p, replier %p receiver %p serial %u\n",
+ pending,
+ pending->will_send_reply,
+ pending->will_get_reply,
+ pending->reply_serial);
+
+ bus_expire_list_remove_link (connections->pending_replies,
+ link);
+ bus_pending_reply_free (pending);
+ }
+ else if (pending->will_send_reply == connection)
+ {
+ /* The reply isn't going to be sent, so set things
+ * up so it will be expired right away
+ */
+ _dbus_verbose ("Will expire pending reply %p, replier %p receiver %p serial %u\n",
+ pending,
+ pending->will_send_reply,
+ pending->will_get_reply,
+ pending->reply_serial);
+
+ pending->will_send_reply = NULL;
+ pending->expire_item.added_tv_sec = 0;
+ pending->expire_item.added_tv_usec = 0;
+
+ bus_expire_list_recheck_immediately (connections->pending_replies);
+ }
+
+ link = next;
+ }
+}
+
+
+typedef struct
+{
+ BusPendingReply *pending;
+ BusConnections *connections;
+} CancelPendingReplyData;
+
+static void
+cancel_pending_reply (void *data)
+{
+ CancelPendingReplyData *d = data;
+
+ _dbus_verbose ("%s: d = %p\n", _DBUS_FUNCTION_NAME, d);
+
+ if (!bus_expire_list_remove (d->connections->pending_replies,
+ &d->pending->expire_item))
+ _dbus_assert_not_reached ("pending reply did not exist to be cancelled");
+
+ bus_pending_reply_free (d->pending); /* since it's been cancelled */
+}
+
+static void
+cancel_pending_reply_data_free (void *data)
+{
+ CancelPendingReplyData *d = data;
+
+ _dbus_verbose ("%s: d = %p\n", _DBUS_FUNCTION_NAME, d);
+
+ /* d->pending should be either freed or still
+ * in the list of pending replies (owned by someone
+ * else)
+ */
+
+ dbus_free (d);
+}
+
+/*
+ * Record that a reply is allowed; return TRUE on success.
+ */
+dbus_bool_t
+bus_connections_expect_reply (BusConnections *connections,
+ BusTransaction *transaction,
+ DBusConnection *will_get_reply,
+ DBusConnection *will_send_reply,
+ DBusMessage *reply_to_this,
+ DBusError *error)
+{
+ BusPendingReply *pending;
+ dbus_uint32_t reply_serial;
+ DBusList *link;
+ CancelPendingReplyData *cprd;
+ int count;
+
+ _dbus_assert (will_get_reply != NULL);
+ _dbus_assert (will_send_reply != NULL);
+ _dbus_assert (reply_to_this != NULL);
+
+ if (dbus_message_get_no_reply (reply_to_this))
+ return TRUE; /* we won't allow a reply, since client doesn't care for one. */
+
+ reply_serial = dbus_message_get_serial (reply_to_this);
+
+ link = bus_expire_list_get_first_link (connections->pending_replies);
+ count = 0;
+ while (link != NULL)
+ {
+ pending = link->data;
+
+ if (pending->reply_serial == reply_serial &&
+ pending->will_get_reply == will_get_reply &&
+ pending->will_send_reply == will_send_reply)
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Message has the same reply serial as a currently-outstanding existing method call");
+ return FALSE;
+ }
+
+ link = bus_expire_list_get_next_link (connections->pending_replies,
+ link);
+ if (pending->will_get_reply == will_get_reply)
+ ++count;
+ }
+
+ if (count >=
+ bus_context_get_max_replies_per_connection (connections->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of pending replies per connection has been reached");
+ return FALSE;
+ }
+
+ pending = dbus_new0 (BusPendingReply, 1);
+ if (pending == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ /* so we can see a not-yet-added pending reply */
+ pending->expire_item.added_tv_sec = 1;
+ pending->expire_item.added_tv_usec = 1;
+#endif
+
+ pending->will_get_reply = will_get_reply;
+ pending->will_send_reply = will_send_reply;
+ pending->reply_serial = reply_serial;
+
+ cprd = dbus_new0 (CancelPendingReplyData, 1);
+ if (cprd == NULL)
+ {
+ BUS_SET_OOM (error);
+ bus_pending_reply_free (pending);
+ return FALSE;
+ }
+
+ if (!bus_expire_list_add (connections->pending_replies,
+ &pending->expire_item))
+ {
+ BUS_SET_OOM (error);
+ dbus_free (cprd);
+ bus_pending_reply_free (pending);
+ return FALSE;
+ }
+
+ if (!bus_transaction_add_cancel_hook (transaction,
+ cancel_pending_reply,
+ cprd,
+ cancel_pending_reply_data_free))
+ {
+ BUS_SET_OOM (error);
+ bus_expire_list_remove (connections->pending_replies, &pending->expire_item);
+ dbus_free (cprd);
+ bus_pending_reply_free (pending);
+ return FALSE;
+ }
+
+ cprd->pending = pending;
+ cprd->connections = connections;
+
+ _dbus_get_current_time (&pending->expire_item.added_tv_sec,
+ &pending->expire_item.added_tv_usec);
+
+ _dbus_verbose ("Added pending reply %p, replier %p receiver %p serial %u\n",
+ pending,
+ pending->will_send_reply,
+ pending->will_get_reply,
+ pending->reply_serial);
+
+ return TRUE;
+}
+
+typedef struct
+{
+ DBusList *link;
+ BusConnections *connections;
+} CheckPendingReplyData;
+
+static void
+cancel_check_pending_reply (void *data)
+{
+ CheckPendingReplyData *d = data;
+
+ _dbus_verbose ("%s: d = %p\n", _DBUS_FUNCTION_NAME, d);
+
+ bus_expire_list_add_link (d->connections->pending_replies,
+ d->link);
+ d->link = NULL;
+}
+
+static void
+check_pending_reply_data_free (void *data)
+{
+ CheckPendingReplyData *d = data;
+
+ _dbus_verbose ("%s: d = %p\n", _DBUS_FUNCTION_NAME, d);
+
+ if (d->link != NULL)
+ {
+ BusPendingReply *pending = d->link->data;
+
+ _dbus_assert (!bus_expire_list_contains_item (d->connections->pending_replies,
+ &pending->expire_item));
+
+ bus_pending_reply_free (pending);
+ _dbus_list_free_link (d->link);
+ }
+
+ dbus_free (d);
+}
+
+/*
+ * Check whether a reply is allowed, remove BusPendingReply
+ * if so, return TRUE if so.
+ */
+dbus_bool_t
+bus_connections_check_reply (BusConnections *connections,
+ BusTransaction *transaction,
+ DBusConnection *sending_reply,
+ DBusConnection *receiving_reply,
+ DBusMessage *reply,
+ DBusError *error)
+{
+ CheckPendingReplyData *cprd;
+ DBusList *link;
+ dbus_uint32_t reply_serial;
+
+ _dbus_assert (sending_reply != NULL);
+ _dbus_assert (receiving_reply != NULL);
+
+ reply_serial = dbus_message_get_reply_serial (reply);
+
+ link = bus_expire_list_get_first_link (connections->pending_replies);
+ while (link != NULL)
+ {
+ BusPendingReply *pending = link->data;
+
+ if (pending->reply_serial == reply_serial &&
+ pending->will_get_reply == receiving_reply &&
+ pending->will_send_reply == sending_reply)
+ {
+ _dbus_verbose ("Found pending reply with serial %u\n", reply_serial);
+ break;
+ }
+
+ link = bus_expire_list_get_next_link (connections->pending_replies,
+ link);
+ }
+
+ if (link == NULL)
+ {
+ _dbus_verbose ("No pending reply expected\n");
+
+ return FALSE;
+ }
+
+ cprd = dbus_new0 (CheckPendingReplyData, 1);
+ if (cprd == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_add_cancel_hook (transaction,
+ cancel_check_pending_reply,
+ cprd,
+ check_pending_reply_data_free))
+ {
+ BUS_SET_OOM (error);
+ dbus_free (cprd);
+ return FALSE;
+ }
+
+ cprd->link = link;
+ cprd->connections = connections;
+
+ bus_expire_list_unlink (connections->pending_replies,
+ link);
+
+ _dbus_assert (!bus_expire_list_contains_item (connections->pending_replies, link->data));
+
+ return TRUE;
+}
+
+/*
+ * Transactions
+ *
+ * Note that this is fairly fragile; in particular, don't try to use
+ * one transaction across any main loop iterations.
+ */
+
+typedef struct
+{
+ BusTransaction *transaction;
+ DBusMessage *message;
+ DBusPreallocatedSend *preallocated;
+} MessageToSend;
+
+typedef struct
+{
+ BusTransactionCancelFunction cancel_function;
+ DBusFreeFunction free_data_function;
+ void *data;
+} CancelHook;
+
+struct BusTransaction
+{
+ DBusList *connections;
+ BusContext *context;
+ DBusList *cancel_hooks;
+};
+
+static void
+message_to_send_free (DBusConnection *connection,
+ MessageToSend *to_send)
+{
+ if (to_send->message)
+ dbus_message_unref (to_send->message);
+
+ if (to_send->preallocated)
+ dbus_connection_free_preallocated_send (connection, to_send->preallocated);
+
+ dbus_free (to_send);
+}
+
+static void
+cancel_hook_cancel (void *element,
+ void *data)
+{
+ CancelHook *ch = element;
+
+ _dbus_verbose ("Running transaction cancel hook\n");
+
+ if (ch->cancel_function)
+ (* ch->cancel_function) (ch->data);
+}
+
+static void
+cancel_hook_free (void *element,
+ void *data)
+{
+ CancelHook *ch = element;
+
+ if (ch->free_data_function)
+ (* ch->free_data_function) (ch->data);
+
+ dbus_free (ch);
+}
+
+static void
+free_cancel_hooks (BusTransaction *transaction)
+{
+ _dbus_list_foreach (&transaction->cancel_hooks,
+ cancel_hook_free, NULL);
+
+ _dbus_list_clear (&transaction->cancel_hooks);
+}
+
+BusTransaction*
+bus_transaction_new (BusContext *context)
+{
+ BusTransaction *transaction;
+
+ transaction = dbus_new0 (BusTransaction, 1);
+ if (transaction == NULL)
+ return NULL;
+
+ transaction->context = context;
+
+ return transaction;
+}
+
+BusContext*
+bus_transaction_get_context (BusTransaction *transaction)
+{
+ return transaction->context;
+}
+
+BusConnections*
+bus_transaction_get_connections (BusTransaction *transaction)
+{
+ return bus_context_get_connections (transaction->context);
+}
+
+dbus_bool_t
+bus_transaction_send_from_driver (BusTransaction *transaction,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ /* We have to set the sender to the driver, and have
+ * to check security policy since it was not done in
+ * dispatch.c
+ */
+ _dbus_verbose ("Sending %s %s %s from driver\n",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(no interface)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(no member)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(no error name)");
+
+ if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
+ return FALSE;
+
+ if (bus_connection_is_active (connection))
+ {
+ if (!dbus_message_set_destination (message,
+ bus_connection_get_name (connection)))
+ return FALSE;
+ }
+
+ /* bus driver never wants a reply */
+ dbus_message_set_no_reply (message, TRUE);
+
+ /* If security policy doesn't allow the message, we silently
+ * eat it; the driver doesn't care about getting a reply.
+ */
+ if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
+ transaction,
+ NULL, connection, connection, message, NULL))
+ return TRUE;
+
+ return bus_transaction_send (transaction, connection, message);
+}
+
+dbus_bool_t
+bus_transaction_send (BusTransaction *transaction,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ MessageToSend *to_send;
+ BusConnectionData *d;
+ DBusList *link;
+
+ _dbus_verbose (" trying to add %s interface=%s member=%s error=%s to transaction%s\n",
+ dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ? "error" :
+ dbus_message_get_reply_serial (message) != 0 ? "reply" :
+ "message",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dbus_connection_get_is_connected (connection) ?
+ "" : " (disconnected)");
+
+ _dbus_assert (dbus_message_get_sender (message) != NULL);
+
+ if (!dbus_connection_get_is_connected (connection))
+ return TRUE; /* silently ignore disconnected connections */
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ to_send = dbus_new (MessageToSend, 1);
+ if (to_send == NULL)
+ {
+ return FALSE;
+ }
+
+ to_send->preallocated = dbus_connection_preallocate_send (connection);
+ if (to_send->preallocated == NULL)
+ {
+ dbus_free (to_send);
+ return FALSE;
+ }
+
+ dbus_message_ref (message);
+ to_send->message = message;
+ to_send->transaction = transaction;
+
+ _dbus_verbose ("about to prepend message\n");
+
+ if (!_dbus_list_prepend (&d->transaction_messages, to_send))
+ {
+ message_to_send_free (connection, to_send);
+ return FALSE;
+ }
+
+ _dbus_verbose ("prepended message\n");
+
+ /* See if we already had this connection in the list
+ * for this transaction. If we have a pending message,
+ * then we should already be in transaction->connections
+ */
+ link = _dbus_list_get_first_link (&d->transaction_messages);
+ _dbus_assert (link->data == to_send);
+ link = _dbus_list_get_next_link (&d->transaction_messages, link);
+ while (link != NULL)
+ {
+ MessageToSend *m = link->data;
+ DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
+
+ if (m->transaction == transaction)
+ break;
+
+ link = next;
+ }
+
+ if (link == NULL)
+ {
+ if (!_dbus_list_prepend (&transaction->connections, connection))
+ {
+ _dbus_list_remove (&d->transaction_messages, to_send);
+ message_to_send_free (connection, to_send);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+connection_cancel_transaction (DBusConnection *connection,
+ BusTransaction *transaction)
+{
+ DBusList *link;
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ link = _dbus_list_get_first_link (&d->transaction_messages);
+ while (link != NULL)
+ {
+ MessageToSend *m = link->data;
+ DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
+
+ if (m->transaction == transaction)
+ {
+ _dbus_list_remove_link (&d->transaction_messages,
+ link);
+
+ message_to_send_free (connection, m);
+ }
+
+ link = next;
+ }
+}
+
+void
+bus_transaction_cancel_and_free (BusTransaction *transaction)
+{
+ DBusConnection *connection;
+
+ _dbus_verbose ("TRANSACTION: cancelled\n");
+
+ while ((connection = _dbus_list_pop_first (&transaction->connections)))
+ connection_cancel_transaction (connection, transaction);
+
+ _dbus_assert (transaction->connections == NULL);
+
+ _dbus_list_foreach (&transaction->cancel_hooks,
+ cancel_hook_cancel, NULL);
+
+ free_cancel_hooks (transaction);
+
+ dbus_free (transaction);
+}
+
+static void
+connection_execute_transaction (DBusConnection *connection,
+ BusTransaction *transaction)
+{
+ DBusList *link;
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ /* Send the queue in order (FIFO) */
+ link = _dbus_list_get_last_link (&d->transaction_messages);
+ while (link != NULL)
+ {
+ MessageToSend *m = link->data;
+ DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
+
+ if (m->transaction == transaction)
+ {
+ _dbus_list_remove_link (&d->transaction_messages,
+ link);
+
+ _dbus_assert (dbus_message_get_sender (m->message) != NULL);
+
+ dbus_connection_send_preallocated (connection,
+ m->preallocated,
+ m->message,
+ NULL);
+
+ m->preallocated = NULL; /* so we don't double-free it */
+
+ message_to_send_free (connection, m);
+ }
+
+ link = prev;
+ }
+}
+
+void
+bus_transaction_execute_and_free (BusTransaction *transaction)
+{
+ /* For each connection in transaction->connections
+ * send the messages
+ */
+ DBusConnection *connection;
+
+ _dbus_verbose ("TRANSACTION: executing\n");
+
+ while ((connection = _dbus_list_pop_first (&transaction->connections)))
+ connection_execute_transaction (connection, transaction);
+
+ _dbus_assert (transaction->connections == NULL);
+
+ free_cancel_hooks (transaction);
+
+ dbus_free (transaction);
+}
+
+static void
+bus_connection_remove_transactions (DBusConnection *connection)
+{
+ MessageToSend *to_send;
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
+ {
+ /* only has an effect for the first MessageToSend listing this transaction */
+ _dbus_list_remove (&to_send->transaction->connections,
+ connection);
+
+ _dbus_list_remove (&d->transaction_messages, to_send);
+ message_to_send_free (connection, to_send);
+ }
+}
+
+/**
+ * Converts the DBusError to a message reply
+ */
+dbus_bool_t
+bus_transaction_send_error_reply (BusTransaction *transaction,
+ DBusConnection *connection,
+ const DBusError *error,
+ DBusMessage *in_reply_to)
+{
+ DBusMessage *reply;
+
+ _dbus_assert (error != NULL);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+
+ _dbus_verbose ("Sending error reply %s \"%s\"\n",
+ error->name, error->message);
+
+ reply = dbus_message_new_error (in_reply_to,
+ error->name,
+ error->message);
+ if (reply == NULL)
+ return FALSE;
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_transaction_add_cancel_hook (BusTransaction *transaction,
+ BusTransactionCancelFunction cancel_function,
+ void *data,
+ DBusFreeFunction free_data_function)
+{
+ CancelHook *ch;
+
+ ch = dbus_new (CancelHook, 1);
+ if (ch == NULL)
+ return FALSE;
+
+ _dbus_verbose (" adding cancel hook function = %p data = %p\n",
+ cancel_function, data);
+
+ ch->cancel_function = cancel_function;
+ ch->data = data;
+ ch->free_data_function = free_data_function;
+
+ /* It's important that the hooks get run in reverse order that they
+ * were added
+ */
+ if (!_dbus_list_prepend (&transaction->cancel_hooks, ch))
+ {
+ dbus_free (ch);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/bus/connection.h b/bus/connection.h
new file mode 100644
index 00000000..4b9a754b
--- /dev/null
+++ b/bus/connection.h
@@ -0,0 +1,141 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* connection.h Client connections
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_CONNECTION_H
+#define BUS_CONNECTION_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-list.h>
+#include "bus.h"
+
+typedef dbus_bool_t (* BusConnectionForeachFunction) (DBusConnection *connection,
+ void *data);
+
+
+BusConnections* bus_connections_new (BusContext *context);
+BusConnections* bus_connections_ref (BusConnections *connections);
+void bus_connections_unref (BusConnections *connections);
+dbus_bool_t bus_connections_setup_connection (BusConnections *connections,
+ DBusConnection *connection);
+void bus_connections_foreach (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data);
+void bus_connections_foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data);
+BusContext* bus_connections_get_context (BusConnections *connections);
+void bus_connections_increment_stamp (BusConnections *connections);
+BusContext* bus_connection_get_context (DBusConnection *connection);
+BusConnections* bus_connection_get_connections (DBusConnection *connection);
+BusRegistry* bus_connection_get_registry (DBusConnection *connection);
+BusActivation* bus_connection_get_activation (DBusConnection *connection);
+BusMatchmaker* bus_connection_get_matchmaker (DBusConnection *connection);
+const char * bus_connection_get_loginfo (DBusConnection *connection);
+BusSELinuxID* bus_connection_get_selinux_id (DBusConnection *connection);
+dbus_bool_t bus_connections_check_limits (BusConnections *connections,
+ DBusConnection *requesting_completion,
+ DBusError *error);
+void bus_connections_expire_incomplete (BusConnections *connections);
+
+dbus_bool_t bus_connections_expect_reply (BusConnections *connections,
+ BusTransaction *transaction,
+ DBusConnection *will_get_reply,
+ DBusConnection *will_send_reply,
+ DBusMessage *reply_to_this,
+ DBusError *error);
+dbus_bool_t bus_connections_check_reply (BusConnections *connections,
+ BusTransaction *transaction,
+ DBusConnection *sending_reply,
+ DBusConnection *receiving_reply,
+ DBusMessage *reply,
+ DBusError *error);
+
+dbus_bool_t bus_connection_mark_stamp (DBusConnection *connection);
+
+dbus_bool_t bus_connection_is_active (DBusConnection *connection);
+const char *bus_connection_get_name (DBusConnection *connection);
+
+dbus_bool_t bus_connection_preallocate_oom_error (DBusConnection *connection);
+void bus_connection_send_oom_error (DBusConnection *connection,
+ DBusMessage *in_reply_to);
+
+/* called by signals.c */
+dbus_bool_t bus_connection_add_match_rule (DBusConnection *connection,
+ BusMatchRule *rule);
+void bus_connection_add_match_rule_link (DBusConnection *connection,
+ DBusList *link);
+void bus_connection_remove_match_rule (DBusConnection *connection,
+ BusMatchRule *rule);
+int bus_connection_get_n_match_rules (DBusConnection *connection);
+
+
+/* called by services.c */
+dbus_bool_t bus_connection_add_owned_service (DBusConnection *connection,
+ BusService *service);
+void bus_connection_remove_owned_service (DBusConnection *connection,
+ BusService *service);
+void bus_connection_add_owned_service_link (DBusConnection *connection,
+ DBusList *link);
+int bus_connection_get_n_services_owned (DBusConnection *connection);
+
+/* called by driver.c */
+dbus_bool_t bus_connection_complete (DBusConnection *connection,
+ const DBusString *name,
+ DBusError *error);
+
+/* called by dispatch.c when the connection is dropped */
+void bus_connection_disconnected (DBusConnection *connection);
+
+dbus_bool_t bus_connection_is_in_unix_group (DBusConnection *connection,
+ unsigned long gid);
+dbus_bool_t bus_connection_get_unix_groups (DBusConnection *connection,
+ unsigned long **groups,
+ int *n_groups,
+ DBusError *error);
+BusClientPolicy* bus_connection_get_policy (DBusConnection *connection);
+
+/* transaction API so we can send or not send a block of messages as a whole */
+
+typedef void (* BusTransactionCancelFunction) (void *data);
+
+BusTransaction* bus_transaction_new (BusContext *context);
+BusContext* bus_transaction_get_context (BusTransaction *transaction);
+BusConnections* bus_transaction_get_connections (BusTransaction *transaction);
+dbus_bool_t bus_transaction_send (BusTransaction *transaction,
+ DBusConnection *connection,
+ DBusMessage *message);
+dbus_bool_t bus_transaction_send_from_driver (BusTransaction *transaction,
+ DBusConnection *connection,
+ DBusMessage *message);
+dbus_bool_t bus_transaction_send_error_reply (BusTransaction *transaction,
+ DBusConnection *connection,
+ const DBusError *error,
+ DBusMessage *in_reply_to);
+void bus_transaction_cancel_and_free (BusTransaction *transaction);
+void bus_transaction_execute_and_free (BusTransaction *transaction);
+dbus_bool_t bus_transaction_add_cancel_hook (BusTransaction *transaction,
+ BusTransactionCancelFunction cancel_function,
+ void *data,
+ DBusFreeFunction free_data_function);
+
+#endif /* BUS_CONNECTION_H */
diff --git a/bus/dbus-daemon.1 b/bus/dbus-daemon.1
new file mode 100644
index 00000000..6bfca148
--- /dev/null
+++ b/bus/dbus-daemon.1
@@ -0,0 +1,760 @@
+.\"
+.\" dbus-daemon manual page.
+.\" Copyright (C) 2003,2008 Red Hat, Inc.
+.\"
+.TH dbus-daemon 1
+.SH NAME
+dbus-daemon \- Message bus daemon
+.SH SYNOPSIS
+.PP
+.B dbus-daemon
+dbus-daemon [\-\-version] [\-\-session] [\-\-system] [\-\-config-file=FILE]
+[\-\-print-address[=DESCRIPTOR]] [\-\-print-pid[=DESCRIPTOR]] [\-\-fork]
+
+.SH DESCRIPTION
+
+\fIdbus-daemon\fP is the D-Bus message bus daemon. See
+http://www.freedesktop.org/software/dbus/ for more information about
+the big picture. D-Bus is first a library that provides one-to-one
+communication between any two applications; \fIdbus-daemon\fP is an
+application that uses this library to implement a message bus
+daemon. Multiple programs connect to the message bus daemon and can
+exchange messages with one another.
+
+.PP
+There are two standard message bus instances: the systemwide message bus
+(installed on many systems as the "messagebus" init service) and the
+per-user-login-session message bus (started each time a user logs in).
+\fIdbus-daemon\fP is used for both of these instances, but with
+a different configuration file.
+
+.PP
+The \-\-session option is equivalent to
+"\-\-config-file=/src/build/dbus/etc/dbus-1/session.conf" and the \-\-system
+option is equivalent to
+"\-\-config-file=/src/build/dbus/etc/dbus-1/system.conf". By creating
+additional configuration files and using the \-\-config-file option,
+additional special-purpose message bus daemons could be created.
+
+.PP
+The systemwide daemon is normally launched by an init script,
+standardly called simply "messagebus".
+
+.PP
+The systemwide daemon is largely used for broadcasting system events,
+such as changes to the printer queue, or adding/removing devices.
+
+.PP
+The per-session daemon is used for various interprocess communication
+among desktop applications (however, it is not tied to X or the GUI
+in any way).
+
+.PP
+SIGHUP will cause the D-Bus daemon to PARTIALLY reload its
+configuration file and to flush its user/group information caches. Some
+configuration changes would require kicking all apps off the bus; so they will
+only take effect if you restart the daemon. Policy changes should take effect
+with SIGHUP.
+
+.SH OPTIONS
+The following options are supported:
+.TP
+.I "--config-file=FILE"
+Use the given configuration file.
+.TP
+.I "--fork"
+Force the message bus to fork and become a daemon, even if
+the configuration file does not specify that it should.
+In most contexts the configuration file already gets this
+right, though.
+.TP
+.I "--print-address[=DESCRIPTOR]"
+Print the address of the message bus to standard output, or
+to the given file descriptor. This is used by programs that
+launch the message bus.
+.TP
+.I "--print-pid[=DESCRIPTOR]"
+Print the process ID of the message bus to standard output, or
+to the given file descriptor. This is used by programs that
+launch the message bus.
+.TP
+.I "--session"
+Use the standard configuration file for the per-login-session message
+bus.
+.TP
+.I "--system"
+Use the standard configuration file for the systemwide message bus.
+.TP
+.I "--version"
+Print the version of the daemon.
+
+.SH CONFIGURATION FILE
+
+A message bus daemon has a configuration file that specializes it
+for a particular application. For example, one configuration
+file might set up the message bus to be a systemwide message bus,
+while another might set it up to be a per-user-login-session bus.
+
+.PP
+The configuration file also establishes resource limits, security
+parameters, and so forth.
+
+.PP
+The configuration file is not part of any interoperability
+specification and its backward compatibility is not guaranteed; this
+document is documentation, not specification.
+
+.PP
+The standard systemwide and per-session message bus setups are
+configured in the files "/src/build/dbus/etc/dbus-1/system.conf" and
+"/src/build/dbus/etc/dbus-1/session.conf". These files normally
+<include> a system-local.conf or session-local.conf; you can put local
+overrides in those files to avoid modifying the primary configuration
+files.
+
+.PP
+The configuration file is an XML document. It must have the following
+doctype declaration:
+.nf
+
+ <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+.fi
+
+.PP
+The following elements may be present in the configuration file.
+
+.TP
+.I "<busconfig>"
+
+.PP
+Root element.
+
+.TP
+.I "<type>"
+
+.PP
+The well-known type of the message bus. Currently known values are
+"system" and "session"; if other values are set, they should be
+either added to the D-Bus specification, or namespaced. The last
+<type> element "wins" (previous values are ignored). This element
+only controls which message bus specific environment variables are
+set in activated clients. Most of the policy that distinguishes a
+session bus from the system bus is controlled from the other elements
+in the configuration file.
+
+.PP
+If the well-known type of the message bus is "session", then the
+DBUS_STARTER_BUS_TYPE environment variable will be set to "session"
+and the DBUS_SESSION_BUS_ADDRESS environment variable will be set
+to the address of the session bus. Likewise, if the type of the
+message bus is "system", then the DBUS_STARTER_BUS_TYPE environment
+variable will be set to "system" and the DBUS_SESSION_BUS_ADDRESS
+environment variable will be set to the address of the system bus
+(which is normally well known anyway).
+
+.PP
+Example: <type>session</type>
+
+.TP
+.I "<include>"
+
+.PP
+Include a file <include>filename.conf</include> at this point. If the
+filename is relative, it is located relative to the configuration file
+doing the including.
+
+.PP
+<include> has an optional attribute "ignore_missing=(yes|no)"
+which defaults to "no" if not provided. This attribute
+controls whether it's a fatal error for the included file
+to be absent.
+
+.TP
+.I "<includedir>"
+
+.PP
+Include all files in <includedir>foo.d</includedir> at this
+point. Files in the directory are included in undefined order.
+Only files ending in ".conf" are included.
+
+.PP
+This is intended to allow extension of the system bus by particular
+packages. For example, if CUPS wants to be able to send out
+notification of printer queue changes, it could install a file to
+/src/build/dbus/etc/dbus-1/system.d that allowed all apps to receive
+this message and allowed the printer daemon user to send it.
+
+.TP
+.I "<user>"
+
+.PP
+The user account the daemon should run as, as either a username or a
+UID. If the daemon cannot change to this UID on startup, it will exit.
+If this element is not present, the daemon will not change or care
+about its UID.
+
+.PP
+The last <user> entry in the file "wins", the others are ignored.
+
+.PP
+The user is changed after the bus has completed initialization. So
+sockets etc. will be created before changing user, but no data will be
+read from clients before changing user. This means that sockets
+and PID files can be created in a location that requires root
+privileges for writing.
+
+.TP
+.I "<fork>"
+
+.PP
+If present, the bus daemon becomes a real daemon (forks
+into the background, etc.). This is generally used
+rather than the \-\-fork command line option.
+
+.TP
+.I "<keep_umask>"
+
+.PP
+If present, the bus daemon keeps its original umask when forking.
+This may be useful to avoid affecting the behavior of child processes.
+
+.TP
+.I "<listen>"
+
+.PP
+Add an address that the bus should listen on. The
+address is in the standard D-Bus format that contains
+a transport name plus possible parameters/options.
+
+.PP
+Example: <listen>unix:path=/tmp/foo</listen>
+
+.PP
+Example: <listen>tcp:host=localhost,port=1234</listen>
+
+.PP
+If there are multiple <listen> elements, then the bus listens
+on multiple addresses. The bus will pass its address to
+started services or other interested parties with
+the last address given in <listen> first. That is,
+apps will try to connect to the last <listen> address first.
+
+.PP
+tcp sockets can accept IPv4 addresses, IPv6 addresses or hostnames.
+If a hostname resolves to multiple addresses, the server will bind
+to all of them. The family=ipv4 or family=ipv6 options can be used
+to force it to bind to a subset of addresses
+
+.PP
+Example: <listen>tcp:host=localhost,port=0,family=ipv4</listen>
+
+.PP
+A special case is using a port number of zero (or omitting the port),
+which means to choose an available port selected by the operating
+system. The port number chosen can be obtained with the
+--print-address command line parameter and will be present in other
+cases where the server reports its own address, such as when
+DBUS_SESSION_BUS_ADDRESS is set.
+
+.PP
+Example: <listen>tcp:host=localhost,port=0</listen>
+
+.PP
+tcp addresses also allow a bind=hostname option, which will override
+the host option specifying what address to bind to, without changing
+the address reported by the bus. The bind option can also take a
+special name '*' to cause the bus to listen on all local address
+(INADDR_ANY). The specified host should be a valid name of the local
+machine or weird stuff will happen.
+
+.PP
+Example: <listen>tcp:host=localhost,bind=*,port=0</listen>
+
+.TP
+.I "<auth>"
+
+.PP
+Lists permitted authorization mechanisms. If this element doesn't
+exist, then all known mechanisms are allowed. If there are multiple
+<auth> elements, all the listed mechanisms are allowed. The order in
+which mechanisms are listed is not meaningful.
+
+.PP
+Example: <auth>EXTERNAL</auth>
+
+.PP
+Example: <auth>DBUS_COOKIE_SHA1</auth>
+
+.TP
+.I "<servicedir>"
+
+.PP
+Adds a directory to scan for .service files. Directories are
+scanned starting with the last to appear in the config file
+(the first .service file found that provides a particular
+service will be used).
+
+.PP
+Service files tell the bus how to automatically start a program.
+They are primarily used with the per-user-session bus,
+not the systemwide bus.
+
+.TP
+.I "<standard_session_servicedirs/>"
+
+.PP
+<standard_session_servicedirs/> is equivalent to specifying a series
+of <servicedir/> elements for each of the data directories in the "XDG
+Base Directory Specification" with the subdirectory "dbus-1/services",
+so for example "/usr/share/dbus-1/services" would be among the
+directories searched.
+
+.PP
+The "XDG Base Directory Specification" can be found at
+http://freedesktop.org/wiki/Standards/basedir-spec if it hasn't moved,
+otherwise try your favorite search engine.
+
+.PP
+The <standard_session_servicedirs/> option is only relevant to the
+per-user-session bus daemon defined in
+/src/build/dbus/etc/dbus-1/session.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<standard_system_servicedirs/>"
+
+.PP
+<standard_system_servicedirs/> specifies the standard system-wide
+activation directories that should be searched for service files.
+This option defaults to /src/build/dbus/share/dbus-1/system-services.
+
+.PP
+The <standard_system_servicedirs/> option is only relevant to the
+per-system bus daemon defined in
+/src/build/dbus/etc/dbus-1/system.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<servicehelper/>"
+
+.PP
+<servicehelper/> specifies the setuid helper that is used to launch
+system daemons with an alternate user. Typically this should be
+the dbus-daemon-launch-helper executable in located in libexec.
+
+.PP
+The <servicehelper/> option is only relevant to the per-system bus daemon
+defined in /src/build/dbus/etc/dbus-1/system.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<limit>"
+
+.PP
+<limit> establishes a resource limit. For example:
+.nf
+ <limit name="max_message_size">64</limit>
+ <limit name="max_completed_connections">512</limit>
+.fi
+
+.PP
+The name attribute is mandatory.
+Available limit names are:
+.nf
+ "max_incoming_bytes" : total size in bytes of messages
+ incoming from a single connection
+ "max_outgoing_bytes" : total size in bytes of messages
+ queued up for a single connection
+ "max_message_size" : max size of a single message in
+ bytes
+ "service_start_timeout" : milliseconds (thousandths) until
+ a started service has to connect
+ "auth_timeout" : milliseconds (thousandths) a
+ connection is given to
+ authenticate
+ "max_completed_connections" : max number of authenticated connections
+ "max_incomplete_connections" : max number of unauthenticated
+ connections
+ "max_connections_per_user" : max number of completed connections from
+ the same user
+ "max_pending_service_starts" : max number of service launches in
+ progress at the same time
+ "max_names_per_connection" : max number of names a single
+ connection can own
+ "max_match_rules_per_connection": max number of match rules for a single
+ connection
+ "max_replies_per_connection" : max number of pending method
+ replies per connection
+ (number of calls-in-progress)
+ "reply_timeout" : milliseconds (thousandths)
+ until a method call times out
+.fi
+
+.PP
+The max incoming/outgoing queue sizes allow a new message to be queued
+if one byte remains below the max. So you can in fact exceed the max
+by max_message_size.
+
+.PP
+max_completed_connections divided by max_connections_per_user is the
+number of users that can work together to denial-of-service all other users by using
+up all connections on the systemwide bus.
+
+.PP
+Limits are normally only of interest on the systemwide bus, not the user session
+buses.
+
+.TP
+.I "<policy>"
+
+.PP
+The <policy> element defines a security policy to be applied to a particular
+set of connections to the bus. A policy is made up of
+<allow> and <deny> elements. Policies are normally used with the systemwide bus;
+they are analogous to a firewall in that they allow expected traffic
+and prevent unexpected traffic.
+
+.PP
+Currently, the system bus has a default-deny policy for sending method calls
+and owning bus names. Everything else, in particular reply messages, receive
+checks, and signals has a default allow policy.
+
+.PP
+In general, it is best to keep system services as small, targeted programs which
+run in their own process and provide a single bus name. Then, all that is needed
+is an <allow> rule for the "own" permission to let the process claim the bus
+name, and a "send_destination" rule to allow traffic from some or all uids to
+your service.
+
+.PP
+The <policy> element has one of four attributes:
+daemon.1.in
+.nf
+ context="(default|mandatory)"
+ at_console="(true|false)"
+ user="username or userid"
+ group="group name or gid"
+.fi
+
+.PP
+Policies are applied to a connection as follows:
+.nf
+ - all context="default" policies are applied
+ - all group="connection's user's group" policies are applied
+ in undefined order
+ - all user="connection's auth user" policies are applied
+ in undefined order
+ - all at_console="true" policies are applied
+ - all at_console="false" policies are applied
+ - all context="mandatory" policies are applied
+.fi
+
+.PP
+Policies applied later will override those applied earlier,
+when the policies overlap. Multiple policies with the same
+user/group/context are applied in the order they appear
+in the config file.
+
+.TP
+.I "<deny>"
+.I "<allow>"
+
+.PP
+A <deny> element appears below a <policy> element and prohibits some
+action. The <allow> element makes an exception to previous <deny>
+statements, and works just like <deny> but with the inverse meaning.
+
+.PP
+The possible attributes of these elements are:
+.nf
+ send_interface="interface_name"
+ send_member="method_or_signal_name"
+ send_error="error_name"
+ send_destination="name"
+ send_type="method_call" | "method_return" | "signal" | "error"
+ send_path="/path/name"
+
+ receive_interface="interface_name"
+ receive_member="method_or_signal_name"
+ receive_error="error_name"
+ receive_sender="name"
+ receive_type="method_call" | "method_return" | "signal" | "error"
+ receive_path="/path/name"
+
+ send_requested_reply="true" | "false"
+ receive_requested_reply="true" | "false"
+
+ eavesdrop="true" | "false"
+
+ own="name"
+ user="username"
+ group="groupname"
+.fi
+
+.PP
+Examples:
+.nf
+ <deny send_interface="org.freedesktop.System" send_member="Reboot"/>
+ <deny receive_interface="org.freedesktop.System" receive_member="Reboot"/>
+ <deny own="org.freedesktop.System"/>
+ <deny send_destination="org.freedesktop.System"/>
+ <deny receive_sender="org.freedesktop.System"/>
+ <deny user="john"/>
+ <deny group="enemies"/>
+.fi
+
+.PP
+The <deny> element's attributes determine whether the deny "matches" a
+particular action. If it matches, the action is denied (unless later
+rules in the config file allow it).
+
+.PP
+send_destination and receive_sender rules mean that messages may not be
+sent to or received from the *owner* of the given name, not that
+they may not be sent *to that name*. That is, if a connection
+owns services A, B, C, and sending to A is denied, sending to B or C
+will not work either.
+
+.PP
+The other send_* and receive_* attributes are purely textual/by-value
+matches against the given field in the message header.
+
+.PP
+"Eavesdropping" occurs when an application receives a message that
+was explicitly addressed to a name the application does not own, or
+is a reply to such a message. Eavesdropping thus only applies to
+messages that are addressed to services and replies to such messages
+(i.e. it does not apply to signals).
+
+.PP
+For <allow>, eavesdrop="true" indicates that the rule matches even
+when eavesdropping. eavesdrop="false" is the default and means that
+the rule only allows messages to go to their specified recipient.
+For <deny>, eavesdrop="true" indicates that the rule matches
+only when eavesdropping. eavesdrop="false" is the default for <deny>
+also, but here it means that the rule applies always, even when
+not eavesdropping. The eavesdrop attribute can only be combined with
+send and receive rules (with send_* and receive_* attributes).
+
+
+.PP
+The [send|receive]_requested_reply attribute works similarly to the eavesdrop
+attribute. It controls whether the <deny> or <allow> matches a reply
+that is expected (corresponds to a previous method call message).
+This attribute only makes sense for reply messages (errors and method
+returns), and is ignored for other message types.
+
+.PP
+For <allow>, [send|receive]_requested_reply="true" is the default and indicates that
+only requested replies are allowed by the
+rule. [send|receive]_requested_reply="false" means that the rule allows any reply
+even if unexpected.
+
+.PP
+For <deny>, [send|receive]_requested_reply="false" is the default but indicates that
+the rule matches only when the reply was not
+requested. [send|receive]_requested_reply="true" indicates that the rule applies
+always, regardless of pending reply state.
+
+.PP
+user and group denials mean that the given user or group may
+not connect to the message bus.
+
+.PP
+For "name", "username", "groupname", etc.
+the character "*" can be substituted, meaning "any." Complex globs
+like "foo.bar.*" aren't allowed for now because they'd be work to
+implement and maybe encourage sloppy security anyway.
+
+.PP
+It does not make sense to deny a user or group inside a <policy>
+for a user or group; user/group denials can only be inside
+context="default" or context="mandatory" policies.
+
+.PP
+A single <deny> rule may specify combinations of attributes such as
+send_destination and send_interface and send_type. In this case, the
+denial applies only if both attributes match the message being denied.
+e.g. <deny send_interface="foo.bar" send_destination="foo.blah"/> would
+deny messages with the given interface AND the given bus name.
+To get an OR effect you specify multiple <deny> rules.
+
+.PP
+You can't include both send_ and receive_ attributes on the same
+rule, since "whether the message can be sent" and "whether it can be
+received" are evaluated separately.
+
+.PP
+Be careful with send_interface/receive_interface, because the
+interface field in messages is optional. In particular, do NOT
+specify <deny send_interface="org.foo.Bar"/>! This will cause
+no-interface messages to be blocked for all services, which is
+almost certainly not what you intended. Always use rules of
+the form: <deny send_interface="org.foo.Bar" send_destination="org.foo.Service"/>
+
+.TP
+.I "<selinux>"
+
+.PP
+The <selinux> element contains settings related to Security Enhanced Linux.
+More details below.
+
+.TP
+.I "<associate>"
+
+.PP
+An <associate> element appears below an <selinux> element and
+creates a mapping. Right now only one kind of association is possible:
+.nf
+ <associate own="org.freedesktop.Foobar" context="foo_t"/>
+.fi
+
+.PP
+This means that if a connection asks to own the name
+"org.freedesktop.Foobar" then the source context will be the context
+of the connection and the target context will be "foo_t" - see the
+short discussion of SELinux below.
+
+.PP
+Note, the context here is the target context when requesting a name,
+NOT the context of the connection owning the name.
+
+.PP
+There's currently no way to set a default for owning any name, if
+we add this syntax it will look like:
+.nf
+ <associate own="*" context="foo_t"/>
+.fi
+If you find a reason this is useful, let the developers know.
+Right now the default will be the security context of the bus itself.
+
+.PP
+If two <associate> elements specify the same name, the element
+appearing later in the configuration file will be used.
+
+.SH SELinux
+
+.PP
+See http://www.nsa.gov/selinux/ for full details on SELinux. Some useful excerpts:
+
+.IP "" 8
+Every subject (process) and object (e.g. file, socket, IPC object,
+etc) in the system is assigned a collection of security attributes,
+known as a security context. A security context contains all of the
+security attributes associated with a particular subject or object
+that are relevant to the security policy.
+
+.IP "" 8
+In order to better encapsulate security contexts and to provide
+greater efficiency, the policy enforcement code of SELinux typically
+handles security identifiers (SIDs) rather than security contexts. A
+SID is an integer that is mapped by the security server to a security
+context at runtime.
+
+.IP "" 8
+When a security decision is required, the policy enforcement code
+passes a pair of SIDs (typically the SID of a subject and the SID of
+an object, but sometimes a pair of subject SIDs or a pair of object
+SIDs), and an object security class to the security server. The object
+security class indicates the kind of object, e.g. a process, a regular
+file, a directory, a TCP socket, etc.
+
+.IP "" 8
+Access decisions specify whether or not a permission is granted for a
+given pair of SIDs and class. Each object class has a set of
+associated permissions defined to control operations on objects with
+that class.
+
+.PP
+D-Bus performs SELinux security checks in two places.
+
+.PP
+First, any time a message is routed from one connection to another
+connection, the bus daemon will check permissions with the security context of
+the first connection as source, security context of the second connection
+as target, object class "dbus" and requested permission "send_msg".
+
+.PP
+If a security context is not available for a connection
+(impossible when using UNIX domain sockets), then the target
+context used is the context of the bus daemon itself.
+There is currently no way to change this default, because we're
+assuming that only UNIX domain sockets will be used to
+connect to the systemwide bus. If this changes, we'll
+probably add a way to set the default connection context.
+
+.PP
+Second, any time a connection asks to own a name,
+the bus daemon will check permissions with the security
+context of the connection as source, the security context specified
+for the name in the config file as target, object
+class "dbus" and requested permission "acquire_svc".
+
+.PP
+The security context for a bus name is specified with the
+<associate> element described earlier in this document.
+If a name has no security context associated in the
+configuration file, the security context of the bus daemon
+itself will be used.
+
+.SH DEBUGGING
+
+.PP
+If you're trying to figure out where your messages are going or why
+you aren't getting messages, there are several things you can try.
+
+.PP
+Remember that the system bus is heavily locked down and if you
+haven't installed a security policy file to allow your message
+through, it won't work. For the session bus, this is not a concern.
+
+.PP
+The simplest way to figure out what's happening on the bus is to run
+the \fIdbus-monitor\fP program, which comes with the D-Bus
+package. You can also send test messages with \fIdbus-send\fP. These
+programs have their own man pages.
+
+.PP
+If you want to know what the daemon itself is doing, you might consider
+running a separate copy of the daemon to test against. This will allow you
+to put the daemon under a debugger, or run it with verbose output, without
+messing up your real session and system daemons.
+
+.PP
+To run a separate test copy of the daemon, for example you might open a terminal
+and type:
+.nf
+ DBUS_VERBOSE=1 dbus-daemon --session --print-address
+.fi
+
+.PP
+The test daemon address will be printed when the daemon starts. You will need
+to copy-and-paste this address and use it as the value of the
+DBUS_SESSION_BUS_ADDRESS environment variable when you launch the applications
+you want to test. This will cause those applications to connect to your
+test bus instead of the DBUS_SESSION_BUS_ADDRESS of your real session bus.
+
+.PP
+DBUS_VERBOSE=1 will have NO EFFECT unless your copy of D-Bus
+was compiled with verbose mode enabled. This is not recommended in
+production builds due to performance impact. You may need to rebuild
+D-Bus if your copy was not built with debugging in mind. (DBUS_VERBOSE
+also affects the D-Bus library and thus applications using D-Bus; it may
+be useful to see verbose output on both the client side and from the daemon.)
+
+.PP
+If you want to get fancy, you can create a custom bus
+configuration for your test bus (see the session.conf and system.conf
+files that define the two default configurations for example). This
+would allow you to specify a different directory for .service files,
+for example.
+
+
+.SH AUTHOR
+See http://www.freedesktop.org/software/dbus/doc/AUTHORS
+
+.SH BUGS
+Please send bug reports to the D-Bus mailing list or bug tracker,
+see http://www.freedesktop.org/software/dbus/
diff --git a/bus/dbus-daemon.1.in b/bus/dbus-daemon.1.in
new file mode 100644
index 00000000..8342600e
--- /dev/null
+++ b/bus/dbus-daemon.1.in
@@ -0,0 +1,760 @@
+.\"
+.\" dbus-daemon manual page.
+.\" Copyright (C) 2003,2008 Red Hat, Inc.
+.\"
+.TH dbus-daemon 1
+.SH NAME
+dbus-daemon \- Message bus daemon
+.SH SYNOPSIS
+.PP
+.B dbus-daemon
+dbus-daemon [\-\-version] [\-\-session] [\-\-system] [\-\-config-file=FILE]
+[\-\-print-address[=DESCRIPTOR]] [\-\-print-pid[=DESCRIPTOR]] [\-\-fork]
+
+.SH DESCRIPTION
+
+\fIdbus-daemon\fP is the D-Bus message bus daemon. See
+http://www.freedesktop.org/software/dbus/ for more information about
+the big picture. D-Bus is first a library that provides one-to-one
+communication between any two applications; \fIdbus-daemon\fP is an
+application that uses this library to implement a message bus
+daemon. Multiple programs connect to the message bus daemon and can
+exchange messages with one another.
+
+.PP
+There are two standard message bus instances: the systemwide message bus
+(installed on many systems as the "messagebus" init service) and the
+per-user-login-session message bus (started each time a user logs in).
+\fIdbus-daemon\fP is used for both of these instances, but with
+a different configuration file.
+
+.PP
+The \-\-session option is equivalent to
+"\-\-config-file=@EXPANDED_SYSCONFDIR@/dbus-1/session.conf" and the \-\-system
+option is equivalent to
+"\-\-config-file=@EXPANDED_SYSCONFDIR@/dbus-1/system.conf". By creating
+additional configuration files and using the \-\-config-file option,
+additional special-purpose message bus daemons could be created.
+
+.PP
+The systemwide daemon is normally launched by an init script,
+standardly called simply "messagebus".
+
+.PP
+The systemwide daemon is largely used for broadcasting system events,
+such as changes to the printer queue, or adding/removing devices.
+
+.PP
+The per-session daemon is used for various interprocess communication
+among desktop applications (however, it is not tied to X or the GUI
+in any way).
+
+.PP
+SIGHUP will cause the D-Bus daemon to PARTIALLY reload its
+configuration file and to flush its user/group information caches. Some
+configuration changes would require kicking all apps off the bus; so they will
+only take effect if you restart the daemon. Policy changes should take effect
+with SIGHUP.
+
+.SH OPTIONS
+The following options are supported:
+.TP
+.I "--config-file=FILE"
+Use the given configuration file.
+.TP
+.I "--fork"
+Force the message bus to fork and become a daemon, even if
+the configuration file does not specify that it should.
+In most contexts the configuration file already gets this
+right, though.
+.TP
+.I "--print-address[=DESCRIPTOR]"
+Print the address of the message bus to standard output, or
+to the given file descriptor. This is used by programs that
+launch the message bus.
+.TP
+.I "--print-pid[=DESCRIPTOR]"
+Print the process ID of the message bus to standard output, or
+to the given file descriptor. This is used by programs that
+launch the message bus.
+.TP
+.I "--session"
+Use the standard configuration file for the per-login-session message
+bus.
+.TP
+.I "--system"
+Use the standard configuration file for the systemwide message bus.
+.TP
+.I "--version"
+Print the version of the daemon.
+
+.SH CONFIGURATION FILE
+
+A message bus daemon has a configuration file that specializes it
+for a particular application. For example, one configuration
+file might set up the message bus to be a systemwide message bus,
+while another might set it up to be a per-user-login-session bus.
+
+.PP
+The configuration file also establishes resource limits, security
+parameters, and so forth.
+
+.PP
+The configuration file is not part of any interoperability
+specification and its backward compatibility is not guaranteed; this
+document is documentation, not specification.
+
+.PP
+The standard systemwide and per-session message bus setups are
+configured in the files "@EXPANDED_SYSCONFDIR@/dbus-1/system.conf" and
+"@EXPANDED_SYSCONFDIR@/dbus-1/session.conf". These files normally
+<include> a system-local.conf or session-local.conf; you can put local
+overrides in those files to avoid modifying the primary configuration
+files.
+
+.PP
+The configuration file is an XML document. It must have the following
+doctype declaration:
+.nf
+
+ <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+.fi
+
+.PP
+The following elements may be present in the configuration file.
+
+.TP
+.I "<busconfig>"
+
+.PP
+Root element.
+
+.TP
+.I "<type>"
+
+.PP
+The well-known type of the message bus. Currently known values are
+"system" and "session"; if other values are set, they should be
+either added to the D-Bus specification, or namespaced. The last
+<type> element "wins" (previous values are ignored). This element
+only controls which message bus specific environment variables are
+set in activated clients. Most of the policy that distinguishes a
+session bus from the system bus is controlled from the other elements
+in the configuration file.
+
+.PP
+If the well-known type of the message bus is "session", then the
+DBUS_STARTER_BUS_TYPE environment variable will be set to "session"
+and the DBUS_SESSION_BUS_ADDRESS environment variable will be set
+to the address of the session bus. Likewise, if the type of the
+message bus is "system", then the DBUS_STARTER_BUS_TYPE environment
+variable will be set to "system" and the DBUS_SESSION_BUS_ADDRESS
+environment variable will be set to the address of the system bus
+(which is normally well known anyway).
+
+.PP
+Example: <type>session</type>
+
+.TP
+.I "<include>"
+
+.PP
+Include a file <include>filename.conf</include> at this point. If the
+filename is relative, it is located relative to the configuration file
+doing the including.
+
+.PP
+<include> has an optional attribute "ignore_missing=(yes|no)"
+which defaults to "no" if not provided. This attribute
+controls whether it's a fatal error for the included file
+to be absent.
+
+.TP
+.I "<includedir>"
+
+.PP
+Include all files in <includedir>foo.d</includedir> at this
+point. Files in the directory are included in undefined order.
+Only files ending in ".conf" are included.
+
+.PP
+This is intended to allow extension of the system bus by particular
+packages. For example, if CUPS wants to be able to send out
+notification of printer queue changes, it could install a file to
+@EXPANDED_SYSCONFDIR@/dbus-1/system.d that allowed all apps to receive
+this message and allowed the printer daemon user to send it.
+
+.TP
+.I "<user>"
+
+.PP
+The user account the daemon should run as, as either a username or a
+UID. If the daemon cannot change to this UID on startup, it will exit.
+If this element is not present, the daemon will not change or care
+about its UID.
+
+.PP
+The last <user> entry in the file "wins", the others are ignored.
+
+.PP
+The user is changed after the bus has completed initialization. So
+sockets etc. will be created before changing user, but no data will be
+read from clients before changing user. This means that sockets
+and PID files can be created in a location that requires root
+privileges for writing.
+
+.TP
+.I "<fork>"
+
+.PP
+If present, the bus daemon becomes a real daemon (forks
+into the background, etc.). This is generally used
+rather than the \-\-fork command line option.
+
+.TP
+.I "<keep_umask>"
+
+.PP
+If present, the bus daemon keeps its original umask when forking.
+This may be useful to avoid affecting the behavior of child processes.
+
+.TP
+.I "<listen>"
+
+.PP
+Add an address that the bus should listen on. The
+address is in the standard D-Bus format that contains
+a transport name plus possible parameters/options.
+
+.PP
+Example: <listen>unix:path=/tmp/foo</listen>
+
+.PP
+Example: <listen>tcp:host=localhost,port=1234</listen>
+
+.PP
+If there are multiple <listen> elements, then the bus listens
+on multiple addresses. The bus will pass its address to
+started services or other interested parties with
+the last address given in <listen> first. That is,
+apps will try to connect to the last <listen> address first.
+
+.PP
+tcp sockets can accept IPv4 addresses, IPv6 addresses or hostnames.
+If a hostname resolves to multiple addresses, the server will bind
+to all of them. The family=ipv4 or family=ipv6 options can be used
+to force it to bind to a subset of addresses
+
+.PP
+Example: <listen>tcp:host=localhost,port=0,family=ipv4</listen>
+
+.PP
+A special case is using a port number of zero (or omitting the port),
+which means to choose an available port selected by the operating
+system. The port number chosen can be obtained with the
+--print-address command line parameter and will be present in other
+cases where the server reports its own address, such as when
+DBUS_SESSION_BUS_ADDRESS is set.
+
+.PP
+Example: <listen>tcp:host=localhost,port=0</listen>
+
+.PP
+tcp addresses also allow a bind=hostname option, which will override
+the host option specifying what address to bind to, without changing
+the address reported by the bus. The bind option can also take a
+special name '*' to cause the bus to listen on all local address
+(INADDR_ANY). The specified host should be a valid name of the local
+machine or weird stuff will happen.
+
+.PP
+Example: <listen>tcp:host=localhost,bind=*,port=0</listen>
+
+.TP
+.I "<auth>"
+
+.PP
+Lists permitted authorization mechanisms. If this element doesn't
+exist, then all known mechanisms are allowed. If there are multiple
+<auth> elements, all the listed mechanisms are allowed. The order in
+which mechanisms are listed is not meaningful.
+
+.PP
+Example: <auth>EXTERNAL</auth>
+
+.PP
+Example: <auth>DBUS_COOKIE_SHA1</auth>
+
+.TP
+.I "<servicedir>"
+
+.PP
+Adds a directory to scan for .service files. Directories are
+scanned starting with the last to appear in the config file
+(the first .service file found that provides a particular
+service will be used).
+
+.PP
+Service files tell the bus how to automatically start a program.
+They are primarily used with the per-user-session bus,
+not the systemwide bus.
+
+.TP
+.I "<standard_session_servicedirs/>"
+
+.PP
+<standard_session_servicedirs/> is equivalent to specifying a series
+of <servicedir/> elements for each of the data directories in the "XDG
+Base Directory Specification" with the subdirectory "dbus-1/services",
+so for example "/usr/share/dbus-1/services" would be among the
+directories searched.
+
+.PP
+The "XDG Base Directory Specification" can be found at
+http://freedesktop.org/wiki/Standards/basedir-spec if it hasn't moved,
+otherwise try your favorite search engine.
+
+.PP
+The <standard_session_servicedirs/> option is only relevant to the
+per-user-session bus daemon defined in
+@EXPANDED_SYSCONFDIR@/dbus-1/session.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<standard_system_servicedirs/>"
+
+.PP
+<standard_system_servicedirs/> specifies the standard system-wide
+activation directories that should be searched for service files.
+This option defaults to @EXPANDED_DATADIR@/dbus-1/system-services.
+
+.PP
+The <standard_system_servicedirs/> option is only relevant to the
+per-system bus daemon defined in
+@EXPANDED_SYSCONFDIR@/dbus-1/system.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<servicehelper/>"
+
+.PP
+<servicehelper/> specifies the setuid helper that is used to launch
+system daemons with an alternate user. Typically this should be
+the dbus-daemon-launch-helper executable in located in libexec.
+
+.PP
+The <servicehelper/> option is only relevant to the per-system bus daemon
+defined in @EXPANDED_SYSCONFDIR@/dbus-1/system.conf. Putting it in any other
+configuration file would probably be nonsense.
+
+.TP
+.I "<limit>"
+
+.PP
+<limit> establishes a resource limit. For example:
+.nf
+ <limit name="max_message_size">64</limit>
+ <limit name="max_completed_connections">512</limit>
+.fi
+
+.PP
+The name attribute is mandatory.
+Available limit names are:
+.nf
+ "max_incoming_bytes" : total size in bytes of messages
+ incoming from a single connection
+ "max_outgoing_bytes" : total size in bytes of messages
+ queued up for a single connection
+ "max_message_size" : max size of a single message in
+ bytes
+ "service_start_timeout" : milliseconds (thousandths) until
+ a started service has to connect
+ "auth_timeout" : milliseconds (thousandths) a
+ connection is given to
+ authenticate
+ "max_completed_connections" : max number of authenticated connections
+ "max_incomplete_connections" : max number of unauthenticated
+ connections
+ "max_connections_per_user" : max number of completed connections from
+ the same user
+ "max_pending_service_starts" : max number of service launches in
+ progress at the same time
+ "max_names_per_connection" : max number of names a single
+ connection can own
+ "max_match_rules_per_connection": max number of match rules for a single
+ connection
+ "max_replies_per_connection" : max number of pending method
+ replies per connection
+ (number of calls-in-progress)
+ "reply_timeout" : milliseconds (thousandths)
+ until a method call times out
+.fi
+
+.PP
+The max incoming/outgoing queue sizes allow a new message to be queued
+if one byte remains below the max. So you can in fact exceed the max
+by max_message_size.
+
+.PP
+max_completed_connections divided by max_connections_per_user is the
+number of users that can work together to denial-of-service all other users by using
+up all connections on the systemwide bus.
+
+.PP
+Limits are normally only of interest on the systemwide bus, not the user session
+buses.
+
+.TP
+.I "<policy>"
+
+.PP
+The <policy> element defines a security policy to be applied to a particular
+set of connections to the bus. A policy is made up of
+<allow> and <deny> elements. Policies are normally used with the systemwide bus;
+they are analogous to a firewall in that they allow expected traffic
+and prevent unexpected traffic.
+
+.PP
+Currently, the system bus has a default-deny policy for sending method calls
+and owning bus names. Everything else, in particular reply messages, receive
+checks, and signals has a default allow policy.
+
+.PP
+In general, it is best to keep system services as small, targeted programs which
+run in their own process and provide a single bus name. Then, all that is needed
+is an <allow> rule for the "own" permission to let the process claim the bus
+name, and a "send_destination" rule to allow traffic from some or all uids to
+your service.
+
+.PP
+The <policy> element has one of four attributes:
+daemon.1.in
+.nf
+ context="(default|mandatory)"
+ at_console="(true|false)"
+ user="username or userid"
+ group="group name or gid"
+.fi
+
+.PP
+Policies are applied to a connection as follows:
+.nf
+ - all context="default" policies are applied
+ - all group="connection's user's group" policies are applied
+ in undefined order
+ - all user="connection's auth user" policies are applied
+ in undefined order
+ - all at_console="true" policies are applied
+ - all at_console="false" policies are applied
+ - all context="mandatory" policies are applied
+.fi
+
+.PP
+Policies applied later will override those applied earlier,
+when the policies overlap. Multiple policies with the same
+user/group/context are applied in the order they appear
+in the config file.
+
+.TP
+.I "<deny>"
+.I "<allow>"
+
+.PP
+A <deny> element appears below a <policy> element and prohibits some
+action. The <allow> element makes an exception to previous <deny>
+statements, and works just like <deny> but with the inverse meaning.
+
+.PP
+The possible attributes of these elements are:
+.nf
+ send_interface="interface_name"
+ send_member="method_or_signal_name"
+ send_error="error_name"
+ send_destination="name"
+ send_type="method_call" | "method_return" | "signal" | "error"
+ send_path="/path/name"
+
+ receive_interface="interface_name"
+ receive_member="method_or_signal_name"
+ receive_error="error_name"
+ receive_sender="name"
+ receive_type="method_call" | "method_return" | "signal" | "error"
+ receive_path="/path/name"
+
+ send_requested_reply="true" | "false"
+ receive_requested_reply="true" | "false"
+
+ eavesdrop="true" | "false"
+
+ own="name"
+ user="username"
+ group="groupname"
+.fi
+
+.PP
+Examples:
+.nf
+ <deny send_interface="org.freedesktop.System" send_member="Reboot"/>
+ <deny receive_interface="org.freedesktop.System" receive_member="Reboot"/>
+ <deny own="org.freedesktop.System"/>
+ <deny send_destination="org.freedesktop.System"/>
+ <deny receive_sender="org.freedesktop.System"/>
+ <deny user="john"/>
+ <deny group="enemies"/>
+.fi
+
+.PP
+The <deny> element's attributes determine whether the deny "matches" a
+particular action. If it matches, the action is denied (unless later
+rules in the config file allow it).
+
+.PP
+send_destination and receive_sender rules mean that messages may not be
+sent to or received from the *owner* of the given name, not that
+they may not be sent *to that name*. That is, if a connection
+owns services A, B, C, and sending to A is denied, sending to B or C
+will not work either.
+
+.PP
+The other send_* and receive_* attributes are purely textual/by-value
+matches against the given field in the message header.
+
+.PP
+"Eavesdropping" occurs when an application receives a message that
+was explicitly addressed to a name the application does not own, or
+is a reply to such a message. Eavesdropping thus only applies to
+messages that are addressed to services and replies to such messages
+(i.e. it does not apply to signals).
+
+.PP
+For <allow>, eavesdrop="true" indicates that the rule matches even
+when eavesdropping. eavesdrop="false" is the default and means that
+the rule only allows messages to go to their specified recipient.
+For <deny>, eavesdrop="true" indicates that the rule matches
+only when eavesdropping. eavesdrop="false" is the default for <deny>
+also, but here it means that the rule applies always, even when
+not eavesdropping. The eavesdrop attribute can only be combined with
+send and receive rules (with send_* and receive_* attributes).
+
+
+.PP
+The [send|receive]_requested_reply attribute works similarly to the eavesdrop
+attribute. It controls whether the <deny> or <allow> matches a reply
+that is expected (corresponds to a previous method call message).
+This attribute only makes sense for reply messages (errors and method
+returns), and is ignored for other message types.
+
+.PP
+For <allow>, [send|receive]_requested_reply="true" is the default and indicates that
+only requested replies are allowed by the
+rule. [send|receive]_requested_reply="false" means that the rule allows any reply
+even if unexpected.
+
+.PP
+For <deny>, [send|receive]_requested_reply="false" is the default but indicates that
+the rule matches only when the reply was not
+requested. [send|receive]_requested_reply="true" indicates that the rule applies
+always, regardless of pending reply state.
+
+.PP
+user and group denials mean that the given user or group may
+not connect to the message bus.
+
+.PP
+For "name", "username", "groupname", etc.
+the character "*" can be substituted, meaning "any." Complex globs
+like "foo.bar.*" aren't allowed for now because they'd be work to
+implement and maybe encourage sloppy security anyway.
+
+.PP
+It does not make sense to deny a user or group inside a <policy>
+for a user or group; user/group denials can only be inside
+context="default" or context="mandatory" policies.
+
+.PP
+A single <deny> rule may specify combinations of attributes such as
+send_destination and send_interface and send_type. In this case, the
+denial applies only if both attributes match the message being denied.
+e.g. <deny send_interface="foo.bar" send_destination="foo.blah"/> would
+deny messages with the given interface AND the given bus name.
+To get an OR effect you specify multiple <deny> rules.
+
+.PP
+You can't include both send_ and receive_ attributes on the same
+rule, since "whether the message can be sent" and "whether it can be
+received" are evaluated separately.
+
+.PP
+Be careful with send_interface/receive_interface, because the
+interface field in messages is optional. In particular, do NOT
+specify <deny send_interface="org.foo.Bar"/>! This will cause
+no-interface messages to be blocked for all services, which is
+almost certainly not what you intended. Always use rules of
+the form: <deny send_interface="org.foo.Bar" send_destination="org.foo.Service"/>
+
+.TP
+.I "<selinux>"
+
+.PP
+The <selinux> element contains settings related to Security Enhanced Linux.
+More details below.
+
+.TP
+.I "<associate>"
+
+.PP
+An <associate> element appears below an <selinux> element and
+creates a mapping. Right now only one kind of association is possible:
+.nf
+ <associate own="org.freedesktop.Foobar" context="foo_t"/>
+.fi
+
+.PP
+This means that if a connection asks to own the name
+"org.freedesktop.Foobar" then the source context will be the context
+of the connection and the target context will be "foo_t" - see the
+short discussion of SELinux below.
+
+.PP
+Note, the context here is the target context when requesting a name,
+NOT the context of the connection owning the name.
+
+.PP
+There's currently no way to set a default for owning any name, if
+we add this syntax it will look like:
+.nf
+ <associate own="*" context="foo_t"/>
+.fi
+If you find a reason this is useful, let the developers know.
+Right now the default will be the security context of the bus itself.
+
+.PP
+If two <associate> elements specify the same name, the element
+appearing later in the configuration file will be used.
+
+.SH SELinux
+
+.PP
+See http://www.nsa.gov/selinux/ for full details on SELinux. Some useful excerpts:
+
+.IP "" 8
+Every subject (process) and object (e.g. file, socket, IPC object,
+etc) in the system is assigned a collection of security attributes,
+known as a security context. A security context contains all of the
+security attributes associated with a particular subject or object
+that are relevant to the security policy.
+
+.IP "" 8
+In order to better encapsulate security contexts and to provide
+greater efficiency, the policy enforcement code of SELinux typically
+handles security identifiers (SIDs) rather than security contexts. A
+SID is an integer that is mapped by the security server to a security
+context at runtime.
+
+.IP "" 8
+When a security decision is required, the policy enforcement code
+passes a pair of SIDs (typically the SID of a subject and the SID of
+an object, but sometimes a pair of subject SIDs or a pair of object
+SIDs), and an object security class to the security server. The object
+security class indicates the kind of object, e.g. a process, a regular
+file, a directory, a TCP socket, etc.
+
+.IP "" 8
+Access decisions specify whether or not a permission is granted for a
+given pair of SIDs and class. Each object class has a set of
+associated permissions defined to control operations on objects with
+that class.
+
+.PP
+D-Bus performs SELinux security checks in two places.
+
+.PP
+First, any time a message is routed from one connection to another
+connection, the bus daemon will check permissions with the security context of
+the first connection as source, security context of the second connection
+as target, object class "dbus" and requested permission "send_msg".
+
+.PP
+If a security context is not available for a connection
+(impossible when using UNIX domain sockets), then the target
+context used is the context of the bus daemon itself.
+There is currently no way to change this default, because we're
+assuming that only UNIX domain sockets will be used to
+connect to the systemwide bus. If this changes, we'll
+probably add a way to set the default connection context.
+
+.PP
+Second, any time a connection asks to own a name,
+the bus daemon will check permissions with the security
+context of the connection as source, the security context specified
+for the name in the config file as target, object
+class "dbus" and requested permission "acquire_svc".
+
+.PP
+The security context for a bus name is specified with the
+<associate> element described earlier in this document.
+If a name has no security context associated in the
+configuration file, the security context of the bus daemon
+itself will be used.
+
+.SH DEBUGGING
+
+.PP
+If you're trying to figure out where your messages are going or why
+you aren't getting messages, there are several things you can try.
+
+.PP
+Remember that the system bus is heavily locked down and if you
+haven't installed a security policy file to allow your message
+through, it won't work. For the session bus, this is not a concern.
+
+.PP
+The simplest way to figure out what's happening on the bus is to run
+the \fIdbus-monitor\fP program, which comes with the D-Bus
+package. You can also send test messages with \fIdbus-send\fP. These
+programs have their own man pages.
+
+.PP
+If you want to know what the daemon itself is doing, you might consider
+running a separate copy of the daemon to test against. This will allow you
+to put the daemon under a debugger, or run it with verbose output, without
+messing up your real session and system daemons.
+
+.PP
+To run a separate test copy of the daemon, for example you might open a terminal
+and type:
+.nf
+ DBUS_VERBOSE=1 dbus-daemon --session --print-address
+.fi
+
+.PP
+The test daemon address will be printed when the daemon starts. You will need
+to copy-and-paste this address and use it as the value of the
+DBUS_SESSION_BUS_ADDRESS environment variable when you launch the applications
+you want to test. This will cause those applications to connect to your
+test bus instead of the DBUS_SESSION_BUS_ADDRESS of your real session bus.
+
+.PP
+DBUS_VERBOSE=1 will have NO EFFECT unless your copy of D-Bus
+was compiled with verbose mode enabled. This is not recommended in
+production builds due to performance impact. You may need to rebuild
+D-Bus if your copy was not built with debugging in mind. (DBUS_VERBOSE
+also affects the D-Bus library and thus applications using D-Bus; it may
+be useful to see verbose output on both the client side and from the daemon.)
+
+.PP
+If you want to get fancy, you can create a custom bus
+configuration for your test bus (see the session.conf and system.conf
+files that define the two default configurations for example). This
+would allow you to specify a different directory for .service files,
+for example.
+
+
+.SH AUTHOR
+See http://www.freedesktop.org/software/dbus/doc/AUTHORS
+
+.SH BUGS
+Please send bug reports to the D-Bus mailing list or bug tracker,
+see http://www.freedesktop.org/software/dbus/
diff --git a/bus/desktop-file.c b/bus/desktop-file.c
new file mode 100644
index 00000000..754a83c3
--- /dev/null
+++ b/bus/desktop-file.c
@@ -0,0 +1,800 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* desktop-file.c .desktop file parser
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * 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 <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-internals.h>
+#include "desktop-file.h"
+#include "utils.h"
+
+typedef struct
+{
+ char *key;
+ char *value;
+} BusDesktopFileLine;
+
+typedef struct
+{
+ char *section_name;
+
+ int n_lines;
+ BusDesktopFileLine *lines;
+ int n_allocated_lines;
+} BusDesktopFileSection;
+
+struct BusDesktopFile
+{
+ int n_sections;
+ BusDesktopFileSection *sections;
+ int n_allocated_sections;
+};
+
+/**
+ * Parser for service files.
+ */
+typedef struct
+{
+ DBusString data; /**< The data from the file */
+
+ BusDesktopFile *desktop_file; /**< The resulting object */
+ int current_section; /**< The current section being parsed */
+
+ int pos; /**< Current position */
+ int len; /**< Length */
+ int line_num; /**< Current line number */
+
+} BusDesktopFileParser;
+
+#define VALID_KEY_CHAR 1
+#define VALID_LOCALE_CHAR 2
+static unsigned char valid[256] = {
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x3 , 0x2 , 0x0 ,
+ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
+ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x2 ,
+ 0x0 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 ,
+ 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x3 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+ 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 ,
+};
+
+static void report_error (BusDesktopFileParser *parser,
+ char *message,
+ const char *error_name,
+ DBusError *error);
+
+static void
+parser_free (BusDesktopFileParser *parser)
+{
+ bus_desktop_file_free (parser->desktop_file);
+
+ _dbus_string_free (&parser->data);
+}
+
+static void
+bus_desktop_file_line_free (BusDesktopFileLine *line)
+{
+ dbus_free (line->key);
+ dbus_free (line->value);
+}
+
+static void
+bus_desktop_file_section_free (BusDesktopFileSection *section)
+{
+ int i;
+
+ for (i = 0; i < section->n_lines; i++)
+ bus_desktop_file_line_free (&section->lines[i]);
+
+ dbus_free (section->lines);
+ dbus_free (section->section_name);
+}
+
+void
+bus_desktop_file_free (BusDesktopFile *desktop_file)
+{
+ int i;
+
+ for (i = 0; i < desktop_file->n_sections; i++)
+ bus_desktop_file_section_free (&desktop_file->sections[i]);
+ dbus_free (desktop_file->sections);
+
+ dbus_free (desktop_file);
+}
+
+static dbus_bool_t
+grow_lines_in_section (BusDesktopFileSection *section)
+{
+ BusDesktopFileLine *lines;
+
+ int new_n_lines;
+
+ if (section->n_allocated_lines == 0)
+ new_n_lines = 1;
+ else
+ new_n_lines = section->n_allocated_lines*2;
+
+ lines = dbus_realloc (section->lines,
+ sizeof (BusDesktopFileLine) * new_n_lines);
+
+ if (lines == NULL)
+ return FALSE;
+
+ section->lines = lines;
+ section->n_allocated_lines = new_n_lines;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+grow_sections (BusDesktopFile *desktop_file)
+{
+ int new_n_sections;
+ BusDesktopFileSection *sections;
+
+ if (desktop_file->n_allocated_sections == 0)
+ new_n_sections = 1;
+ else
+ new_n_sections = desktop_file->n_allocated_sections*2;
+
+ sections = dbus_realloc (desktop_file->sections,
+ sizeof (BusDesktopFileSection) * new_n_sections);
+ if (sections == NULL)
+ return FALSE;
+
+ desktop_file->sections = sections;
+
+ desktop_file->n_allocated_sections = new_n_sections;
+
+ return TRUE;
+}
+
+static char *
+unescape_string (BusDesktopFileParser *parser,
+ const DBusString *str,
+ int pos,
+ int end_pos,
+ DBusError *error)
+{
+ char *retval, *q;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* len + 1 is enough, because unescaping never makes the
+ * string longer
+ */
+ retval = dbus_malloc (end_pos - pos + 1);
+ if (retval == NULL)
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ q = retval;
+
+ while (pos < end_pos)
+ {
+ if (_dbus_string_get_byte (str, pos) == 0)
+ {
+ /* Found an embedded null */
+ dbus_free (retval);
+ report_error (parser, "Text to be unescaped contains embedded nul",
+ BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
+ return NULL;
+ }
+
+ if (_dbus_string_get_byte (str, pos) == '\\')
+ {
+ pos ++;
+
+ if (pos >= end_pos)
+ {
+ /* Escape at end of string */
+ dbus_free (retval);
+ report_error (parser, "Text to be unescaped ended in \\",
+ BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
+ return NULL;
+ }
+
+ switch (_dbus_string_get_byte (str, pos))
+ {
+ case 's':
+ *q++ = ' ';
+ break;
+ case 't':
+ *q++ = '\t';
+ break;
+ case 'n':
+ *q++ = '\n';
+ break;
+ case 'r':
+ *q++ = '\r';
+ break;
+ case '\\':
+ *q++ = '\\';
+ break;
+ default:
+ /* Invalid escape code */
+ dbus_free (retval);
+ report_error (parser, "Text to be unescaped had invalid escape sequence",
+ BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES, error);
+ return NULL;
+ }
+ pos++;
+ }
+ else
+ {
+ *q++ =_dbus_string_get_byte (str, pos);
+
+ pos++;
+ }
+ }
+
+ *q = 0;
+
+ return retval;
+}
+
+static BusDesktopFileSection*
+new_section (BusDesktopFile *desktop_file,
+ const char *name)
+{
+ int n;
+ char *name_copy;
+
+ if (desktop_file->n_allocated_sections == desktop_file->n_sections)
+ {
+ if (!grow_sections (desktop_file))
+ return NULL;
+ }
+
+ name_copy = _dbus_strdup (name);
+ if (name_copy == NULL)
+ return NULL;
+
+ n = desktop_file->n_sections;
+ desktop_file->sections[n].section_name = name_copy;
+
+ desktop_file->sections[n].n_lines = 0;
+ desktop_file->sections[n].lines = NULL;
+ desktop_file->sections[n].n_allocated_lines = 0;
+
+ if (!grow_lines_in_section (&desktop_file->sections[n]))
+ {
+ dbus_free (desktop_file->sections[n].section_name);
+ desktop_file->sections[n].section_name = NULL;
+ return NULL;
+ }
+
+ desktop_file->n_sections += 1;
+
+ return &desktop_file->sections[n];
+}
+
+static BusDesktopFileSection*
+open_section (BusDesktopFileParser *parser,
+ char *name)
+{
+ BusDesktopFileSection *section;
+
+ section = new_section (parser->desktop_file, name);
+ if (section == NULL)
+ return NULL;
+
+ parser->current_section = parser->desktop_file->n_sections - 1;
+ _dbus_assert (&parser->desktop_file->sections[parser->current_section] == section);
+
+ return section;
+}
+
+static BusDesktopFileLine *
+new_line (BusDesktopFileParser *parser)
+{
+ BusDesktopFileSection *section;
+ BusDesktopFileLine *line;
+
+ section = &parser->desktop_file->sections[parser->current_section];
+
+ if (section->n_allocated_lines == section->n_lines)
+ {
+ if (!grow_lines_in_section (section))
+ return NULL;
+ }
+
+ line = &section->lines[section->n_lines++];
+
+ memset (line, 0, sizeof (BusDesktopFileLine));
+
+ return line;
+}
+
+static dbus_bool_t
+is_blank_line (BusDesktopFileParser *parser)
+{
+ int p;
+ char c;
+
+ p = parser->pos;
+
+ c = _dbus_string_get_byte (&parser->data, p);
+
+ while (c && c != '\n')
+ {
+ if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'))
+ return FALSE;
+
+ p++;
+ c = _dbus_string_get_byte (&parser->data, p);
+ }
+
+ return TRUE;
+}
+
+static void
+parse_comment_or_blank (BusDesktopFileParser *parser)
+{
+ int line_end, eol_len;
+
+ if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
+ line_end = parser->len;
+
+ if (line_end == parser->len)
+ parser->pos = parser->len;
+ else
+ parser->pos = line_end + eol_len;
+
+ parser->line_num += 1;
+}
+
+static dbus_bool_t
+is_valid_section_name (const char *name)
+{
+ /* 5. Group names may contain all ASCII characters except for control characters and '[' and ']'. */
+
+ while (*name)
+ {
+ if (!((*name >= 'A' && *name <= 'Z') || (*name >= 'a' || *name <= 'z') ||
+ *name == '\n' || *name == '\t'))
+ return FALSE;
+
+ name++;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+parse_section_start (BusDesktopFileParser *parser, DBusError *error)
+{
+ int line_end, eol_len;
+ char *section_name;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
+ line_end = parser->len;
+
+ if (line_end - parser->pos <= 2 ||
+ _dbus_string_get_byte (&parser->data, line_end - 1) != ']')
+ {
+ report_error (parser, "Invalid syntax for section header", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
+ parser_free (parser);
+ return FALSE;
+ }
+
+ section_name = unescape_string (parser,
+ &parser->data, parser->pos + 1, line_end - 1,
+ error);
+
+ if (section_name == NULL)
+ {
+ parser_free (parser);
+ return FALSE;
+ }
+
+ if (!is_valid_section_name (section_name))
+ {
+ report_error (parser, "Invalid characters in section name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
+ parser_free (parser);
+ dbus_free (section_name);
+ return FALSE;
+ }
+
+ if (open_section (parser, section_name) == NULL)
+ {
+ dbus_free (section_name);
+ parser_free (parser);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (line_end == parser->len)
+ parser->pos = parser->len;
+ else
+ parser->pos = line_end + eol_len;
+
+ parser->line_num += 1;
+
+ dbus_free (section_name);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+parse_key_value (BusDesktopFileParser *parser, DBusError *error)
+{
+ int line_end, eol_len;
+ int key_start, key_end;
+ int value_start;
+ int p;
+ char *value, *tmp;
+ DBusString key;
+ BusDesktopFileLine *line;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_find_eol (&parser->data, parser->pos, &line_end, &eol_len))
+ line_end = parser->len;
+
+ p = parser->pos;
+ key_start = p;
+ while (p < line_end &&
+ (valid[_dbus_string_get_byte (&parser->data, p)] & VALID_KEY_CHAR))
+ p++;
+ key_end = p;
+
+ if (key_start == key_end)
+ {
+ report_error (parser, "Empty key name", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
+ parser_free (parser);
+ return FALSE;
+ }
+
+ /* We ignore locales for now */
+ if (p < line_end && _dbus_string_get_byte (&parser->data, p) == '[')
+ {
+ if (line_end == parser->len)
+ parser->pos = parser->len;
+ else
+ parser->pos = line_end + eol_len;
+
+ parser->line_num += 1;
+
+ return TRUE;
+ }
+
+ /* Skip space before '=' */
+ while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
+ p++;
+
+ if (p < line_end && _dbus_string_get_byte (&parser->data, p) != '=')
+ {
+ report_error (parser, "Invalid characters in key name", BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS, error);
+ parser_free (parser);
+ return FALSE;
+ }
+
+ if (p == line_end)
+ {
+ report_error (parser, "No '=' in key/value pair", BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX, error);
+ parser_free (parser);
+ return FALSE;
+ }
+
+ /* Skip the '=' */
+ p++;
+
+ /* Skip space after '=' */
+ while (p < line_end && _dbus_string_get_byte (&parser->data, p) == ' ')
+ p++;
+
+ value_start = p;
+
+ value = unescape_string (parser, &parser->data, value_start, line_end, error);
+ if (value == NULL)
+ {
+ parser_free (parser);
+ return FALSE;
+ }
+
+ line = new_line (parser);
+ if (line == NULL)
+ {
+ dbus_free (value);
+ parser_free (parser);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&key))
+ {
+ dbus_free (value);
+ parser_free (parser);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_copy_len (&parser->data, key_start, key_end - key_start,
+ &key, 0))
+ {
+ _dbus_string_free (&key);
+ dbus_free (value);
+ parser_free (parser);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_steal_data (&key, &tmp))
+ {
+ _dbus_string_free (&key);
+ dbus_free (value);
+ parser_free (parser);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_string_free (&key);
+
+ line->key = tmp;
+ line->value = value;
+
+ if (line_end == parser->len)
+ parser->pos = parser->len;
+ else
+ parser->pos = line_end + eol_len;
+
+ parser->line_num += 1;
+
+ return TRUE;
+}
+
+static void
+report_error (BusDesktopFileParser *parser,
+ char *message,
+ const char *error_name,
+ DBusError *error)
+{
+ const char *section_name = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (parser->current_section != -1)
+ section_name = parser->desktop_file->sections[parser->current_section].section_name;
+
+ if (section_name)
+ dbus_set_error (error, error_name,
+ "Error in section %s at line %d: %s\n", section_name, parser->line_num, message);
+ else
+ dbus_set_error (error, error_name,
+ "Error at line %d: %s\n", parser->line_num, message);
+}
+
+#if 0
+static void
+dump_desktop_file (BusDesktopFile *file)
+{
+ int i;
+
+ for (i = 0; i < file->n_sections; i++)
+ {
+ int j;
+
+ printf ("[%s]\n", file->sections[i].section_name);
+
+ for (j = 0; j < file->sections[i].n_lines; j++)
+ {
+ printf ("%s=%s\n", file->sections[i].lines[j].key,
+ file->sections[i].lines[j].value);
+ }
+ }
+}
+#endif
+
+BusDesktopFile*
+bus_desktop_file_load (DBusString *filename,
+ DBusError *error)
+{
+ DBusString str;
+ BusDesktopFileParser parser;
+ DBusStat sb;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* Clearly there's a race here, but it's just to make it unlikely
+ * that we do something silly, we still handle doing it below.
+ */
+ if (!_dbus_stat (filename, &sb, error))
+ return NULL;
+
+ if (sb.size > _DBUS_ONE_KILOBYTE * 128)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Desktop file size (%ld bytes) is too large", (long) sb.size);
+ return NULL;
+ }
+
+ if (!_dbus_string_init (&str))
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ if (!_dbus_file_get_contents (&str, filename, error))
+ {
+ _dbus_string_free (&str);
+ return NULL;
+ }
+
+ if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str)))
+ {
+ _dbus_string_free (&str);
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "invalid UTF-8");
+ return NULL;
+ }
+
+ parser.desktop_file = dbus_new0 (BusDesktopFile, 1);
+ if (parser.desktop_file == NULL)
+ {
+ _dbus_string_free (&str);
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ parser.data = str;
+ parser.line_num = 1;
+ parser.pos = 0;
+ parser.len = _dbus_string_get_length (&parser.data);
+ parser.current_section = -1;
+
+ while (parser.pos < parser.len)
+ {
+ if (_dbus_string_get_byte (&parser.data, parser.pos) == '[')
+ {
+ if (!parse_section_start (&parser, error))
+ {
+ return NULL;
+ }
+ }
+ else if (is_blank_line (&parser) ||
+ _dbus_string_get_byte (&parser.data, parser.pos) == '#')
+ parse_comment_or_blank (&parser);
+ else
+ {
+ if (!parse_key_value (&parser, error))
+ {
+ return NULL;
+ }
+ }
+ }
+
+ _dbus_string_free (&parser.data);
+
+ return parser.desktop_file;
+}
+
+static BusDesktopFileSection *
+lookup_section (BusDesktopFile *desktop_file,
+ const char *section_name)
+{
+ BusDesktopFileSection *section;
+ int i;
+
+ if (section_name == NULL)
+ return NULL;
+
+ for (i = 0; i < desktop_file->n_sections; i ++)
+ {
+ section = &desktop_file->sections[i];
+
+ if (strcmp (section->section_name, section_name) == 0)
+ return section;
+ }
+
+ return NULL;
+}
+
+static BusDesktopFileLine *
+lookup_line (BusDesktopFile *desktop_file,
+ BusDesktopFileSection *section,
+ const char *keyname)
+{
+ BusDesktopFileLine *line;
+ int i;
+
+ for (i = 0; i < section->n_lines; i++)
+ {
+ line = &section->lines[i];
+
+ if (strcmp (line->key, keyname) == 0)
+ return line;
+ }
+
+ return NULL;
+}
+
+dbus_bool_t
+bus_desktop_file_get_raw (BusDesktopFile *desktop_file,
+ const char *section_name,
+ const char *keyname,
+ const char **val)
+{
+ BusDesktopFileSection *section;
+ BusDesktopFileLine *line;
+
+ *val = NULL;
+
+ section = lookup_section (desktop_file, section_name);
+
+ if (!section)
+ return FALSE;
+
+ line = lookup_line (desktop_file,
+ section,
+ keyname);
+
+ if (!line)
+ return FALSE;
+
+ *val = line->value;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_desktop_file_get_string (BusDesktopFile *desktop_file,
+ const char *section,
+ const char *keyname,
+ char **val,
+ DBusError *error)
+{
+ const char *raw;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ *val = NULL;
+
+ if (!bus_desktop_file_get_raw (desktop_file, section, keyname, &raw))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "No \"%s\" key in .service file\n", keyname);
+ return FALSE;
+ }
+
+ *val = _dbus_strdup (raw);
+
+ if (*val == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/bus/desktop-file.h b/bus/desktop-file.h
new file mode 100644
index 00000000..7f43458a
--- /dev/null
+++ b/bus/desktop-file.h
@@ -0,0 +1,56 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* desktop-file.h .desktop file parser
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (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
+ *
+ */
+#ifndef BUS_DESKTOP_FILE_H
+#define BUS_DESKTOP_FILE_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+
+#define BUS_DESKTOP_PARSE_ERROR_INVALID_SYNTAX "org.freedesktop.DBus.DesktopParseError.InvalidSyntax"
+#define BUS_DESKTOP_PARSE_ERROR_INVALID_ESCAPES "org.freedesktop.DBus.DesktopParseError.InvalidEscapes"
+#define BUS_DESKTOP_PARSE_ERROR_INVALID_CHARS "org.freedesktop.DBus.DesktopParseError.InvalidChars"
+
+#define DBUS_SERVICE_SECTION "D-BUS Service"
+#define DBUS_SERVICE_NAME "Name"
+#define DBUS_SERVICE_EXEC "Exec"
+#define DBUS_SERVICE_USER "User"
+#define DBUS_SERVICE_GROUP "Group"
+
+typedef struct BusDesktopFile BusDesktopFile;
+
+BusDesktopFile *bus_desktop_file_load (DBusString *filename,
+ DBusError *error);
+void bus_desktop_file_free (BusDesktopFile *file);
+
+dbus_bool_t bus_desktop_file_get_raw (BusDesktopFile *desktop_file,
+ const char *section_name,
+ const char *keyname,
+ const char **val);
+dbus_bool_t bus_desktop_file_get_string (BusDesktopFile *desktop_file,
+ const char *section,
+ const char *keyname,
+ char **val,
+ DBusError *error);
+
+
+#endif /* BUS_DESKTOP_FILE_H */
diff --git a/bus/dir-watch-default.c b/bus/dir-watch-default.c
new file mode 100644
index 00000000..8e457eb6
--- /dev/null
+++ b/bus/dir-watch-default.c
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dir-watch-default.c OS specific directory change notification for message bus
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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/dbus-internals.h>
+#include "dir-watch.h"
+
+
+/* NoOp */
+
+void
+bus_watch_directory (const char *dir, BusContext *context)
+{
+}
+
+void
+bus_set_watched_dirs (BusContext *context, DBusList **directories)
+{
+}
diff --git a/bus/dir-watch-dnotify.c b/bus/dir-watch-dnotify.c
new file mode 100644
index 00000000..b38d7d19
--- /dev/null
+++ b/bus/dir-watch-dnotify.c
@@ -0,0 +1,93 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dir-watch-dnotify.c OS specific directory change notification for message bus
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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>
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <dbus/dbus-internals.h>
+#include "dir-watch.h"
+
+#define MAX_DIRS_TO_WATCH 128
+
+/* use a static array to avoid handling OOM */
+static int fds[MAX_DIRS_TO_WATCH];
+static int num_fds = 0;
+
+void
+bus_watch_directory (const char *dir, BusContext *context)
+{
+ int fd;
+
+ _dbus_assert (dir != NULL);
+
+ if (num_fds >= MAX_DIRS_TO_WATCH )
+ {
+ _dbus_warn ("Cannot watch config directory '%s'. Already watching %d directories\n", dir, MAX_DIRS_TO_WATCH);
+ goto out;
+ }
+
+ fd = open (dir, O_RDONLY);
+ if (fd < 0)
+ {
+ _dbus_warn ("Cannot open directory '%s'; error '%s'\n", dir, _dbus_strerror (errno));
+ goto out;
+ }
+
+ if (fcntl (fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_RENAME|DN_MODIFY) == -1)
+ {
+ _dbus_warn ("Cannot setup D_NOTIFY for '%s' error '%s'\n", dir, _dbus_strerror (errno));
+ close (fd);
+ goto out;
+ }
+
+ fds[num_fds++] = fd;
+ _dbus_verbose ("Added watch on config directory '%s'\n", dir);
+
+ out:
+ ;
+}
+
+void
+bus_drop_all_directory_watches (void)
+{
+ int i;
+
+ _dbus_verbose ("Dropping all watches on config directories\n");
+
+ for (i = 0; i < num_fds; i++)
+ {
+ if (close (fds[i]) != 0)
+ {
+ _dbus_verbose ("Error closing fd %d for config directory watch\n", fds[i]);
+ }
+ }
+
+ num_fds = 0;
+}
diff --git a/bus/dir-watch-inotify.c b/bus/dir-watch-inotify.c
new file mode 100644
index 00000000..094993bb
--- /dev/null
+++ b/bus/dir-watch-inotify.c
@@ -0,0 +1,279 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dir-watch-inotify.c OS specific directory change notification for message bus
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * (c) 2006 Mandriva
+ *
+ * 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>
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-watch.h>
+#include "dir-watch.h"
+
+#define MAX_DIRS_TO_WATCH 128
+#define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
+#define INOTIFY_BUF_LEN (1024 * (INOTIFY_EVENT_SIZE + 16))
+
+/* use a static array to avoid handling OOM */
+static int wds[MAX_DIRS_TO_WATCH];
+static char *dirs[MAX_DIRS_TO_WATCH];
+static int num_wds = 0;
+static int inotify_fd = -1;
+static DBusWatch *watch = NULL;
+static DBusLoop *loop = NULL;
+
+static dbus_bool_t
+_inotify_watch_callback (DBusWatch *watch, unsigned int condition, void *data)
+{
+ return dbus_watch_handle (watch, condition);
+}
+
+static dbus_bool_t
+_handle_inotify_watch (DBusWatch *passed_watch, unsigned int flags, void *data)
+{
+ char buffer[INOTIFY_BUF_LEN];
+ ssize_t ret = 0;
+ int i = 0;
+ pid_t pid;
+ dbus_bool_t have_change = FALSE;
+
+ ret = read (inotify_fd, buffer, INOTIFY_BUF_LEN);
+ if (ret < 0)
+ _dbus_verbose ("Error reading inotify event: '%s'\n", _dbus_strerror(errno));
+ else if (!ret)
+ _dbus_verbose ("Error reading inotify event: buffer too small\n");
+
+ while (i < ret)
+ {
+ struct inotify_event *ev;
+ pid = _dbus_getpid ();
+
+ ev = (struct inotify_event *) &buffer[i];
+ i += INOTIFY_EVENT_SIZE + ev->len;
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ if (ev->len)
+ _dbus_verbose ("event name: '%s'\n", ev->name);
+ _dbus_verbose ("inotify event: wd=%d mask=%u cookie=%u len=%u\n", ev->wd, ev->mask, ev->cookie, ev->len);
+#endif
+ _dbus_verbose ("Sending SIGHUP signal on reception of a inotify event\n");
+ have_change = TRUE;
+ }
+ if (have_change)
+ (void) kill (pid, SIGHUP);
+
+ return TRUE;
+}
+
+#include <stdio.h>
+
+static void
+_set_watched_dirs_internal (DBusList **directories)
+{
+ int new_wds[MAX_DIRS_TO_WATCH];
+ char *new_dirs[MAX_DIRS_TO_WATCH];
+ DBusList *link;
+ int i, j, wd;
+
+ for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
+ {
+ new_wds[i] = -1;
+ new_dirs[i] = NULL;
+ }
+
+ i = 0;
+ link = _dbus_list_get_first_link (directories);
+ while (link != NULL)
+ {
+ new_dirs[i++] = (char *)link->data;
+ link = _dbus_list_get_next_link (directories, link);
+ }
+
+ /* Look for directories in both the old and new sets, if
+ * we find one, move its data into the new set.
+ */
+ for (i = 0; new_dirs[i]; i++)
+ {
+ for (j = 0; j < num_wds; j++)
+ {
+ if (dirs[j] && strcmp (new_dirs[i], dirs[j]) == 0)
+ {
+ new_wds[i] = wds[j];
+ new_dirs[i] = dirs[j];
+ wds[j] = -1;
+ dirs[j] = NULL;
+ break;
+ }
+ }
+ }
+
+ /* Any directories we find in "wds" with a nonzero fd must
+ * not be in the new set, so perform cleanup now.
+ */
+ for (j = 0; j < num_wds; j++)
+ {
+ if (wds[j] != -1)
+ {
+ inotify_rm_watch (inotify_fd, wds[j]);
+ dbus_free (dirs[j]);
+ wds[j] = -1;
+ dirs[j] = NULL;
+ }
+ }
+
+ for (i = 0; new_dirs[i]; i++)
+ {
+ if (new_wds[i] == -1)
+ {
+ /* FIXME - less lame error handling for failing to add a watch; we may need to sleep. */
+ wd = inotify_add_watch (inotify_fd, new_dirs[i], IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM);
+ if (wd < 0)
+ {
+ /* Not all service directories need to exist. */
+ if (errno != ENOENT)
+ {
+ _dbus_warn ("Cannot setup inotify for '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno));
+ goto out;
+ }
+ else
+ {
+ new_wds[i] = -1;
+ new_dirs[i] = NULL;
+ continue;
+ }
+ }
+ new_wds[i] = wd;
+ new_dirs[i] = _dbus_strdup (new_dirs[i]);
+ if (!new_dirs[i])
+ {
+ /* FIXME have less lame handling for OOM, we just silently fail to
+ * watch. (In reality though, the whole OOM handling in dbus is stupid
+ * but we won't go into that in this comment =) )
+ */
+ inotify_rm_watch (inotify_fd, wd);
+ new_wds[i] = -1;
+ }
+ }
+ }
+
+ num_wds = i;
+
+ for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
+ {
+ wds[i] = new_wds[i];
+ dirs[i] = new_dirs[i];
+ }
+
+ out:;
+}
+
+#include <stdio.h>
+static void
+_shutdown_inotify (void *data)
+{
+ DBusList *empty = NULL;
+
+ if (inotify_fd == -1)
+ return;
+
+ _set_watched_dirs_internal (&empty);
+
+ close (inotify_fd);
+ inotify_fd = -1;
+ if (watch != NULL)
+ {
+ _dbus_loop_remove_watch (loop, watch, _inotify_watch_callback, NULL);
+ _dbus_watch_unref (watch);
+ _dbus_loop_unref (loop);
+ }
+ watch = NULL;
+ loop = NULL;
+}
+
+static int
+_init_inotify (BusContext *context)
+{
+ int ret = 0;
+
+ if (inotify_fd == -1)
+ {
+#ifdef HAVE_INOTIFY_INIT1
+ inotify_fd = inotify_init1 (IN_CLOEXEC);
+ /* This ensures we still run on older Linux kernels.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=23957
+ */
+ if (inotify_fd < 0)
+ inotify_fd = inotify_init ();
+#else
+ inotify_fd = inotify_init ();
+#endif
+ if (inotify_fd <= 0)
+ {
+ _dbus_warn ("Cannot initialize inotify\n");
+ goto out;
+ }
+ loop = bus_context_get_loop (context);
+ _dbus_loop_ref (loop);
+
+ watch = _dbus_watch_new (inotify_fd, DBUS_WATCH_READABLE, TRUE,
+ _handle_inotify_watch, NULL, NULL);
+
+ if (watch == NULL)
+ {
+ _dbus_warn ("Unable to create inotify watch\n");
+ goto out;
+ }
+
+ if (!_dbus_loop_add_watch (loop, watch, _inotify_watch_callback,
+ NULL, NULL))
+ {
+ _dbus_warn ("Unable to add reload watch to main loop");
+ _dbus_watch_unref (watch);
+ watch = NULL;
+ goto out;
+ }
+
+ _dbus_register_shutdown_func (_shutdown_inotify, NULL);
+ }
+
+ ret = 1;
+
+out:
+ return ret;
+}
+
+void
+bus_set_watched_dirs (BusContext *context, DBusList **directories)
+{
+ if (!_init_inotify (context))
+ return;
+
+ _set_watched_dirs_internal (directories);
+}
diff --git a/bus/dir-watch-kqueue.c b/bus/dir-watch-kqueue.c
new file mode 100644
index 00000000..4a01b748
--- /dev/null
+++ b/bus/dir-watch-kqueue.c
@@ -0,0 +1,255 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dir-watch-kqueue.c OS specific directory change notification for message bus
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "bus.h"
+#include <dbus/dbus-watch.h>
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-list.h>
+#include "dir-watch.h"
+
+#define MAX_DIRS_TO_WATCH 128
+
+static int kq = -1;
+static int fds[MAX_DIRS_TO_WATCH];
+static char *dirs[MAX_DIRS_TO_WATCH];
+static int num_fds = 0;
+static DBusWatch *watch = NULL;
+static DBusLoop *loop = NULL;
+
+static dbus_bool_t
+_kqueue_watch_callback (DBusWatch *watch, unsigned int condition, void *data)
+{
+ return dbus_watch_handle (watch, condition);
+}
+
+static dbus_bool_t
+_handle_kqueue_watch (DBusWatch *watch, unsigned int flags, void *data)
+{
+ struct kevent ev;
+ struct timespec nullts = { 0, 0 };
+ int res;
+ pid_t pid;
+
+ res = kevent (kq, NULL, 0, &ev, 1, &nullts);
+
+ /* Sleep for half a second to avoid a race when files are install(1)'d
+ * to system.d. */
+ usleep(500000);
+
+ if (res > 0)
+ {
+ pid = getpid ();
+ _dbus_verbose ("Sending SIGHUP signal on reception of a kevent\n");
+ (void) kill (pid, SIGHUP);
+ }
+ else if (res < 0 && errno == EBADF)
+ {
+ kq = -1;
+ if (watch != NULL)
+ {
+ _dbus_loop_remove_watch (loop, watch, _kqueue_watch_callback, NULL);
+ _dbus_watch_unref (watch);
+ watch = NULL;
+ }
+ pid = getpid ();
+ _dbus_verbose ("Sending SIGHUP signal since kqueue has been closed\n");
+ (void) kill (pid, SIGHUP);
+ }
+
+ return TRUE;
+}
+
+static int
+_init_kqueue (BusContext *context)
+{
+ int ret = 0;
+
+ if (kq < 0)
+ {
+
+ kq = kqueue ();
+ if (kq < 0)
+ {
+ _dbus_warn ("Cannot create kqueue; error '%s'\n", _dbus_strerror (errno));
+ goto out;
+ }
+
+ loop = bus_context_get_loop (context);
+
+ watch = _dbus_watch_new (kq, DBUS_WATCH_READABLE, TRUE,
+ _handle_kqueue_watch, NULL, NULL);
+
+ if (watch == NULL)
+ {
+ _dbus_warn ("Unable to create kqueue watch\n");
+ close (kq);
+ kq = -1;
+ goto out;
+ }
+
+ if (!_dbus_loop_add_watch (loop, watch, _kqueue_watch_callback,
+ NULL, NULL))
+ {
+ _dbus_warn ("Unable to add reload watch to main loop");
+ close (kq);
+ kq = -1;
+ _dbus_watch_unref (watch);
+ watch = NULL;
+ goto out;
+ }
+ }
+
+ ret = 1;
+
+out:
+ return ret;
+}
+
+void
+bus_set_watched_dirs (BusContext *context, DBusList **directories)
+{
+ int new_fds[MAX_DIRS_TO_WATCH];
+ char *new_dirs[MAX_DIRS_TO_WATCH];
+ DBusList *link;
+ int i, j, f, fd;
+ struct kevent ev;
+
+ if (!_init_kqueue (context))
+ goto out;
+
+ for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
+ {
+ new_fds[i] = -1;
+ new_dirs[i] = NULL;
+ }
+
+ i = 0;
+ link = _dbus_list_get_first_link (directories);
+ while (link != NULL)
+ {
+ new_dirs[i++] = (char *)link->data;
+ link = _dbus_list_get_next_link (directories, link);
+ }
+
+ /* Look for directories in both the old and new sets, if
+ * we find one, move its data into the new set.
+ */
+ for (i = 0; new_dirs[i]; i++)
+ {
+ for (j = 0; i < num_fds; j++)
+ {
+ if (dirs[j] && strcmp (new_dirs[i], dirs[j]) == 0)
+ {
+ new_fds[i] = fds[j];
+ new_dirs[i] = dirs[j];
+ fds[j] = -1;
+ dirs[j] = NULL;
+ break;
+ }
+ }
+ }
+
+ /* Any directory we find in "fds" with a nonzero fd must
+ * not be in the new set, so perform cleanup now.
+ */
+ for (j = 0; j < num_fds; j++)
+ {
+ if (fds[j] != -1)
+ {
+ close (fds[j]);
+ dbus_free (dirs[j]);
+ fds[j] = -1;
+ dirs[j] = NULL;
+ }
+ }
+
+ for (i = 0; new_dirs[i]; i++)
+ {
+ if (new_fds[i] == -1)
+ {
+ /* FIXME - less lame error handling for failing to add a watch;
+ * we may need to sleep.
+ */
+ fd = open (new_dirs[i], O_RDONLY);
+ if (fd < 0)
+ {
+ if (errno != ENOENT)
+ {
+ _dbus_warn ("Cannot open directory '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno));
+ goto out;
+ }
+ else
+ {
+ new_fds[i] = -1;
+ new_dirs[i] = NULL;
+ continue;
+ }
+ }
+
+ EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_RENAME, 0, 0);
+ if (kevent (kq, &ev, 1, NULL, 0, NULL) == -1)
+ {
+ _dbus_warn ("Cannot setup a kevent for '%s'; error '%s'\n", new_dirs[i], _dbus_strerror (errno));
+ close (fd);
+ goto out;
+ }
+
+ new_fds[i] = fd;
+ new_dirs[i] = _dbus_strdup (new_dirs[i]);
+ if (!new_dirs[i])
+ {
+ /* FIXME have less lame handling for OOM, we just silently fail to
+ * watch. (In reality though, the whole OOM handling in dbus is
+ * stupid but we won't go into that in this comment =) )
+ */
+ close (fd);
+ new_fds[i] = -1;
+ }
+ }
+ }
+
+ num_fds = i;
+
+ for (i = 0; i < MAX_DIRS_TO_WATCH; i++)
+ {
+ fds[i] = new_fds[i];
+ dirs[i] = new_dirs[i];
+ }
+
+ out:
+ ;
+}
diff --git a/bus/dir-watch.h b/bus/dir-watch.h
new file mode 100644
index 00000000..b44529e5
--- /dev/null
+++ b/bus/dir-watch.h
@@ -0,0 +1,40 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dir-watch.h Watch directories
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * 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 "bus.h"
+
+#ifndef DIR_WATCH_H
+#define DIR_WATCH_H
+
+/**
+ * Update the set of directories to monitor for changes. The
+ * operating-system-specific implementation of this function should
+ * avoid creating a window where a directory in both the
+ * old and new set isn't monitored.
+ *
+ * @param context The bus context
+ * @param dirs List of strings which are directory paths
+ */
+void bus_set_watched_dirs (BusContext *context, DBusList **dirs);
+
+#endif /* DIR_WATCH_H */
diff --git a/bus/dispatch.c b/bus/dispatch.c
new file mode 100644
index 00000000..ca55177b
--- /dev/null
+++ b/bus/dispatch.c
@@ -0,0 +1,4725 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dispatch.c Message dispatcher
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ * Copyright (C) 2004 Imendio HB
+ *
+ * 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 "dispatch.h"
+#include "connection.h"
+#include "driver.h"
+#include "services.h"
+#include "activation.h"
+#include "utils.h"
+#include "bus.h"
+#include "signals.h"
+#include "test.h"
+#include <dbus/dbus-internals.h>
+#include <string.h>
+
+static dbus_bool_t
+send_one_message (DBusConnection *connection,
+ BusContext *context,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ if (!bus_context_check_security_policy (context, transaction,
+ sender,
+ addressed_recipient,
+ connection,
+ message,
+ NULL))
+ return TRUE; /* silently don't send it */
+
+ if (!bus_transaction_send (transaction,
+ connection,
+ message))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_dispatch_matches (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusError tmp_error;
+ BusConnections *connections;
+ DBusList *recipients;
+ BusMatchmaker *matchmaker;
+ DBusList *link;
+ BusContext *context;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* sender and recipient can both be NULL for the bus driver,
+ * or for signals with no particular recipient
+ */
+
+ _dbus_assert (sender == NULL || bus_connection_is_active (sender));
+ _dbus_assert (dbus_message_get_sender (message) != NULL);
+
+ context = bus_transaction_get_context (transaction);
+
+ /* First, send the message to the addressed_recipient, if there is one. */
+ if (addressed_recipient != NULL)
+ {
+ if (!bus_context_check_security_policy (context, transaction,
+ sender, addressed_recipient,
+ addressed_recipient,
+ message, error))
+ return FALSE;
+
+ /* Dispatch the message */
+ if (!bus_transaction_send (transaction, addressed_recipient, message))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ /* Now dispatch to others who look interested in this message */
+ connections = bus_transaction_get_connections (transaction);
+ dbus_error_init (&tmp_error);
+ matchmaker = bus_context_get_matchmaker (context);
+
+ recipients = NULL;
+ if (!bus_matchmaker_get_recipients (matchmaker, connections,
+ sender, addressed_recipient, message,
+ &recipients))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ link = _dbus_list_get_first_link (&recipients);
+ while (link != NULL)
+ {
+ DBusConnection *dest;
+
+ dest = link->data;
+
+ if (!send_one_message (dest, context, sender, addressed_recipient,
+ message, transaction, &tmp_error))
+ break;
+
+ link = _dbus_list_get_next_link (&recipients, link);
+ }
+
+ _dbus_list_clear (&recipients);
+
+ if (dbus_error_is_set (&tmp_error))
+ {
+ dbus_move_error (&tmp_error, error);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static DBusHandlerResult
+bus_dispatch (DBusConnection *connection,
+ DBusMessage *message)
+{
+ const char *sender, *service_name;
+ DBusError error;
+ BusTransaction *transaction;
+ BusContext *context;
+ DBusHandlerResult result;
+ DBusConnection *addressed_recipient;
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+
+ transaction = NULL;
+ addressed_recipient = NULL;
+ dbus_error_init (&error);
+
+ context = bus_connection_get_context (connection);
+ _dbus_assert (context != NULL);
+
+ /* If we can't even allocate an OOM error, we just go to sleep
+ * until we can.
+ */
+ while (!bus_connection_preallocate_oom_error (connection))
+ _dbus_wait_for_memory ();
+
+ /* Ref connection in case we disconnect it at some point in here */
+ dbus_connection_ref (connection);
+
+ service_name = dbus_message_get_destination (message);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ const char *interface_name, *member_name, *error_name;
+
+ interface_name = dbus_message_get_interface (message);
+ member_name = dbus_message_get_member (message);
+ error_name = dbus_message_get_error_name (message);
+
+ _dbus_verbose ("DISPATCH: %s %s %s to %s\n",
+ interface_name ? interface_name : "(no interface)",
+ member_name ? member_name : "(no member)",
+ error_name ? error_name : "(no error name)",
+ service_name ? service_name : "peer");
+ }
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+ /* If service_name is NULL, if it's a signal we send it to all
+ * connections with a match rule. If it's not a signal, there
+ * are some special cases here but mostly we just bail out.
+ */
+ if (service_name == NULL)
+ {
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected"))
+ {
+ bus_connection_disconnected (connection);
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ /* DBusConnection also handles some of these automatically, we leave
+ * it to do so.
+ */
+ result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto out;
+ }
+ }
+
+ /* Create our transaction */
+ transaction = bus_transaction_new (context);
+ if (transaction == NULL)
+ {
+ BUS_SET_OOM (&error);
+ goto out;
+ }
+
+ /* Assign a sender to the message */
+ if (bus_connection_is_active (connection))
+ {
+ sender = bus_connection_get_name (connection);
+ _dbus_assert (sender != NULL);
+
+ if (!dbus_message_set_sender (message, sender))
+ {
+ BUS_SET_OOM (&error);
+ goto out;
+ }
+
+ /* We need to refetch the service name here, because
+ * dbus_message_set_sender can cause the header to be
+ * reallocated, and thus the service_name pointer will become
+ * invalid.
+ */
+ service_name = dbus_message_get_destination (message);
+ }
+
+ if (service_name &&
+ strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
+ {
+ if (!bus_context_check_security_policy (context, transaction,
+ connection, NULL, NULL, message, &error))
+ {
+ _dbus_verbose ("Security policy rejected message\n");
+ goto out;
+ }
+
+ _dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS);
+ if (!bus_driver_handle_message (connection, transaction, message, &error))
+ goto out;
+ }
+ else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
+ {
+ _dbus_verbose ("Received message from non-registered client. Disconnecting.\n");
+ dbus_connection_close (connection);
+ goto out;
+ }
+ else if (service_name != NULL) /* route to named service */
+ {
+ DBusString service_string;
+ BusService *service;
+ BusRegistry *registry;
+
+ _dbus_assert (service_name != NULL);
+
+ registry = bus_connection_get_registry (connection);
+
+ _dbus_string_init_const (&service_string, service_name);
+ service = bus_registry_lookup (registry, &service_string);
+
+ if (service == NULL && dbus_message_get_auto_start (message))
+ {
+ BusActivation *activation;
+ /* We can't do the security policy check here, since the addressed
+ * recipient service doesn't exist yet. We do it before sending the
+ * message after the service has been created.
+ */
+ activation = bus_connection_get_activation (connection);
+
+ if (!bus_activation_activate_service (activation, connection, transaction, TRUE,
+ message, service_name, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ _dbus_verbose ("bus_activation_activate_service() failed: %s\n", error.name);
+ goto out;
+ }
+
+ goto out;
+ }
+ else if (service == NULL)
+ {
+ dbus_set_error (&error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Name \"%s\" does not exist",
+ service_name);
+ goto out;
+ }
+ else
+ {
+ addressed_recipient = bus_service_get_primary_owners_connection (service);
+ _dbus_assert (addressed_recipient != NULL);
+ }
+ }
+
+ /* Now send the message to its destination (or not, if
+ * addressed_recipient == NULL), and match it against other connections'
+ * match rules.
+ */
+ if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
+ goto out;
+
+ out:
+ if (dbus_error_is_set (&error))
+ {
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ /* If we disconnected it, we won't bother to send it any error
+ * messages.
+ */
+ _dbus_verbose ("Not sending error to connection we disconnected\n");
+ }
+ else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ bus_connection_send_oom_error (connection, message);
+
+ /* cancel transaction due to OOM */
+ if (transaction != NULL)
+ {
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ }
+ }
+ else
+ {
+ /* Try to send the real error, if no mem to do that, send
+ * the OOM error
+ */
+ _dbus_assert (transaction != NULL);
+ if (!bus_transaction_send_error_reply (transaction, connection,
+ &error, message))
+ {
+ bus_connection_send_oom_error (connection, message);
+
+ /* cancel transaction due to OOM */
+ if (transaction != NULL)
+ {
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ }
+ }
+ }
+
+
+ dbus_error_free (&error);
+ }
+
+ if (transaction != NULL)
+ {
+ bus_transaction_execute_and_free (transaction);
+ }
+
+ dbus_connection_unref (connection);
+
+ return result;
+}
+
+static DBusHandlerResult
+bus_dispatch_message_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ return bus_dispatch (connection, message);
+}
+
+dbus_bool_t
+bus_dispatch_add_connection (DBusConnection *connection)
+{
+ if (!dbus_connection_add_filter (connection,
+ bus_dispatch_message_filter,
+ NULL, NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+bus_dispatch_remove_connection (DBusConnection *connection)
+{
+ /* Here we tell the bus driver that we want to get off. */
+ bus_driver_remove_connection (connection);
+
+ dbus_connection_remove_filter (connection,
+ bus_dispatch_message_filter,
+ NULL);
+}
+
+#ifdef DBUS_BUILD_TESTS
+
+#include <stdio.h>
+
+/* This is used to know whether we need to block in order to finish
+ * sending a message, or whether the initial dbus_connection_send()
+ * already flushed the queue.
+ */
+#define SEND_PENDING(connection) (dbus_connection_has_messages_to_send (connection))
+
+typedef dbus_bool_t (* Check1Func) (BusContext *context);
+typedef dbus_bool_t (* Check2Func) (BusContext *context,
+ DBusConnection *connection);
+
+static dbus_bool_t check_no_leftovers (BusContext *context);
+
+static void
+block_connection_until_message_from_bus (BusContext *context,
+ DBusConnection *connection,
+ const char *what_is_expected)
+{
+ _dbus_verbose ("expecting: %s\n", what_is_expected);
+
+ while (dbus_connection_get_dispatch_status (connection) ==
+ DBUS_DISPATCH_COMPLETE &&
+ dbus_connection_get_is_connected (connection))
+ {
+ bus_test_run_bus_loop (context, TRUE);
+ bus_test_run_clients_loop (FALSE);
+ }
+}
+
+static void
+spin_connection_until_authenticated (BusContext *context,
+ DBusConnection *connection)
+{
+ _dbus_verbose ("Spinning to auth connection %p\n", connection);
+ while (!dbus_connection_get_is_authenticated (connection) &&
+ dbus_connection_get_is_connected (connection))
+ {
+ bus_test_run_bus_loop (context, FALSE);
+ bus_test_run_clients_loop (FALSE);
+ }
+ _dbus_verbose (" ... done spinning to auth connection %p\n", connection);
+}
+
+/* compensate for fact that pop_message() can return #NULL due to OOM */
+static DBusMessage*
+pop_message_waiting_for_memory (DBusConnection *connection)
+{
+ while (dbus_connection_get_dispatch_status (connection) ==
+ DBUS_DISPATCH_NEED_MEMORY)
+ _dbus_wait_for_memory ();
+
+ return dbus_connection_pop_message (connection);
+}
+
+static DBusMessage*
+borrow_message_waiting_for_memory (DBusConnection *connection)
+{
+ while (dbus_connection_get_dispatch_status (connection) ==
+ DBUS_DISPATCH_NEED_MEMORY)
+ _dbus_wait_for_memory ();
+
+ return dbus_connection_borrow_message (connection);
+}
+
+static void
+warn_unexpected_real (DBusConnection *connection,
+ DBusMessage *message,
+ const char *expected,
+ const char *function,
+ int line)
+{
+ if (message)
+ _dbus_warn ("%s:%d received message interface \"%s\" member \"%s\" error name \"%s\" on %p, expecting %s\n",
+ function, line,
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ connection,
+ expected);
+ else
+ _dbus_warn ("%s:%d received no message on %p, expecting %s\n",
+ function, line, connection, expected);
+}
+
+#define warn_unexpected(connection, message, expected) \
+ warn_unexpected_real (connection, message, expected, _DBUS_FUNCTION_NAME, __LINE__)
+
+static void
+verbose_message_received (DBusConnection *connection,
+ DBusMessage *message)
+{
+ _dbus_verbose ("Received message interface \"%s\" member \"%s\" error name \"%s\" on %p\n",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ connection);
+}
+
+typedef enum
+{
+ SERVICE_CREATED,
+ OWNER_CHANGED,
+ SERVICE_DELETED
+} ServiceInfoKind;
+
+typedef struct
+{
+ ServiceInfoKind expected_kind;
+ const char *expected_service_name;
+ dbus_bool_t failed;
+ DBusConnection *skip_connection;
+} CheckServiceOwnerChangedData;
+
+static dbus_bool_t
+check_service_owner_changed_foreach (DBusConnection *connection,
+ void *data)
+{
+ CheckServiceOwnerChangedData *d = data;
+ DBusMessage *message;
+ DBusError error;
+ const char *service_name, *old_owner, *new_owner;
+
+ if (d->expected_kind == SERVICE_CREATED
+ && connection == d->skip_connection)
+ return TRUE;
+
+ dbus_error_init (&error);
+ d->failed = TRUE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a message on %p, expecting %s\n",
+ connection, "NameOwnerChanged");
+ goto out;
+ }
+ else if (!dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ warn_unexpected (connection, message, "NameOwnerChanged");
+
+ goto out;
+ }
+ else
+ {
+ reget_service_info_data:
+ service_name = NULL;
+ old_owner = NULL;
+ new_owner = NULL;
+
+ dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_error_is_set (&error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto reget_service_info_data;
+ }
+ else
+ {
+ _dbus_warn ("Did not get the expected arguments\n");
+ goto out;
+ }
+ }
+
+ if ((d->expected_kind == SERVICE_CREATED && ( old_owner[0] || !new_owner[0]))
+ || (d->expected_kind == OWNER_CHANGED && (!old_owner[0] || !new_owner[0]))
+ || (d->expected_kind == SERVICE_DELETED && (!old_owner[0] || new_owner[0])))
+ {
+ _dbus_warn ("inconsistent NameOwnerChanged arguments\n");
+ goto out;
+ }
+
+ if (strcmp (service_name, d->expected_service_name) != 0)
+ {
+ _dbus_warn ("expected info on service %s, got info on %s\n",
+ d->expected_service_name,
+ service_name);
+ goto out;
+ }
+
+ if (*service_name == ':' && new_owner[0]
+ && strcmp (service_name, new_owner) != 0)
+ {
+ _dbus_warn ("inconsistent ServiceOwnedChanged message (\"%s\" [ %s -> %s ])\n",
+ service_name, old_owner, new_owner);
+ goto out;
+ }
+ }
+
+ d->failed = FALSE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return !d->failed;
+}
+
+
+static void
+kill_client_connection (BusContext *context,
+ DBusConnection *connection)
+{
+ char *base_service;
+ const char *s;
+ CheckServiceOwnerChangedData socd;
+
+ _dbus_verbose ("killing connection %p\n", connection);
+
+ s = dbus_bus_get_unique_name (connection);
+ _dbus_assert (s != NULL);
+
+ while ((base_service = _dbus_strdup (s)) == NULL)
+ _dbus_wait_for_memory ();
+
+ dbus_connection_ref (connection);
+
+ /* kick in the disconnect handler that unrefs the connection */
+ dbus_connection_close (connection);
+
+ bus_test_run_everything (context);
+
+ _dbus_assert (bus_test_client_listed (connection));
+
+ /* Run disconnect handler in test.c */
+ if (bus_connection_dispatch_one_message (connection))
+ _dbus_assert_not_reached ("something received on connection being killed other than the disconnect");
+
+ _dbus_assert (!dbus_connection_get_is_connected (connection));
+ dbus_connection_unref (connection);
+ connection = NULL;
+ _dbus_assert (!bus_test_client_listed (connection));
+
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ dbus_free (base_service);
+
+ if (socd.failed)
+ _dbus_assert_not_reached ("didn't get the expected NameOwnerChanged (deletion) messages");
+
+ if (!check_no_leftovers (context))
+ _dbus_assert_not_reached ("stuff left in message queues after disconnecting a client");
+}
+
+static void
+kill_client_connection_unchecked (DBusConnection *connection)
+{
+ /* This kills the connection without expecting it to affect
+ * the rest of the bus.
+ */
+ _dbus_verbose ("Unchecked kill of connection %p\n", connection);
+
+ dbus_connection_ref (connection);
+ dbus_connection_close (connection);
+ /* dispatching disconnect handler will unref once */
+ if (bus_connection_dispatch_one_message (connection))
+ _dbus_assert_not_reached ("message other than disconnect dispatched after failure to register");
+
+ _dbus_assert (!bus_test_client_listed (connection));
+ dbus_connection_unref (connection);
+}
+
+typedef struct
+{
+ dbus_bool_t failed;
+} CheckNoMessagesData;
+
+static dbus_bool_t
+check_no_messages_foreach (DBusConnection *connection,
+ void *data)
+{
+ CheckNoMessagesData *d = data;
+ DBusMessage *message;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message != NULL)
+ {
+ warn_unexpected (connection, message, "no messages");
+
+ d->failed = TRUE;
+ }
+
+ if (message)
+ dbus_message_unref (message);
+ return !d->failed;
+}
+
+static dbus_bool_t
+check_no_leftovers (BusContext *context)
+{
+ CheckNoMessagesData nmd;
+
+ nmd.failed = FALSE;
+ bus_test_clients_foreach (check_no_messages_foreach,
+ &nmd);
+
+ if (nmd.failed)
+ {
+ _dbus_verbose ("%s: leftover message found\n",
+ _DBUS_FUNCTION_NAME);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_hello_message (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusMessage *name_message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ DBusError error;
+ const char *name;
+ const char *acquired;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ name = NULL;
+ acquired = NULL;
+ message = NULL;
+ name_message = NULL;
+
+ _dbus_verbose ("check_hello_message for %p\n", connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "Hello");
+
+ if (message == NULL)
+ return TRUE;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ dbus_connection_unref (connection);
+ return TRUE;
+ }
+
+ _dbus_assert (dbus_message_has_signature (message, ""));
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected (presumably auth failed)\n");
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected (presumably auth failed)\n");
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ block_connection_until_message_from_bus (context, connection, "reply to Hello");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected (presumably auth failed)\n");
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Hello", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ CheckServiceOwnerChangedData socd;
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "method return for Hello");
+
+ goto out;
+ }
+
+ retry_get_hello_name:
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("no memory to get service name arg from hello\n");
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto retry_get_hello_name;
+ }
+ else
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Did not get the expected single string argument to hello\n");
+ goto out;
+ }
+ }
+
+ _dbus_verbose ("Got hello name: %s\n", name);
+
+ while (!dbus_bus_set_unique_name (connection, name))
+ _dbus_wait_for_memory ();
+
+ socd.expected_kind = SERVICE_CREATED;
+ socd.expected_service_name = name;
+ socd.failed = FALSE;
+ socd.skip_connection = connection; /* we haven't done AddMatch so won't get it ourselves */
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ name_message = message;
+ /* Client should also have gotten ServiceAcquired */
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Expecting %s, got nothing\n",
+ "NameAcquired");
+ goto out;
+ }
+ if (! dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired"))
+ {
+ _dbus_warn ("Expecting %s, got smthg else\n",
+ "NameAcquired");
+ goto out;
+ }
+
+ retry_get_acquired_name:
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &acquired,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("no memory to get service name arg from acquired\n");
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto retry_get_acquired_name;
+ }
+ else
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Did not get the expected single string argument to ServiceAcquired\n");
+ goto out;
+ }
+ }
+
+ _dbus_verbose ("Got acquired name: %s\n", acquired);
+
+ if (strcmp (acquired, name) != 0)
+ {
+ _dbus_warn ("Acquired name is %s but expected %s\n",
+ acquired, name);
+ goto out;
+ }
+ acquired = NULL;
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ _dbus_verbose ("ending %s retval = %d\n", _DBUS_FUNCTION_NAME, retval);
+
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ if (name_message)
+ dbus_message_unref (name_message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_double_hello_message (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ DBusError error;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ _dbus_verbose ("check_double_hello_message for %p\n", connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "Hello");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+ block_connection_until_message_from_bus (context, connection, "reply to Hello");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Hello", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+ {
+ warn_unexpected (connection, message, "method return for Hello");
+ goto out;
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_get_connection_unix_user (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ DBusError error;
+ const char *base_service_name;
+ dbus_uint32_t uid;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ _dbus_verbose ("check_get_connection_unix_user for %p\n", connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixUser");
+
+ if (message == NULL)
+ return TRUE;
+
+ base_service_name = dbus_bus_get_unique_name (connection);
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &base_service_name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+ block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixUser");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "GetConnectionUnixUser", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ }
+ else
+ {
+ warn_unexpected (connection, message,
+ "method_return for GetConnectionUnixUser");
+
+ goto out;
+ }
+
+ retry_get_property:
+
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_UINT32, &uid,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("no memory to get uid by GetConnectionUnixUser\n");
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto retry_get_property;
+ }
+ else
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Did not get the expected DBUS_TYPE_UINT32 from GetConnectionUnixUser\n");
+ goto out;
+ }
+ }
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_get_connection_unix_process_id (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ DBusError error;
+ const char *base_service_name;
+ dbus_uint32_t pid;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ _dbus_verbose ("check_get_connection_unix_process_id for %p\n", connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID");
+
+ if (message == NULL)
+ return TRUE;
+
+ base_service_name = dbus_bus_get_unique_name (connection);
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &base_service_name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+ block_connection_until_message_from_bus (context, connection, "reply to GetConnectionUnixProcessID");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "GetConnectionUnixProcessID", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+#ifdef DBUS_WIN
+ else if (dbus_message_is_error (message, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN))
+ {
+ /* We are expecting this error, since we know in the test suite we aren't
+ * talking to a client running on UNIX
+ */
+ _dbus_verbose ("Windows correctly does not support GetConnectionUnixProcessID\n");
+ }
+#endif
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+#ifdef DBUS_WIN
+ warn_unexpected (connection, message, "GetConnectionUnixProcessID to fail on Windows");
+ goto out;
+#else
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ }
+ else
+ {
+ warn_unexpected (connection, message,
+ "method_return for GetConnectionUnixProcessID");
+
+ goto out;
+ }
+
+ retry_get_property:
+
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("no memory to get pid by GetConnectionUnixProcessID\n");
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto retry_get_property;
+ }
+ else
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Did not get the expected DBUS_TYPE_UINT32 from GetConnectionUnixProcessID\n");
+ goto out;
+ }
+ }
+ else
+ {
+ /* test if returned pid is the same as our own pid
+ *
+ * @todo It would probably be good to restructure the tests
+ * in a way so our parent is the bus that we're testing
+ * cause then we can test that the pid returned matches
+ * getppid()
+ */
+ if (pid != (dbus_uint32_t) _dbus_getpid ())
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Result from GetConnectionUnixProcessID is not our own pid\n");
+ goto out;
+ }
+ }
+#endif /* !DBUS_WIN */
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_add_match_all (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ dbus_uint32_t serial;
+ DBusError error;
+ const char *empty = "";
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ _dbus_verbose ("check_add_match_all for %p\n", connection);
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "AddMatch");
+
+ if (message == NULL)
+ return TRUE;
+
+ /* empty string match rule matches everything */
+ if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &empty,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ block_connection_until_message_from_bus (context, connection, "reply to AddMatch");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "AddMatch", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ _dbus_assert (dbus_message_get_reply_serial (message) == serial);
+ }
+ else
+ {
+ warn_unexpected (connection, message, "method return for AddMatch");
+
+ goto out;
+ }
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_hello_connection (BusContext *context)
+{
+ DBusConnection *connection;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ connection = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (connection == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ dbus_error_free (&error);
+ return TRUE;
+ }
+
+ if (!bus_setup_debug_client (connection))
+ {
+ dbus_connection_close (connection);
+ dbus_connection_unref (connection);
+ return TRUE;
+ }
+
+ spin_connection_until_authenticated (context, connection);
+
+ if (!check_hello_message (context, connection))
+ return FALSE;
+
+ if (dbus_bus_get_unique_name (connection) == NULL)
+ {
+ /* We didn't successfully register, so we can't
+ * do the usual kill_client_connection() checks
+ */
+ kill_client_connection_unchecked (connection);
+ }
+ else
+ {
+ if (!check_add_match_all (context, connection))
+ return FALSE;
+
+ kill_client_connection (context, connection);
+ }
+
+ return TRUE;
+}
+
+#define NONEXISTENT_SERVICE_NAME "test.this.service.does.not.exist.ewuoiurjdfxcvn"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_nonexistent_service_no_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *nonexistent = NONEXISTENT_SERVICE_NAME;
+ dbus_uint32_t flags;
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+
+ if (message == NULL)
+ return TRUE;
+
+ dbus_message_set_auto_start (message, FALSE);
+
+ flags = 0;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &nonexistent,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to ActivateService on nonexistent");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "StartServiceByName", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully activate %s\n",
+ NONEXISTENT_SERVICE_NAME);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_nonexistent_service_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (NONEXISTENT_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to Echo");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully activate %s\n",
+ NONEXISTENT_SERVICE_NAME);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+static dbus_bool_t
+check_base_service_activated (BusContext *context,
+ DBusConnection *connection,
+ DBusMessage *initial_message,
+ const char **base_service_p)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ DBusError error;
+ const char *base_service, *base_service_from_bus, *old_owner;
+
+ retval = FALSE;
+
+ dbus_error_init (&error);
+ base_service = NULL;
+ old_owner = NULL;
+ base_service_from_bus = NULL;
+
+ message = initial_message;
+ dbus_message_ref (message);
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ CheckServiceOwnerChangedData socd;
+
+ reget_service_name_arg:
+ base_service = NULL;
+ old_owner = NULL;
+ base_service_from_bus = NULL;
+
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &base_service,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &base_service_from_bus,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto reget_service_name_arg;
+ }
+ else
+ {
+ _dbus_warn ("Message %s doesn't have a service name: %s\n",
+ "NameOwnerChanged (creation)",
+ error.message);
+ goto out;
+ }
+ }
+
+ if (*base_service != ':')
+ {
+ _dbus_warn ("Expected base service activation, got \"%s\" instead\n",
+ base_service);
+ goto out;
+ }
+
+ if (strcmp (base_service, base_service_from_bus) != 0)
+ {
+ _dbus_warn ("Expected base service activation, got \"%s\" instead with owner \"%s\"\n",
+ base_service, base_service_from_bus);
+ goto out;
+ }
+
+ if (old_owner[0])
+ {
+ _dbus_warn ("Received an old_owner argument during base service activation, \"%s\"\n",
+ old_owner);
+ goto out;
+ }
+
+ socd.expected_kind = SERVICE_CREATED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = connection;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+ }
+ else
+ {
+ warn_unexpected (connection, message, "NameOwnerChanged (creation) for base service");
+
+ goto out;
+ }
+
+ if (base_service_p)
+ *base_service_p = base_service;
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return retval;
+}
+
+static dbus_bool_t
+check_service_activated (BusContext *context,
+ DBusConnection *connection,
+ const char *activated_name,
+ const char *base_service_name,
+ DBusMessage *initial_message)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ DBusError error;
+ dbus_uint32_t activation_result;
+
+ retval = FALSE;
+
+ dbus_error_init (&error);
+
+ message = initial_message;
+ dbus_message_ref (message);
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ CheckServiceOwnerChangedData socd;
+ const char *service_name, *base_service_from_bus, *old_owner;
+
+ reget_service_name_arg:
+ service_name = NULL;
+ old_owner = NULL;
+ base_service_from_bus = NULL;
+
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &base_service_from_bus,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto reget_service_name_arg;
+ }
+ else
+ {
+ _dbus_warn ("Message %s doesn't have a service name: %s\n",
+ "NameOwnerChanged (creation)",
+ error.message);
+ goto out;
+ }
+ }
+
+ if (strcmp (service_name, activated_name) != 0)
+ {
+ _dbus_warn ("Expected to see service %s created, saw %s instead\n",
+ activated_name, service_name);
+ goto out;
+ }
+
+ if (strcmp (base_service_name, base_service_from_bus) != 0)
+ {
+ _dbus_warn ("NameOwnerChanged reports wrong base service: %s owner, expected %s instead\n",
+ base_service_from_bus, base_service_name);
+ goto out;
+ }
+
+ if (old_owner[0])
+ {
+ _dbus_warn ("expected a %s, got a %s\n",
+ "NameOwnerChanged (creation)",
+ "NameOwnerChanged (change)");
+ goto out;
+ }
+
+ socd.expected_kind = SERVICE_CREATED;
+ socd.skip_connection = connection;
+ socd.failed = FALSE;
+ socd.expected_service_name = service_name;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ dbus_message_unref (message);
+ service_name = NULL;
+ old_owner = NULL;
+ base_service_from_bus = NULL;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Expected a reply to %s, got nothing\n",
+ "StartServiceByName");
+ goto out;
+ }
+ }
+ else
+ {
+ warn_unexpected (connection, message, "NameOwnerChanged for the activated name");
+
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ warn_unexpected (connection, message, "reply to StartServiceByName");
+
+ goto out;
+ }
+
+ activation_result = 0;
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_UINT32, &activation_result,
+ DBUS_TYPE_INVALID))
+ {
+ if (!dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_warn ("Did not have activation result first argument to %s: %s\n",
+ "StartServiceByName", error.message);
+ goto out;
+ }
+
+ dbus_error_free (&error);
+ }
+ else
+ {
+ if (activation_result == DBUS_START_REPLY_SUCCESS)
+ ; /* Good */
+ else if (activation_result == DBUS_START_REPLY_ALREADY_RUNNING)
+ ; /* Good also */
+ else
+ {
+ _dbus_warn ("Activation result was %u, no good.\n",
+ activation_result);
+ goto out;
+ }
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after verifying existent activation results\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return retval;
+}
+
+static dbus_bool_t
+check_service_auto_activated (BusContext *context,
+ DBusConnection *connection,
+ const char *activated_name,
+ const char *base_service_name,
+ DBusMessage *initial_message)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ DBusError error;
+
+ retval = FALSE;
+
+ dbus_error_init (&error);
+
+ message = initial_message;
+ dbus_message_ref (message);
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ const char *service_name;
+ CheckServiceOwnerChangedData socd;
+
+ reget_service_name_arg:
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto reget_service_name_arg;
+ }
+ else
+ {
+ _dbus_warn ("Message %s doesn't have a service name: %s\n",
+ "NameOwnerChanged",
+ error.message);
+ dbus_error_free (&error);
+ goto out;
+ }
+ }
+
+ if (strcmp (service_name, activated_name) != 0)
+ {
+ _dbus_warn ("Expected to see service %s created, saw %s instead\n",
+ activated_name, service_name);
+ goto out;
+ }
+
+ socd.expected_kind = SERVICE_CREATED;
+ socd.expected_service_name = service_name;
+ socd.failed = FALSE;
+ socd.skip_connection = connection;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ /* Note that this differs from regular activation in that we don't get a
+ * reply to ActivateService here.
+ */
+
+ dbus_message_unref (message);
+ message = NULL;
+ service_name = NULL;
+ }
+ else
+ {
+ warn_unexpected (connection, message, "NameOwnerChanged for the activated name");
+
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+static dbus_bool_t
+check_service_deactivated (BusContext *context,
+ DBusConnection *connection,
+ const char *activated_name,
+ const char *base_service)
+{
+ dbus_bool_t retval;
+ CheckServiceOwnerChangedData socd;
+
+ retval = FALSE;
+
+ /* Now we are expecting ServiceOwnerChanged (deletion) messages for the base
+ * service and the activated_name. The base service
+ * notification is required to come last.
+ */
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = activated_name;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+static dbus_bool_t
+check_send_exit_to_service (BusContext *context,
+ DBusConnection *connection,
+ const char *service_name,
+ const char *base_service)
+{
+ dbus_bool_t got_error;
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ _dbus_verbose ("Sending exit message to the test service\n");
+
+ retval = FALSE;
+
+ /* Kill off the test service by sending it a quit message */
+ message = dbus_message_new_method_call (service_name,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Exit");
+
+ if (message == NULL)
+ {
+ /* Do this again; we still need the service to exit... */
+ if (!check_send_exit_to_service (context, connection,
+ service_name, base_service))
+ goto out;
+
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+
+ /* Do this again; we still need the service to exit... */
+ if (!check_send_exit_to_service (context, connection,
+ service_name, base_service))
+ goto out;
+
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ /* send message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ /* read it in and write it out to test service */
+ bus_test_run_bus_loop (context, FALSE);
+
+ /* see if we got an error during message bus dispatching */
+ bus_test_run_clients_loop (FALSE);
+ message = borrow_message_waiting_for_memory (connection);
+ got_error = message != NULL && dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR;
+ if (message)
+ {
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+ }
+
+ if (!got_error)
+ {
+ /* If no error, wait for the test service to exit */
+ block_connection_until_message_from_bus (context, connection, "test service to exit");
+
+ bus_test_run_everything (context);
+ }
+
+ if (got_error)
+ {
+ message = pop_message_waiting_for_memory (connection);
+ _dbus_assert (message != NULL);
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ warn_unexpected (connection, message,
+ "error with the correct reply serial");
+ goto out;
+ }
+
+ if (!dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ warn_unexpected (connection, message,
+ "a no memory error from asking test service to exit");
+ goto out;
+ }
+
+ _dbus_verbose ("Got error %s when asking test service to exit\n",
+ dbus_message_get_error_name (message));
+
+ /* Do this again; we still need the service to exit... */
+ if (!check_send_exit_to_service (context, connection,
+ service_name, base_service))
+ goto out;
+ }
+ else
+ {
+ if (!check_service_deactivated (context, connection,
+ service_name, base_service))
+ goto out;
+
+ /* Should now have a NoReply error from the Exit() method
+ * call; it should have come after all the deactivation
+ * stuff.
+ */
+ message = pop_message_waiting_for_memory (connection);
+
+ if (message == NULL)
+ {
+ warn_unexpected (connection, NULL,
+ "reply to Exit() method call");
+ goto out;
+ }
+ if (!dbus_message_is_error (message,
+ DBUS_ERROR_NO_REPLY))
+ {
+ warn_unexpected (connection, message,
+ "NoReply error from Exit() method call");
+ goto out;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ warn_unexpected (connection, message,
+ "error with the correct reply serial");
+ goto out;
+ }
+
+ _dbus_verbose ("Got error %s after test service exited\n",
+ dbus_message_get_error_name (message));
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after %s\n",
+ _DBUS_FUNCTION_NAME);
+ goto out;
+ }
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+static dbus_bool_t
+check_got_error (BusContext *context,
+ DBusConnection *connection,
+ const char *first_error_name,
+ ...)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ va_list ap;
+ dbus_bool_t error_found;
+ const char *error_name;
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not get an expected error\n");
+ goto out;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_ERROR)
+ {
+ warn_unexpected (connection, message, "an error");
+
+ goto out;
+ }
+
+ error_found = FALSE;
+
+ va_start (ap, first_error_name);
+ error_name = first_error_name;
+ while (error_name != NULL)
+ {
+ if (dbus_message_is_error (message, error_name))
+ {
+ error_found = TRUE;
+ break;
+ }
+ error_name = va_arg (ap, char*);
+ }
+ va_end (ap);
+
+ if (!error_found)
+ {
+ _dbus_warn ("Expected error %s or other, got %s instead\n",
+ first_error_name,
+ dbus_message_get_error_name (message));
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+typedef enum
+{
+ GOT_SERVICE_CREATED,
+ GOT_SERVICE_DELETED,
+ GOT_ERROR,
+ GOT_SOMETHING_ELSE
+} GotServiceInfo;
+
+static GotServiceInfo
+check_got_service_info (DBusMessage *message)
+{
+ GotServiceInfo message_kind;
+
+ if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+ DBusError error;
+ const char *service_name, *old_owner, *new_owner;
+ dbus_error_init (&error);
+
+ reget_service_info_data:
+ service_name = NULL;
+ old_owner = NULL;
+ new_owner = NULL;
+
+ dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID);
+ if (dbus_error_is_set (&error))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ goto reget_service_info_data;
+ }
+ else
+ {
+ _dbus_warn ("unexpected arguments for NameOwnerChanged message\n");
+ message_kind = GOT_SOMETHING_ELSE;
+ }
+ }
+ else if (!old_owner[0])
+ message_kind = GOT_SERVICE_CREATED;
+ else if (!new_owner[0])
+ message_kind = GOT_SERVICE_DELETED;
+ else
+ message_kind = GOT_SOMETHING_ELSE;
+
+ dbus_error_free (&error);
+ }
+ else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ message_kind = GOT_ERROR;
+ else
+ message_kind = GOT_SOMETHING_ELSE;
+
+ return message_kind;
+}
+
+#define EXISTENT_SERVICE_NAME "org.freedesktop.DBus.TestSuiteEchoService"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_existent_service_no_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusMessage *base_service_message;
+ const char *base_service;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *existent = EXISTENT_SERVICE_NAME;
+ dbus_uint32_t flags;
+
+ base_service_message = NULL;
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+
+ if (message == NULL)
+ return TRUE;
+
+ dbus_message_set_auto_start (message, FALSE);
+
+ flags = 0;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &existent,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* now wait for the message bus to hear back from the activated
+ * service.
+ */
+ block_connection_until_message_from_bus (context, connection, "activated service to connect");
+
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after %s %d on %p\n",
+ "StartServiceByName", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+ _dbus_verbose (" (after sending %s)\n", "StartServiceByName");
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_EXITED) ||
+ dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_SIGNALED) ||
+ dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_EXEC_FAILED))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ _dbus_warn ("Did not expect error %s\n",
+ dbus_message_get_error_name (message));
+ goto out;
+ }
+ }
+ else
+ {
+ GotServiceInfo message_kind;
+
+ if (!check_base_service_activated (context, connection,
+ message, &base_service))
+ goto out;
+
+ base_service_message = message;
+ message = NULL;
+
+ /* We may need to block here for the test service to exit or finish up */
+ block_connection_until_message_from_bus (context, connection, "test service to exit or finish up");
+
+ message = dbus_connection_borrow_message (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after base service creation notification\n");
+ goto out;
+ }
+
+ message_kind = check_got_service_info (message);
+
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+
+ switch (message_kind)
+ {
+ case GOT_SOMETHING_ELSE:
+ _dbus_warn ("Unexpected message after ActivateService "
+ "(should be an error or a service announcement");
+ goto out;
+
+ case GOT_ERROR:
+ if (!check_got_error (context, connection,
+ DBUS_ERROR_SPAWN_CHILD_EXITED,
+ DBUS_ERROR_NO_MEMORY,
+ NULL))
+ goto out;
+ /* A service deleted should be coming along now after this error.
+ * We can also get the error *after* the service deleted.
+ */
+
+ /* fall through */
+
+ case GOT_SERVICE_DELETED:
+ {
+ /* The service started up and got a base address, but then
+ * failed to register under EXISTENT_SERVICE_NAME
+ */
+ CheckServiceOwnerChangedData socd;
+
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ /* Now we should get an error about the service exiting
+ * if we didn't get it before.
+ */
+ if (message_kind != GOT_ERROR)
+ {
+ block_connection_until_message_from_bus (context, connection, "error about service exiting");
+
+ /* and process everything again */
+ bus_test_run_everything (context);
+
+ if (!check_got_error (context, connection,
+ DBUS_ERROR_SPAWN_CHILD_EXITED,
+ DBUS_ERROR_NO_MEMORY,
+ NULL))
+ goto out;
+ }
+ break;
+ }
+
+ case GOT_SERVICE_CREATED:
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message we just put back! "
+ "should have been a NameOwnerChanged (creation)\n");
+ goto out;
+ }
+
+ if (!check_service_activated (context, connection, EXISTENT_SERVICE_NAME,
+ base_service, message))
+ goto out;
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after successful activation\n");
+ goto out;
+ }
+
+ if (!check_send_exit_to_service (context, connection,
+ EXISTENT_SERVICE_NAME, base_service))
+ goto out;
+
+ break;
+ }
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ if (base_service_message)
+ dbus_message_unref (base_service_message);
+
+ return retval;
+}
+
+#ifndef DBUS_WIN_FIXME
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_segfault_service_no_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *segv_service;
+ dbus_uint32_t flags;
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+
+ if (message == NULL)
+ return TRUE;
+
+ dbus_message_set_auto_start (message, FALSE);
+
+ segv_service = "org.freedesktop.DBus.TestSuiteSegfaultService";
+ flags = 0;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &segv_service,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to activating segfault service");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "StartServiceByName", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_FAILED))
+ {
+ const char *servicehelper;
+ servicehelper = bus_context_get_servicehelper (context);
+ /* make sure this only happens with the launch helper */
+ _dbus_assert (servicehelper != NULL);
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_SIGNALED))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully activate segfault service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_segfault_service_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteSegfaultService",
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to Echo on segfault service");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_SIGNALED))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully activate segfault service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+#endif
+
+#define TEST_ECHO_MESSAGE "Test echo message"
+#define TEST_RUN_HELLO_FROM_SELF_MESSAGE "Test sending message to self"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_existent_hello_from_self (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ const char *text;
+
+ message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "RunHelloFromSelf");
+
+ if (message == NULL)
+ return TRUE;
+
+ text = TEST_RUN_HELLO_FROM_SELF_MESSAGE;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* Note: if this test is run in OOM mode, it will block when the bus
+ * doesn't send a reply due to OOM.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply from running hello from self");
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message! Should have been reply from RunHelloFromSelf message\n");
+ return FALSE;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ _dbus_warn ("Wrong reply serial\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ return TRUE;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_existent_ping (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.DBus.Peer",
+ "Ping");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* Note: if this test is run in OOM mode, it will block when the bus
+ * doesn't send a reply due to OOM.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply from running Ping");
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message! Should have been reply from Ping message\n");
+ return FALSE;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ _dbus_warn ("Wrong reply serial\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ _dbus_warn ("Unexpected message return during Ping\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ return TRUE;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_existent_get_machine_id (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ const char *machine_id;
+
+ message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.DBus.Peer",
+ "GetMachineId");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* Note: if this test is run in OOM mode, it will block when the bus
+ * doesn't send a reply due to OOM.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply from running GetMachineId");
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message! Should have been reply from GetMachineId message\n");
+ return FALSE;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ _dbus_warn ("Wrong reply serial\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ _dbus_warn ("Unexpected message return during GetMachineId\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ machine_id = NULL;
+ if (!dbus_message_get_args (message, NULL, DBUS_TYPE_STRING, &machine_id, DBUS_TYPE_INVALID))
+ {
+ _dbus_warn ("Did not get a machine ID in reply to GetMachineId\n");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ if (machine_id == NULL || strlen (machine_id) != 32)
+ {
+ _dbus_warn ("Machine id looks bogus: '%s'\n", machine_id ? machine_id : "null");
+ dbus_message_unref (message);
+ return FALSE;
+ }
+
+ /* We can't check that the machine id is correct because during make check it is
+ * just made up for each process separately
+ */
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ return TRUE;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_existent_service_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusMessage *base_service_message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *base_service;
+ const char *text;
+
+ base_service_message = NULL;
+
+ message = dbus_message_new_method_call (EXISTENT_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ text = TEST_ECHO_MESSAGE;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* now wait for the message bus to hear back from the activated
+ * service.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply to Echo on existent service");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after auto start %d on %p\n",
+ serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+ _dbus_verbose (" (after sending %s)\n", "auto start");
+
+ /* we should get zero or two ServiceOwnerChanged signals */
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ GotServiceInfo message_kind;
+
+ if (!check_base_service_activated (context, connection,
+ message, &base_service))
+ goto out;
+
+ base_service_message = message;
+ message = NULL;
+
+ /* We may need to block here for the test service to exit or finish up */
+ block_connection_until_message_from_bus (context, connection, "service to exit");
+
+ /* Should get a service creation notification for the activated
+ * service name, or a service deletion on the base service name
+ */
+ message = dbus_connection_borrow_message (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("No message after auto activation "
+ "(should be a service announcement)\n");
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+ goto out;
+ }
+
+ message_kind = check_got_service_info (message);
+
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+
+ switch (message_kind)
+ {
+ case GOT_SERVICE_CREATED:
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message we just put back! "
+ "should have been a NameOwnerChanged (creation)\n");
+ goto out;
+ }
+
+ /* Check that ServiceOwnerChanged (creation) was correctly received */
+ if (!check_service_auto_activated (context, connection, EXISTENT_SERVICE_NAME,
+ base_service, message))
+ goto out;
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ break;
+
+ case GOT_SERVICE_DELETED:
+ {
+ /* The service started up and got a base address, but then
+ * failed to register under EXISTENT_SERVICE_NAME
+ */
+ CheckServiceOwnerChangedData socd;
+
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ break;
+ }
+
+ case GOT_ERROR:
+ case GOT_SOMETHING_ELSE:
+ _dbus_warn ("Unexpected message after auto activation\n");
+ goto out;
+ }
+ }
+
+ /* OK, now we've dealt with ServiceOwnerChanged signals, now should
+ * come the method reply (or error) from the initial method call
+ */
+
+ /* Note: if this test is run in OOM mode, it will block when the bus
+ * doesn't send a reply due to OOM.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply from echo message after auto-activation");
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message! Should have been reply from echo message\n");
+ goto out;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ _dbus_warn ("Wrong reply serial\n");
+ goto out;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!check_existent_ping (context, connection))
+ goto out;
+
+ if (!check_existent_get_machine_id (context, connection))
+ goto out;
+
+ if (!check_existent_hello_from_self (context, connection))
+ goto out;
+
+ if (!check_send_exit_to_service (context, connection,
+ EXISTENT_SERVICE_NAME,
+ base_service))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ if (base_service_message)
+ dbus_message_unref (base_service_message);
+
+ return retval;
+}
+
+#define SERVICE_FILE_MISSING_NAME "org.freedesktop.DBus.TestSuiteEchoServiceDotServiceFileDoesNotExist"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_launch_service_file_missing (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (SERVICE_FILE_MISSING_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to service file missing should fail to auto-start");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ _dbus_verbose("got service unknown\n");
+ ; /* good, this is expected (only valid when using launch helper) */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully auto-start missing service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+#define SERVICE_USER_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoUser"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_launch_service_user_missing (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (SERVICE_USER_MISSING_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection,
+ "reply to service which should fail to auto-start (missing User)");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_warn ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_FILE_INVALID))
+ {
+ _dbus_verbose("got service file invalid\n");
+ ; /* good, this is expected (only valid when using launch helper) */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully auto-start missing service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+#define SERVICE_EXEC_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoExec"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_launch_service_exec_missing (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (SERVICE_EXEC_MISSING_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection,
+ "reply to service which should fail to auto-start (missing Exec)");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_warn ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ _dbus_verbose("could not activate as invalid service file was not added\n");
+ ; /* good, this is expected as we shouldn't have been added to
+ * the activation list with a missing Exec key */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_FILE_INVALID))
+ {
+ _dbus_verbose("got service file invalid\n");
+ ; /* good, this is allowed, and is the message passed back from the
+ * launch helper */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully auto-start missing service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+#define SERVICE_SERVICE_MISSING_NAME "org.freedesktop.DBus.TestSuiteNoService"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_launch_service_service_missing (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (SERVICE_SERVICE_MISSING_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection,
+ "reply to service which should fail to auto-start (missing Service)");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_warn ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ _dbus_verbose("could not activate as invalid service file was not added\n");
+ ; /* good, this is expected as we shouldn't have been added to
+ * the activation list with a missing Exec key */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_FILE_INVALID))
+ {
+ _dbus_verbose("got service file invalid\n");
+ ; /* good, this is allowed, and is the message passed back from the
+ * launch helper */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully auto-start missing service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+#define SHELL_FAIL_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceFail"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_shell_fail_service_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+
+ message = dbus_message_new_method_call (SHELL_FAIL_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+ block_connection_until_message_from_bus (context, connection, "reply to shell Echo on service which should fail to auto-start");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ "Echo message (auto activation)", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_INVALID_ARGS))
+ {
+ _dbus_verbose("got invalid args\n");
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ _dbus_warn ("Did not expect to successfully auto-start shell fail service\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+#define SHELL_SUCCESS_SERVICE_NAME "org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess"
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_shell_service_success_auto_start (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusMessage *base_service_message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *base_service;
+ const char *argv[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+ base_service_message = NULL;
+
+ message = dbus_message_new_method_call (SHELL_SUCCESS_SERVICE_NAME,
+ "/org/freedesktop/TestSuite",
+ "org.freedesktop.TestSuite",
+ "Echo");
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* now wait for the message bus to hear back from the activated
+ * service.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply to Echo on shell success service");
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after auto start %d on %p\n",
+ serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+ _dbus_verbose (" (after sending %s)\n", "auto start");
+
+ /* we should get zero or two ServiceOwnerChanged signals */
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ GotServiceInfo message_kind;
+
+ if (!check_base_service_activated (context, connection,
+ message, &base_service))
+ goto out;
+
+ base_service_message = message;
+ message = NULL;
+
+ /* We may need to block here for the test service to exit or finish up */
+ block_connection_until_message_from_bus (context, connection, "service to exit");
+
+ /* Should get a service creation notification for the activated
+ * service name, or a service deletion on the base service name
+ */
+ message = dbus_connection_borrow_message (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("No message after auto activation "
+ "(should be a service announcement)\n");
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+ goto out;
+ }
+
+ message_kind = check_got_service_info (message);
+
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+
+ switch (message_kind)
+ {
+ case GOT_SERVICE_CREATED:
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message we just put back! "
+ "should have been a NameOwnerChanged (creation)\n");
+ goto out;
+ }
+
+ /* Check that ServiceOwnerChanged (creation) was correctly received */
+ if (!check_service_auto_activated (context, connection, SHELL_SUCCESS_SERVICE_NAME,
+ base_service, message))
+ goto out;
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ break;
+
+ case GOT_SERVICE_DELETED:
+ {
+ /* The service started up and got a base address, but then
+ * failed to register under SHELL_SUCCESS_SERVICE_NAME
+ */
+ CheckServiceOwnerChangedData socd;
+
+ socd.expected_kind = SERVICE_DELETED;
+ socd.expected_service_name = base_service;
+ socd.failed = FALSE;
+ socd.skip_connection = NULL;
+ bus_test_clients_foreach (check_service_owner_changed_foreach,
+ &socd);
+
+ if (socd.failed)
+ goto out;
+
+ break;
+ }
+
+ case GOT_ERROR:
+ case GOT_SOMETHING_ELSE:
+ _dbus_warn ("Unexpected message after auto activation\n");
+ goto out;
+ }
+ }
+
+ /* OK, now we've dealt with ServiceOwnerChanged signals, now should
+ * come the method reply (or error) from the initial method call
+ */
+
+ /* Note: if this test is run in OOM mode, it will block when the bus
+ * doesn't send a reply due to OOM.
+ */
+ block_connection_until_message_from_bus (context, connection, "reply from echo message after auto-activation");
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message! Should have been reply from echo message\n");
+ goto out;
+ }
+
+ if (dbus_message_get_reply_serial (message) != serial)
+ {
+ _dbus_warn ("Wrong reply serial\n");
+ goto out;
+ }
+
+ if (!dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &argv[0],
+ DBUS_TYPE_STRING, &argv[1],
+ DBUS_TYPE_STRING, &argv[2],
+ DBUS_TYPE_STRING, &argv[3],
+ DBUS_TYPE_STRING, &argv[4],
+ DBUS_TYPE_STRING, &argv[5],
+ DBUS_TYPE_STRING, &argv[6],
+ DBUS_TYPE_INVALID))
+ {
+ _dbus_warn ("Error getting arguments from return\n");
+ goto out;
+ }
+
+ /* don't worry about arg[0] as it may be different
+ depending on the path to the tests
+ */
+ if (strcmp("-test", argv[1]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[1] in shell success service test (expected: %s, got: %s)\n",
+ "-test", argv[1]);
+ goto out;
+ }
+
+ if (strcmp("that", argv[2]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[2] in shell success service test (expected: %s, got: %s)\n",
+ "that", argv[2]);
+ goto out;
+ }
+
+ if (strcmp("we get", argv[3]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[3] in shell success service test (expected: %s, got: %s)\n",
+ "we get", argv[3]);
+ goto out;
+ }
+
+ if (strcmp("back", argv[4]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[4] in shell success service test (expected: %s, got: %s)\n",
+ "back", argv[4]);
+ goto out;
+ }
+
+ if (strcmp("--what", argv[5]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[5] in shell success service test (expected: %s, got: %s)\n",
+ "--what", argv[5]);
+ goto out;
+ }
+
+ if (strcmp("we put in", argv[6]) != 0)
+ {
+ _dbus_warn ("Unexpected argv[6] in shell success service test (expected: %s, got: %s)\n",
+ "we put in", argv[6]);
+ goto out;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!check_send_exit_to_service (context, connection,
+ SHELL_SUCCESS_SERVICE_NAME,
+ base_service))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ if (base_service_message)
+ dbus_message_unref (base_service_message);
+
+ return retval;
+}
+
+typedef struct
+{
+ Check1Func func;
+ BusContext *context;
+} Check1Data;
+
+static dbus_bool_t
+check_oom_check1_func (void *data)
+{
+ Check1Data *d = data;
+
+ if (! (* d->func) (d->context))
+ return FALSE;
+
+ if (!check_no_leftovers (d->context))
+ {
+ _dbus_warn ("Messages were left over, should be covered by test suite\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+check1_try_iterations (BusContext *context,
+ const char *description,
+ Check1Func func)
+{
+ Check1Data d;
+
+ d.func = func;
+ d.context = context;
+
+ if (!_dbus_test_oom_handling (description, check_oom_check1_func,
+ &d))
+ _dbus_assert_not_reached ("test failed");
+}
+
+static dbus_bool_t
+check_get_services (BusContext *context,
+ DBusConnection *connection,
+ const char *method,
+ char ***services,
+ int *len)
+{
+ DBusMessage *message;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ DBusError error;
+ char **srvs;
+ int l;
+
+ retval = FALSE;
+ dbus_error_init (&error);
+ message = NULL;
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ method);
+
+ if (message == NULL)
+ return TRUE;
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ /* send our message */
+ bus_test_run_clients_loop (SEND_PENDING (connection));
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ dbus_connection_ref (connection); /* because we may get disconnected */
+ block_connection_until_message_from_bus (context, connection, "reply to ListActivatableNames/ListNames");
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+
+ dbus_connection_unref (connection);
+
+ return TRUE;
+ }
+
+ dbus_connection_unref (connection);
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive a reply to %s %d on %p\n",
+ method, serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (dbus_message_is_error (message, DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else
+ {
+ warn_unexpected (connection, message, "not this error");
+
+ goto out;
+ }
+ }
+ else
+ {
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ ; /* good, expected */
+ }
+ else
+ {
+ warn_unexpected (connection, message,
+ "method_return for ListActivatableNames/ListNames");
+
+ goto out;
+ }
+
+ retry_get_property:
+
+ if (!dbus_message_get_args (message, &error,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING,
+ &srvs, &l,
+ DBUS_TYPE_INVALID))
+ {
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_verbose ("no memory to list services by %s\n", method);
+ dbus_error_free (&error);
+ _dbus_wait_for_memory ();
+ goto retry_get_property;
+ }
+ else
+ {
+ _dbus_assert (dbus_error_is_set (&error));
+ _dbus_warn ("Did not get the expected DBUS_TYPE_ARRAY from %s\n", method);
+ goto out;
+ }
+ } else {
+ *services = srvs;
+ *len = l;
+ }
+ }
+
+ if (!check_no_leftovers (context))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ dbus_error_free (&error);
+
+ if (message)
+ dbus_message_unref (message);
+
+ return retval;
+}
+
+/* returns TRUE if the correct thing happens,
+ * but the correct thing may include OOM errors.
+ */
+static dbus_bool_t
+check_list_services (BusContext *context,
+ DBusConnection *connection)
+{
+ DBusMessage *message;
+ DBusMessage *base_service_message;
+ const char *base_service;
+ dbus_uint32_t serial;
+ dbus_bool_t retval;
+ const char *existent = EXISTENT_SERVICE_NAME;
+ dbus_uint32_t flags;
+ char **services;
+ int len;
+
+ _dbus_verbose ("check_list_services for %p\n", connection);
+
+ if (!check_get_services (context, connection, "ListActivatableNames", &services, &len))
+ {
+ return TRUE;
+ }
+
+ if (!_dbus_string_array_contains ((const char **)services, existent))
+ {
+ _dbus_warn ("Did not get the expected %s from ListActivatableNames\n", existent);
+ return FALSE;
+ }
+
+ dbus_free_string_array (services);
+
+ base_service_message = NULL;
+
+ message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "StartServiceByName");
+
+ if (message == NULL)
+ return TRUE;
+
+ dbus_message_set_auto_start (message, FALSE);
+
+ flags = 0;
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &existent,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ if (!dbus_connection_send (connection, message, &serial))
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ bus_test_run_everything (context);
+
+ /* now wait for the message bus to hear back from the activated
+ * service.
+ */
+ block_connection_until_message_from_bus (context, connection, "activated service to connect");
+
+ bus_test_run_everything (context);
+
+ if (!dbus_connection_get_is_connected (connection))
+ {
+ _dbus_verbose ("connection was disconnected: %s %d\n", _DBUS_FUNCTION_NAME, __LINE__);
+ return TRUE;
+ }
+
+ retval = FALSE;
+
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after %s %d on %p\n",
+ "StartServiceByName", serial, connection);
+ goto out;
+ }
+
+ verbose_message_received (connection, message);
+ _dbus_verbose (" (after sending %s)\n", "StartServiceByName");
+
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR)
+ {
+ if (!dbus_message_has_sender (message, DBUS_SERVICE_DBUS))
+ {
+ _dbus_warn ("Message has wrong sender %s\n",
+ dbus_message_get_sender (message) ?
+ dbus_message_get_sender (message) : "(none)");
+ goto out;
+ }
+
+ if (dbus_message_is_error (message,
+ DBUS_ERROR_NO_MEMORY))
+ {
+ ; /* good, this is a valid response */
+ }
+ else if (dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_EXITED) ||
+ dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_CHILD_SIGNALED) ||
+ dbus_message_is_error (message,
+ DBUS_ERROR_SPAWN_EXEC_FAILED))
+ {
+ ; /* good, this is expected also */
+ }
+ else
+ {
+ _dbus_warn ("Did not expect error %s\n",
+ dbus_message_get_error_name (message));
+ goto out;
+ }
+ }
+ else
+ {
+ GotServiceInfo message_kind;
+
+ if (!check_base_service_activated (context, connection,
+ message, &base_service))
+ goto out;
+
+ base_service_message = message;
+ message = NULL;
+
+ /* We may need to block here for the test service to exit or finish up */
+ block_connection_until_message_from_bus (context, connection, "test service to exit or finish up");
+
+ message = dbus_connection_borrow_message (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Did not receive any messages after base service creation notification\n");
+ goto out;
+ }
+
+ message_kind = check_got_service_info (message);
+
+ dbus_connection_return_message (connection, message);
+ message = NULL;
+
+ switch (message_kind)
+ {
+ case GOT_SOMETHING_ELSE:
+ case GOT_ERROR:
+ case GOT_SERVICE_DELETED:
+ _dbus_warn ("Unexpected message after ActivateService "
+ "(should be an error or a service announcement)\n");
+ goto out;
+
+ case GOT_SERVICE_CREATED:
+ message = pop_message_waiting_for_memory (connection);
+ if (message == NULL)
+ {
+ _dbus_warn ("Failed to pop message we just put back! "
+ "should have been a NameOwnerChanged (creation)\n");
+ goto out;
+ }
+
+ if (!check_service_activated (context, connection, EXISTENT_SERVICE_NAME,
+ base_service, message))
+ goto out;
+
+ dbus_message_unref (message);
+ message = NULL;
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after successful activation\n");
+ goto out;
+ }
+
+ break;
+ }
+ }
+
+ if (!check_get_services (context, connection, "ListNames", &services, &len))
+ {
+ return TRUE;
+ }
+
+ if (!_dbus_string_array_contains ((const char **)services, existent))
+ {
+ _dbus_warn ("Did not get the expected %s from ListNames\n", existent);
+ goto out;
+ }
+
+ dbus_free_string_array (services);
+
+ if (!check_send_exit_to_service (context, connection,
+ EXISTENT_SERVICE_NAME, base_service))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ if (message)
+ dbus_message_unref (message);
+
+ if (base_service_message)
+ dbus_message_unref (base_service_message);
+
+ return retval;
+}
+
+typedef struct
+{
+ Check2Func func;
+ BusContext *context;
+ DBusConnection *connection;
+} Check2Data;
+
+static dbus_bool_t
+check_oom_check2_func (void *data)
+{
+ Check2Data *d = data;
+
+ if (! (* d->func) (d->context, d->connection))
+ return FALSE;
+
+ if (!check_no_leftovers (d->context))
+ {
+ _dbus_warn ("Messages were left over, should be covered by test suite\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+check2_try_iterations (BusContext *context,
+ DBusConnection *connection,
+ const char *description,
+ Check2Func func)
+{
+ Check2Data d;
+
+ d.func = func;
+ d.context = context;
+ d.connection = connection;
+
+ if (!_dbus_test_oom_handling (description, check_oom_check2_func,
+ &d))
+ {
+ _dbus_warn ("%s failed during oom\n", description);
+ _dbus_assert_not_reached ("test failed");
+ }
+}
+
+static dbus_bool_t
+setenv_TEST_LAUNCH_HELPER_CONFIG(const DBusString *test_data_dir,
+ const char *filename)
+{
+ DBusString full;
+ DBusString file;
+
+ if (!_dbus_string_init (&full))
+ return FALSE;
+
+ if (!_dbus_string_copy (test_data_dir, 0, &full, 0))
+ {
+ _dbus_string_free (&full);
+ return FALSE;
+ }
+
+ _dbus_string_init_const (&file, filename);
+
+ if (!_dbus_concat_dir_and_file (&full, &file))
+ {
+ _dbus_string_free (&full);
+ return FALSE;
+ }
+
+ _dbus_verbose ("Setting TEST_LAUNCH_HELPER_CONFIG to '%s'\n",
+ _dbus_string_get_const_data (&full));
+
+ _dbus_setenv ("TEST_LAUNCH_HELPER_CONFIG", _dbus_string_get_const_data (&full));
+
+ _dbus_string_free (&full);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_dispatch_test_conf (const DBusString *test_data_dir,
+ const char *filename,
+ dbus_bool_t use_launcher)
+{
+ BusContext *context;
+ DBusConnection *foo;
+ DBusConnection *bar;
+ DBusConnection *baz;
+ DBusError error;
+
+ /* save the config name for the activation helper */
+ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
+ _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
+
+ dbus_error_init (&error);
+
+ context = bus_context_new_test (test_data_dir, filename);
+ if (context == NULL)
+ return FALSE;
+
+ foo = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (foo == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (foo))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, foo);
+
+ if (!check_hello_message (context, foo))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_double_hello_message (context, foo))
+ _dbus_assert_not_reached ("double hello message failed");
+
+ if (!check_add_match_all (context, foo))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ bar = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (bar == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (bar))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, bar);
+
+ if (!check_hello_message (context, bar))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_add_match_all (context, bar))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ baz = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (baz == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (baz))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, baz);
+
+ if (!check_hello_message (context, baz))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_add_match_all (context, baz))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ if (!check_get_connection_unix_user (context, baz))
+ _dbus_assert_not_reached ("GetConnectionUnixUser message failed");
+
+ if (!check_get_connection_unix_process_id (context, baz))
+ _dbus_assert_not_reached ("GetConnectionUnixProcessID message failed");
+
+ if (!check_list_services (context, baz))
+ _dbus_assert_not_reached ("ListActivatableNames message failed");
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after setting up initial connections\n");
+ _dbus_assert_not_reached ("initial connection setup failed");
+ }
+
+ check1_try_iterations (context, "create_and_hello",
+ check_hello_connection);
+
+ check2_try_iterations (context, foo, "nonexistent_service_no_auto_start",
+ check_nonexistent_service_no_auto_start);
+
+#ifdef DBUS_WIN_FIXME
+ _dbus_warn("TODO: dispatch.c segfault_service_no_auto_start test\n");
+#else
+ check2_try_iterations (context, foo, "segfault_service_no_auto_start",
+ check_segfault_service_no_auto_start);
+#endif
+
+ check2_try_iterations (context, foo, "existent_service_no_auto_start",
+ check_existent_service_no_auto_start);
+
+ check2_try_iterations (context, foo, "nonexistent_service_auto_start",
+ check_nonexistent_service_auto_start);
+
+
+#ifdef DBUS_WIN_FIXME
+ _dbus_warn("TODO: dispatch.c segfault_service_auto_start test\n");
+#else
+ /* only do the segfault test if we are not using the launcher */
+ check2_try_iterations (context, foo, "segfault_service_auto_start",
+ check_segfault_service_auto_start);
+#endif
+
+ /* only do the shell fail test if we are not using the launcher */
+ check2_try_iterations (context, foo, "shell_fail_service_auto_start",
+ check_shell_fail_service_auto_start);
+
+ /* specific to launcher */
+ if (use_launcher)
+ if (!check_launch_service_file_missing (context, foo))
+ _dbus_assert_not_reached ("did not get service file not found error");
+
+#if 0
+ /* Note: need to resolve some issues with the testing code in order to run
+ * this in oom (handle that we sometimes don't get replies back from the bus
+ * when oom happens, without blocking the test).
+ */
+ check2_try_iterations (context, foo, "existent_service_auto_auto_start",
+ check_existent_service_auto_start);
+#endif
+
+ if (!check_existent_service_auto_start (context, foo))
+ _dbus_assert_not_reached ("existent service auto start failed");
+
+ if (!check_shell_service_success_auto_start (context, foo))
+ _dbus_assert_not_reached ("shell success service auto start failed");
+
+ _dbus_verbose ("Disconnecting foo, bar, and baz\n");
+
+ kill_client_connection_unchecked (foo);
+ kill_client_connection_unchecked (bar);
+ kill_client_connection_unchecked (baz);
+
+ bus_context_unref (context);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
+ const char *filename)
+{
+ BusContext *context;
+ DBusConnection *foo;
+ DBusError error;
+
+ /* save the config name for the activation helper */
+ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
+ _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
+
+ dbus_error_init (&error);
+
+ context = bus_context_new_test (test_data_dir, filename);
+ if (context == NULL)
+ return FALSE;
+
+ foo = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (foo == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (foo))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, foo);
+
+ if (!check_hello_message (context, foo))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_double_hello_message (context, foo))
+ _dbus_assert_not_reached ("double hello message failed");
+
+ if (!check_add_match_all (context, foo))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ /* this only tests the activation.c user check */
+ if (!check_launch_service_user_missing (context, foo))
+ _dbus_assert_not_reached ("user missing did not trigger error");
+
+ /* this only tests the desktop.c exec check */
+ if (!check_launch_service_exec_missing (context, foo))
+ _dbus_assert_not_reached ("exec missing did not trigger error");
+
+ /* this only tests the desktop.c service check */
+ if (!check_launch_service_service_missing (context, foo))
+ _dbus_assert_not_reached ("service missing did not trigger error");
+
+ _dbus_verbose ("Disconnecting foo\n");
+
+ kill_client_connection_unchecked (foo);
+
+ bus_context_unref (context);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_dispatch_test (const DBusString *test_data_dir)
+{
+ /* run normal activation tests */
+ _dbus_verbose ("Normal activation tests\n");
+ if (!bus_dispatch_test_conf (test_data_dir,
+ "valid-config-files/debug-allow-all.conf", FALSE))
+ return FALSE;
+
+ /* run launch-helper activation tests */
+ _dbus_verbose ("Launch helper activation tests\n");
+ if (!bus_dispatch_test_conf (test_data_dir,
+ "valid-config-files-system/debug-allow-all-pass.conf", TRUE))
+ return FALSE;
+
+ /* run select launch-helper activation tests on broken service files */
+ if (!bus_dispatch_test_conf_fail (test_data_dir,
+ "valid-config-files-system/debug-allow-all-fail.conf"))
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_dispatch_sha1_test (const DBusString *test_data_dir)
+{
+ BusContext *context;
+ DBusConnection *foo;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ /* Test SHA1 authentication */
+ _dbus_verbose ("Testing SHA1 context\n");
+
+ context = bus_context_new_test (test_data_dir,
+ "valid-config-files/debug-allow-all-sha1.conf");
+ if (context == NULL)
+ return FALSE;
+
+ foo = dbus_connection_open_private ("debug-pipe:name=test-server", &error);
+ if (foo == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (foo))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, foo);
+
+ if (!check_hello_message (context, foo))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_add_match_all (context, foo))
+ _dbus_assert_not_reached ("addmatch message failed");
+
+ if (!check_no_leftovers (context))
+ {
+ _dbus_warn ("Messages were left over after setting up initial SHA-1 connection\n");
+ _dbus_assert_not_reached ("initial connection setup failed");
+ }
+
+ check1_try_iterations (context, "create_and_hello_sha1",
+ check_hello_connection);
+
+ kill_client_connection_unchecked (foo);
+
+ bus_context_unref (context);
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/bus/dispatch.h b/bus/dispatch.h
new file mode 100644
index 00000000..fb5ba7a5
--- /dev/null
+++ b/bus/dispatch.h
@@ -0,0 +1,38 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dispatch.h Message dispatcher
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (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
+ *
+ */
+
+#ifndef BUS_DISPATCH_H
+#define BUS_DISPATCH_H
+
+#include <dbus/dbus.h>
+#include "connection.h"
+
+dbus_bool_t bus_dispatch_add_connection (DBusConnection *connection);
+void bus_dispatch_remove_connection (DBusConnection *connection);
+dbus_bool_t bus_dispatch_matches (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *recipient,
+ DBusMessage *message,
+ DBusError *error);
+
+#endif /* BUS_DISPATCH_H */
diff --git a/bus/driver.c b/bus/driver.c
new file mode 100644
index 00000000..5e8a7a26
--- /dev/null
+++ b/bus/driver.c
@@ -0,0 +1,2022 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* driver.c Bus client (driver)
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003, 2004, 2005 Red Hat, Inc.
+ *
+ * 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 "activation.h"
+#include "connection.h"
+#include "driver.h"
+#include "dispatch.h"
+#include "services.h"
+#include "selinux.h"
+#include "signals.h"
+#include "utils.h"
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-message.h>
+#include <dbus/dbus-marshal-recursive.h>
+#include <string.h>
+
+static dbus_bool_t bus_driver_send_welcome_message (DBusConnection *connection,
+ DBusMessage *hello_message,
+ BusTransaction *transaction,
+ DBusError *error);
+
+dbus_bool_t
+bus_driver_send_service_owner_changed (const char *service_name,
+ const char *old_owner,
+ const char *new_owner,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ DBusMessage *message;
+ dbus_bool_t retval;
+ const char *null_service;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ null_service = "";
+ _dbus_verbose ("sending name owner changed: %s [%s -> %s]\n",
+ service_name,
+ old_owner ? old_owner : null_service,
+ new_owner ? new_owner : null_service);
+
+ message = dbus_message_new_signal (DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged");
+
+ if (message == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
+ goto oom;
+
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_STRING, old_owner ? &old_owner : &null_service,
+ DBUS_TYPE_STRING, new_owner ? &new_owner : &null_service,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ _dbus_assert (dbus_message_has_signature (message, "sss"));
+
+ retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
+ dbus_message_unref (message);
+
+ return retval;
+
+ oom:
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+}
+
+dbus_bool_t
+bus_driver_send_service_lost (DBusConnection *connection,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ DBusMessage *message;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ message = dbus_message_new_signal (DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameLost");
+
+ if (message == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_set_destination (message, bus_connection_get_name (connection)) ||
+ !dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, message))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+}
+
+dbus_bool_t
+bus_driver_send_service_acquired (DBusConnection *connection,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ DBusMessage *message;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ message = dbus_message_new_signal (DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameAcquired");
+
+ if (message == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_set_destination (message, bus_connection_get_name (connection)) ||
+ !dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &service_name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, message))
+ {
+ dbus_message_unref (message);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_message_unref (message);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+create_unique_client_name (BusRegistry *registry,
+ DBusString *str)
+{
+ /* We never want to use the same unique client name twice, because
+ * we want to guarantee that if you send a message to a given unique
+ * name, you always get the same application. So we use two numbers
+ * for INT_MAX * INT_MAX combinations, should be pretty safe against
+ * wraparound.
+ */
+ /* FIXME these should be in BusRegistry rather than static vars */
+ static int next_major_number = 0;
+ static int next_minor_number = 0;
+ int len;
+
+ len = _dbus_string_get_length (str);
+
+ while (TRUE)
+ {
+ /* start out with 1-0, go to 1-1, 1-2, 1-3,
+ * up to 1-MAXINT, then 2-0, 2-1, etc.
+ */
+ if (next_minor_number <= 0)
+ {
+ next_major_number += 1;
+ next_minor_number = 0;
+ if (next_major_number <= 0)
+ _dbus_assert_not_reached ("INT_MAX * INT_MAX clients were added");
+ }
+
+ _dbus_assert (next_major_number > 0);
+ _dbus_assert (next_minor_number >= 0);
+
+ /* appname:MAJOR-MINOR */
+
+ if (!_dbus_string_append (str, ":"))
+ return FALSE;
+
+ if (!_dbus_string_append_int (str, next_major_number))
+ return FALSE;
+
+ if (!_dbus_string_append (str, "."))
+ return FALSE;
+
+ if (!_dbus_string_append_int (str, next_minor_number))
+ return FALSE;
+
+ next_minor_number += 1;
+
+ /* Check if a client with the name exists */
+ if (bus_registry_lookup (registry, str) == NULL)
+ break;
+
+ /* drop the number again, try the next one. */
+ _dbus_string_set_length (str, len);
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_driver_handle_hello (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusString unique_name;
+ BusService *service;
+ dbus_bool_t retval;
+ BusRegistry *registry;
+ BusConnections *connections;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (bus_connection_is_active (connection))
+ {
+ /* We already handled an Hello message for this connection. */
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Already handled an Hello message");
+ return FALSE;
+ }
+
+ /* Note that when these limits are exceeded we don't disconnect the
+ * connection; we just sort of leave it hanging there until it times
+ * out or disconnects itself or is dropped due to the max number of
+ * incomplete connections. It's even OK if the connection wants to
+ * retry the hello message, we support that.
+ */
+ connections = bus_connection_get_connections (connection);
+ if (!bus_connections_check_limits (connections, connection,
+ error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&unique_name))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ retval = FALSE;
+
+ registry = bus_connection_get_registry (connection);
+
+ if (!create_unique_client_name (registry, &unique_name))
+ {
+ BUS_SET_OOM (error);
+ goto out_0;
+ }
+
+ if (!bus_connection_complete (connection, &unique_name, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto out_0;
+ }
+
+ if (!dbus_message_set_sender (message,
+ bus_connection_get_name (connection)))
+ {
+ BUS_SET_OOM (error);
+ goto out_0;
+ }
+
+ if (!bus_driver_send_welcome_message (connection, message, transaction, error))
+ goto out_0;
+
+ /* Create the service */
+ service = bus_registry_ensure (registry,
+ &unique_name, connection, 0, transaction, error);
+ if (service == NULL)
+ goto out_0;
+
+ _dbus_assert (bus_connection_is_active (connection));
+ retval = TRUE;
+
+ out_0:
+ _dbus_string_free (&unique_name);
+ return retval;
+}
+
+static dbus_bool_t
+bus_driver_send_welcome_message (DBusConnection *connection,
+ DBusMessage *hello_message,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ DBusMessage *welcome;
+ const char *name;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ name = bus_connection_get_name (connection);
+ _dbus_assert (name != NULL);
+
+ welcome = dbus_message_new_method_return (hello_message);
+ if (welcome == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!dbus_message_append_args (welcome,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ {
+ dbus_message_unref (welcome);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ _dbus_assert (dbus_message_has_signature (welcome, DBUS_TYPE_STRING_AS_STRING));
+
+ if (!bus_transaction_send_from_driver (transaction, connection, welcome))
+ {
+ dbus_message_unref (welcome);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_message_unref (welcome);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+bus_driver_handle_list_services (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ int len;
+ char **services;
+ BusRegistry *registry;
+ int i;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_registry_list_services (registry, &services, &len))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &sub))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ {
+ /* Include the bus driver in the list */
+ const char *v_STRING = DBUS_SERVICE_DBUS;
+ if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING,
+ &v_STRING))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ i = 0;
+ while (i < len)
+ {
+ if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING,
+ &services[i]))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ ++i;
+ }
+
+ dbus_free_string_array (services);
+
+ if (!dbus_message_iter_close_container (&iter, &sub))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_message_unref (reply);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+bus_driver_handle_list_activatable_services (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ int len;
+ char **services;
+ BusActivation *activation;
+ int i;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ activation = bus_connection_get_activation (connection);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_activation_list_services (activation, &services, &len))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &sub))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ {
+ /* Include the bus driver in the list */
+ const char *v_STRING = DBUS_SERVICE_DBUS;
+ if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING,
+ &v_STRING))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+
+ i = 0;
+ while (i < len)
+ {
+ if (!dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING,
+ &services[i]))
+ {
+ dbus_free_string_array (services);
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ ++i;
+ }
+
+ dbus_free_string_array (services);
+
+ if (!dbus_message_iter_close_container (&iter, &sub))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ dbus_message_unref (reply);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ else
+ {
+ dbus_message_unref (reply);
+ return TRUE;
+ }
+}
+
+static dbus_bool_t
+bus_driver_handle_acquire_service (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ DBusString service_name;
+ const char *name;
+ dbus_uint32_t service_reply;
+ dbus_uint32_t flags;
+ dbus_bool_t retval;
+ BusRegistry *registry;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ _dbus_verbose ("Trying to own name %s with flags 0x%x\n", name, flags);
+
+ retval = FALSE;
+ reply = NULL;
+
+ _dbus_string_init_const (&service_name, name);
+
+ if (!bus_registry_acquire_service (registry, connection,
+ &service_name, flags,
+ &service_reply, transaction,
+ error))
+ goto out;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, &service_reply, DBUS_TYPE_INVALID))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (reply)
+ dbus_message_unref (reply);
+ return retval;
+}
+
+static dbus_bool_t
+bus_driver_handle_release_service (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ DBusString service_name;
+ const char *name;
+ dbus_uint32_t service_reply;
+ dbus_bool_t retval;
+ BusRegistry *registry;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ _dbus_verbose ("Trying to release name %s\n", name);
+
+ retval = FALSE;
+ reply = NULL;
+
+ _dbus_string_init_const (&service_name, name);
+
+ if (!bus_registry_release_service (registry, connection,
+ &service_name, &service_reply,
+ transaction, error))
+ goto out;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!dbus_message_append_args (reply, DBUS_TYPE_UINT32, &service_reply, DBUS_TYPE_INVALID))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (reply)
+ dbus_message_unref (reply);
+ return retval;
+}
+
+static dbus_bool_t
+bus_driver_handle_service_exists (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+ DBusString service_name;
+ BusService *service;
+ dbus_bool_t service_exists;
+ const char *name;
+ dbus_bool_t retval;
+ BusRegistry *registry;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ retval = FALSE;
+
+ if (strcmp (name, DBUS_SERVICE_DBUS) == 0)
+ {
+ service_exists = TRUE;
+ }
+ else
+ {
+ _dbus_string_init_const (&service_name, name);
+ service = bus_registry_lookup (registry, &service_name);
+ service_exists = service != NULL;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!dbus_message_append_args (reply,
+ DBUS_TYPE_BOOLEAN, &service_exists,
+ 0))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (reply)
+ dbus_message_unref (reply);
+
+ return retval;
+}
+
+static dbus_bool_t
+bus_driver_handle_activate_service (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ dbus_uint32_t flags;
+ const char *name;
+ dbus_bool_t retval;
+ BusActivation *activation;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ activation = bus_connection_get_activation (connection);
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_UINT32, &flags,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_verbose ("No memory to get arguments to StartServiceByName\n");
+ return FALSE;
+ }
+
+ retval = FALSE;
+
+ if (!bus_activation_activate_service (activation, connection, transaction, FALSE,
+ message, name, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_verbose ("bus_activation_activate_service() failed\n");
+ goto out;
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+static dbus_bool_t
+send_ack_reply (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply;
+
+ if (dbus_message_get_no_reply (message))
+ return TRUE;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_transaction_send_from_driver (transaction, connection, reply))
+ {
+ BUS_SET_OOM (error);
+ dbus_message_unref (reply);
+ return FALSE;
+ }
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_driver_handle_update_activation_environment (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ dbus_bool_t retval;
+ BusActivation *activation;
+ DBusMessageIter iter;
+ DBusMessageIter dict_iter;
+ DBusMessageIter dict_entry_iter;
+ int msg_type;
+ int array_type;
+ int key_type;
+ DBusList *keys, *key_link;
+ DBusList *values, *value_link;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ activation = bus_connection_get_activation (connection);
+
+ dbus_message_iter_init (message, &iter);
+
+ /* The message signature has already been checked for us,
+ * so let's just assert it's right.
+ */
+ msg_type = dbus_message_iter_get_arg_type (&iter);
+
+ _dbus_assert (msg_type == DBUS_TYPE_ARRAY);
+
+ dbus_message_iter_recurse (&iter, &dict_iter);
+
+ retval = FALSE;
+
+ /* Then loop through the sent dictionary, add the location of
+ * the environment keys and values to lists. The result will
+ * be in reverse order, so we don't have to constantly search
+ * for the end of the list in a loop.
+ */
+ keys = NULL;
+ values = NULL;
+ while ((array_type = dbus_message_iter_get_arg_type (&dict_iter)) == DBUS_TYPE_DICT_ENTRY)
+ {
+ dbus_message_iter_recurse (&dict_iter, &dict_entry_iter);
+
+ while ((key_type = dbus_message_iter_get_arg_type (&dict_entry_iter)) == DBUS_TYPE_STRING)
+ {
+ char *key;
+ char *value;
+ int value_type;
+
+ dbus_message_iter_get_basic (&dict_entry_iter, &key);
+ dbus_message_iter_next (&dict_entry_iter);
+
+ value_type = dbus_message_iter_get_arg_type (&dict_entry_iter);
+
+ if (value_type != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic (&dict_entry_iter, &value);
+
+ if (!_dbus_list_append (&keys, key))
+ {
+ BUS_SET_OOM (error);
+ break;
+ }
+
+ if (!_dbus_list_append (&values, value))
+ {
+ BUS_SET_OOM (error);
+ break;
+ }
+
+ dbus_message_iter_next (&dict_entry_iter);
+ }
+
+ if (key_type != DBUS_TYPE_INVALID)
+ break;
+
+ dbus_message_iter_next (&dict_iter);
+ }
+
+ if (array_type != DBUS_TYPE_INVALID)
+ goto out;
+
+ _dbus_assert (_dbus_list_get_length (&keys) == _dbus_list_get_length (&values));
+
+ key_link = keys;
+ value_link = values;
+ while (key_link != NULL)
+ {
+ const char *key;
+ const char *value;
+
+ key = key_link->data;
+ value = value_link->data;
+
+ if (!bus_activation_set_environment_variable (activation,
+ key, value, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_verbose ("bus_activation_set_environment_variable() failed\n");
+ break;
+ }
+ key_link = _dbus_list_get_next_link (&keys, key_link);
+ value_link = _dbus_list_get_next_link (&values, value_link);
+ }
+
+ /* FIXME: We can fail early having set only some of the environment variables,
+ * (because of OOM failure). It's sort of hard to fix and it doesn't really
+ * matter, so we're punting for now.
+ */
+ if (key_link != NULL)
+ goto out;
+
+ if (!send_ack_reply (connection, transaction,
+ message, error))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ _dbus_list_clear (&keys);
+ _dbus_list_clear (&values);
+ return retval;
+}
+
+static dbus_bool_t
+bus_driver_handle_add_match (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusMatchRule *rule;
+ const char *text;
+ DBusString str;
+ BusMatchmaker *matchmaker;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ text = NULL;
+ rule = NULL;
+
+ if (bus_connection_get_n_match_rules (connection) >=
+ bus_context_get_max_match_rules_per_connection (bus_transaction_get_context (transaction)))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Connection \"%s\" is not allowed to add more match rules "
+ "(increase limits in configuration file if required)",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)");
+ goto failed;
+ }
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ {
+ _dbus_verbose ("No memory to get arguments to AddMatch\n");
+ goto failed;
+ }
+
+ _dbus_string_init_const (&str, text);
+
+ rule = bus_match_rule_parse (connection, &str, error);
+ if (rule == NULL)
+ goto failed;
+
+ matchmaker = bus_connection_get_matchmaker (connection);
+
+ if (!bus_matchmaker_add_rule (matchmaker, rule))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!send_ack_reply (connection, transaction,
+ message, error))
+ {
+ bus_matchmaker_remove_rule (matchmaker, rule);
+ goto failed;
+ }
+
+ bus_match_rule_unref (rule);
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (rule)
+ bus_match_rule_unref (rule);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_remove_match (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusMatchRule *rule;
+ const char *text;
+ DBusString str;
+ BusMatchmaker *matchmaker;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ text = NULL;
+ rule = NULL;
+
+ if (!dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ {
+ _dbus_verbose ("No memory to get arguments to RemoveMatch\n");
+ goto failed;
+ }
+
+ _dbus_string_init_const (&str, text);
+
+ rule = bus_match_rule_parse (connection, &str, error);
+ if (rule == NULL)
+ goto failed;
+
+ /* Send the ack before we remove the rule, since the ack is undone
+ * on transaction cancel, but rule removal isn't.
+ */
+ if (!send_ack_reply (connection, transaction,
+ message, error))
+ goto failed;
+
+ matchmaker = bus_connection_get_matchmaker (connection);
+
+ if (!bus_matchmaker_remove_rule_by_value (matchmaker, rule, error))
+ goto failed;
+
+ bus_match_rule_unref (rule);
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (rule)
+ bus_match_rule_unref (rule);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_service_owner (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *text;
+ const char *base_name;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *service;
+ DBusMessage *reply;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ text = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_string_init_const (&str, text);
+ service = bus_registry_lookup (registry, &str);
+ if (service == NULL &&
+ _dbus_string_equal_c_str (&str, DBUS_SERVICE_DBUS))
+ {
+ /* ORG_FREEDESKTOP_DBUS owns itself */
+ base_name = DBUS_SERVICE_DBUS;
+ }
+ else if (service == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get owner of name '%s': no such name", text);
+ goto failed;
+ }
+ else
+ {
+ base_name = bus_connection_get_name (bus_service_get_primary_owners_connection (service));
+ if (base_name == NULL)
+ {
+ /* FIXME - how is this error possible? */
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not determine unique name for '%s'", text);
+ goto failed;
+ }
+ _dbus_assert (*base_name == ':');
+ }
+
+ _dbus_assert (base_name != NULL);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (! dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &base_name,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_list_queued_owners (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *text;
+ DBusList *base_names;
+ DBusList *link;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *service;
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ char *dbus_service_name = DBUS_SERVICE_DBUS;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ base_names = NULL;
+ text = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &text,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_string_init_const (&str, text);
+ service = bus_registry_lookup (registry, &str);
+ if (service == NULL &&
+ _dbus_string_equal_c_str (&str, DBUS_SERVICE_DBUS))
+ {
+ /* ORG_FREEDESKTOP_DBUS owns itself */
+ if (! _dbus_list_append (&base_names, dbus_service_name))
+ goto oom;
+ }
+ else if (service == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get owners of name '%s': no such name", text);
+ goto failed;
+ }
+ else
+ {
+ if (!bus_service_list_queued_owners (service,
+ &base_names,
+ error))
+ goto failed;
+ }
+
+ _dbus_assert (base_names != NULL);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ dbus_message_iter_init_append (reply, &iter);
+ if (!dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter))
+ goto oom;
+
+ link = _dbus_list_get_first_link (&base_names);
+ while (link != NULL)
+ {
+ char *uname;
+
+ _dbus_assert (link->data != NULL);
+ uname = (char *)link->data;
+
+ if (!dbus_message_iter_append_basic (&array_iter,
+ DBUS_TYPE_STRING,
+ &uname))
+ goto oom;
+
+ link = _dbus_list_get_next_link (&base_names, link);
+ }
+
+ if (! dbus_message_iter_close_container (&iter, &array_iter))
+ goto oom;
+
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+
+ if (base_names)
+ _dbus_list_clear (&base_names);
+
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_connection_unix_user (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *service;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *serv;
+ DBusConnection *conn;
+ DBusMessage *reply;
+ unsigned long uid;
+ dbus_uint32_t uid32;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ service = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_verbose ("asked for UID of connection %s\n", service);
+
+ _dbus_string_init_const (&str, service);
+ serv = bus_registry_lookup (registry, &str);
+ if (serv == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get UID of name '%s': no such name", service);
+ goto failed;
+ }
+
+ conn = bus_service_get_primary_owners_connection (serv);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (!dbus_connection_get_unix_user (conn, &uid))
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_FAILED,
+ "Could not determine UID for '%s'", service);
+ goto failed;
+ }
+
+ uid32 = uid;
+ if (! dbus_message_append_args (reply,
+ DBUS_TYPE_UINT32, &uid32,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_connection_unix_process_id (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *service;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *serv;
+ DBusConnection *conn;
+ DBusMessage *reply;
+ unsigned long pid;
+ dbus_uint32_t pid32;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ service = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_verbose ("asked for PID of connection %s\n", service);
+
+ _dbus_string_init_const (&str, service);
+ serv = bus_registry_lookup (registry, &str);
+ if (serv == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get PID of name '%s': no such name", service);
+ goto failed;
+ }
+
+ conn = bus_service_get_primary_owners_connection (serv);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (!dbus_connection_get_unix_process_id (conn, &pid))
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN,
+ "Could not determine PID for '%s'", service);
+ goto failed;
+ }
+
+ pid32 = pid;
+ if (! dbus_message_append_args (reply,
+ DBUS_TYPE_UINT32, &pid32,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_adt_audit_session_data (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *service;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *serv;
+ DBusConnection *conn;
+ DBusMessage *reply;
+ void *data = NULL;
+ dbus_uint32_t data_size;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ service = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_verbose ("asked for audit session data for connection %s\n", service);
+
+ _dbus_string_init_const (&str, service);
+ serv = bus_registry_lookup (registry, &str);
+ if (serv == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get audit session data for name '%s': no such name", service);
+ goto failed;
+ }
+
+ conn = bus_service_get_primary_owners_connection (serv);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (!dbus_connection_get_adt_audit_session_data (conn, &data, &data_size) || data == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN,
+ "Could not determine audit session data for '%s'", service);
+ goto failed;
+ }
+
+ if (! dbus_message_append_args (reply,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_size,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_connection_selinux_security_context (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *service;
+ DBusString str;
+ BusRegistry *registry;
+ BusService *serv;
+ DBusConnection *conn;
+ DBusMessage *reply;
+ BusSELinuxID *context;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ registry = bus_connection_get_registry (connection);
+
+ service = NULL;
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_INVALID))
+ goto failed;
+
+ _dbus_verbose ("asked for security context of connection %s\n", service);
+
+ _dbus_string_init_const (&str, service);
+ serv = bus_registry_lookup (registry, &str);
+ if (serv == NULL)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_NAME_HAS_NO_OWNER,
+ "Could not get security context of name '%s': no such name", service);
+ goto failed;
+ }
+
+ conn = bus_service_get_primary_owners_connection (serv);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ context = bus_connection_get_selinux_id (conn);
+ if (!context)
+ {
+ dbus_set_error (error,
+ DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN,
+ "Could not determine security context for '%s'", service);
+ goto failed;
+ }
+
+ if (! bus_selinux_append_context (reply, context, error))
+ goto failed;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_reload_config (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusContext *context;
+ DBusMessage *reply;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ reply = NULL;
+
+ context = bus_connection_get_context (connection);
+ if (!bus_context_reload_config (context, error))
+ goto failed;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_get_id (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusContext *context;
+ DBusMessage *reply;
+ DBusString uuid;
+ const char *v_STRING;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!_dbus_string_init (&uuid))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ reply = NULL;
+
+ context = bus_connection_get_context (connection);
+ if (!bus_context_get_id (context, &uuid))
+ goto oom;
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ v_STRING = _dbus_string_get_const_data (&uuid);
+ if (!dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &v_STRING,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ _dbus_assert (dbus_message_has_signature (reply, "s"));
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ _dbus_string_free (&uuid);
+ dbus_message_unref (reply);
+ return TRUE;
+
+ oom:
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ BUS_SET_OOM (error);
+
+ if (reply)
+ dbus_message_unref (reply);
+ _dbus_string_free (&uuid);
+ return FALSE;
+}
+
+/* For speed it might be useful to sort this in order of
+ * frequency of use (but doesn't matter with only a few items
+ * anyhow)
+ */
+static struct
+{
+ const char *name;
+ const char *in_args;
+ const char *out_args;
+ dbus_bool_t (* handler) (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error);
+} message_handlers[] = {
+ { "Hello",
+ "",
+ DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_hello },
+ { "RequestName",
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
+ DBUS_TYPE_UINT32_AS_STRING,
+ bus_driver_handle_acquire_service },
+ { "ReleaseName",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_UINT32_AS_STRING,
+ bus_driver_handle_release_service },
+ { "StartServiceByName",
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
+ DBUS_TYPE_UINT32_AS_STRING,
+ bus_driver_handle_activate_service },
+ { "UpdateActivationEnvironment",
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ "",
+ bus_driver_handle_update_activation_environment },
+ { "NameHasOwner",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_BOOLEAN_AS_STRING,
+ bus_driver_handle_service_exists },
+ { "ListNames",
+ "",
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_list_services },
+ { "ListActivatableNames",
+ "",
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_list_activatable_services },
+ { "AddMatch",
+ DBUS_TYPE_STRING_AS_STRING,
+ "",
+ bus_driver_handle_add_match },
+ { "RemoveMatch",
+ DBUS_TYPE_STRING_AS_STRING,
+ "",
+ bus_driver_handle_remove_match },
+ { "GetNameOwner",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_get_service_owner },
+ { "ListQueuedOwners",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_list_queued_owners },
+ { "GetConnectionUnixUser",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_UINT32_AS_STRING,
+ bus_driver_handle_get_connection_unix_user },
+ { "GetConnectionUnixProcessID",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_UINT32_AS_STRING,
+ bus_driver_handle_get_connection_unix_process_id },
+ { "GetAdtAuditSessionData",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
+ bus_driver_handle_get_adt_audit_session_data },
+ { "GetConnectionSELinuxSecurityContext",
+ DBUS_TYPE_STRING_AS_STRING,
+ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
+ bus_driver_handle_get_connection_selinux_security_context },
+ { "ReloadConfig",
+ "",
+ "",
+ bus_driver_handle_reload_config },
+ { "GetId",
+ "",
+ DBUS_TYPE_STRING_AS_STRING,
+ bus_driver_handle_get_id }
+};
+
+static dbus_bool_t
+write_args_for_direction (DBusString *xml,
+ const char *signature,
+ dbus_bool_t in)
+{
+ DBusTypeReader typereader;
+ DBusString sigstr;
+ int current_type;
+
+ _dbus_string_init_const (&sigstr, signature);
+ _dbus_type_reader_init_types_only (&typereader, &sigstr, 0);
+
+ while ((current_type = _dbus_type_reader_get_current_type (&typereader)) != DBUS_TYPE_INVALID)
+ {
+ const DBusString *subsig;
+ int start, len;
+
+ _dbus_type_reader_get_signature (&typereader, &subsig, &start, &len);
+ if (!_dbus_string_append_printf (xml, " <arg direction=\"%s\" type=\"",
+ in ? "in" : "out"))
+ goto oom;
+ if (!_dbus_string_append_len (xml,
+ _dbus_string_get_const_data (subsig) + start,
+ len))
+ goto oom;
+ if (!_dbus_string_append (xml, "\"/>\n"))
+ goto oom;
+
+ _dbus_type_reader_next (&typereader);
+ }
+ return TRUE;
+ oom:
+ return FALSE;
+}
+
+dbus_bool_t
+bus_driver_generate_introspect_string (DBusString *xml)
+{
+ int i;
+
+ if (!_dbus_string_append (xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
+ return FALSE;
+ if (!_dbus_string_append (xml, "<node>\n"))
+ return FALSE;
+ if (!_dbus_string_append_printf (xml, " <interface name=\"%s\">\n", DBUS_INTERFACE_INTROSPECTABLE))
+ return FALSE;
+ if (!_dbus_string_append (xml, " <method name=\"Introspect\">\n"))
+ return FALSE;
+ if (!_dbus_string_append_printf (xml, " <arg name=\"data\" direction=\"out\" type=\"%s\"/>\n", DBUS_TYPE_STRING_AS_STRING))
+ return FALSE;
+ if (!_dbus_string_append (xml, " </method>\n"))
+ return FALSE;
+ if (!_dbus_string_append (xml, " </interface>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <interface name=\"%s\">\n",
+ DBUS_INTERFACE_DBUS))
+ return FALSE;
+
+ i = 0;
+ while (i < _DBUS_N_ELEMENTS (message_handlers))
+ {
+
+ if (!_dbus_string_append_printf (xml, " <method name=\"%s\">\n",
+ message_handlers[i].name))
+ return FALSE;
+
+ if (!write_args_for_direction (xml, message_handlers[i].in_args, TRUE))
+ return FALSE;
+
+ if (!write_args_for_direction (xml, message_handlers[i].out_args, FALSE))
+ return FALSE;
+
+ if (!_dbus_string_append (xml, " </method>\n"))
+ return FALSE;
+
+ ++i;
+ }
+
+ if (!_dbus_string_append_printf (xml, " <signal name=\"NameOwnerChanged\">\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " </signal>\n"))
+ return FALSE;
+
+
+
+ if (!_dbus_string_append_printf (xml, " <signal name=\"NameLost\">\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " </signal>\n"))
+ return FALSE;
+
+
+
+ if (!_dbus_string_append_printf (xml, " <signal name=\"NameAcquired\">\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " <arg type=\"s\"/>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append_printf (xml, " </signal>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append (xml, " </interface>\n"))
+ return FALSE;
+
+ if (!_dbus_string_append (xml, "</node>\n"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_driver_handle_introspect (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusString xml;
+ DBusMessage *reply;
+ const char *v_STRING;
+
+ _dbus_verbose ("Introspect() on bus driver\n");
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ reply = NULL;
+
+ if (! dbus_message_get_args (message, error,
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&xml))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!bus_driver_generate_introspect_string (&xml))
+ goto oom;
+
+ v_STRING = _dbus_string_get_const_data (&xml);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (! dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &v_STRING,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ dbus_message_unref (reply);
+ _dbus_string_free (&xml);
+
+ return TRUE;
+
+ oom:
+ BUS_SET_OOM (error);
+
+ if (reply)
+ dbus_message_unref (reply);
+
+ _dbus_string_free (&xml);
+
+ return FALSE;
+}
+
+dbus_bool_t
+bus_driver_handle_message (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ const char *name, *sender, *interface;
+ int i;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ _dbus_verbose ("Driver got a non-method-call message, ignoring\n");
+ return TRUE; /* we just ignore this */
+ }
+
+ if (dbus_message_is_method_call (message,
+ DBUS_INTERFACE_INTROSPECTABLE,
+ "Introspect"))
+ return bus_driver_handle_introspect (connection, transaction, message, error);
+
+ interface = dbus_message_get_interface (message);
+ if (interface == NULL)
+ interface = DBUS_INTERFACE_DBUS;
+
+ _dbus_assert (dbus_message_get_member (message) != NULL);
+
+ name = dbus_message_get_member (message);
+ sender = dbus_message_get_sender (message);
+
+ if (strcmp (interface,
+ DBUS_INTERFACE_DBUS) != 0)
+ {
+ _dbus_verbose ("Driver got message to unknown interface \"%s\"\n",
+ interface);
+ goto unknown;
+ }
+
+ _dbus_verbose ("Driver got a method call: %s\n",
+ dbus_message_get_member (message));
+
+ /* security checks should have kept this from getting here */
+ _dbus_assert (sender != NULL || strcmp (name, "Hello") == 0);
+
+ i = 0;
+ while (i < _DBUS_N_ELEMENTS (message_handlers))
+ {
+ if (strcmp (message_handlers[i].name, name) == 0)
+ {
+ _dbus_verbose ("Found driver handler for %s\n", name);
+
+ if (!dbus_message_has_signature (message, message_handlers[i].in_args))
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_verbose ("Call to %s has wrong args (%s, expected %s)\n",
+ name, dbus_message_get_signature (message),
+ message_handlers[i].in_args);
+
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Call to %s has wrong args (%s, expected %s)\n",
+ name, dbus_message_get_signature (message),
+ message_handlers[i].in_args);
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
+
+ if ((* message_handlers[i].handler) (connection, transaction, message, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ _dbus_verbose ("Driver handler succeeded\n");
+ return TRUE;
+ }
+ else
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_verbose ("Driver handler returned failure\n");
+ return FALSE;
+ }
+ }
+
+ ++i;
+ }
+
+ unknown:
+ _dbus_verbose ("No driver handler for message \"%s\"\n",
+ name);
+
+ dbus_set_error (error, DBUS_ERROR_UNKNOWN_METHOD,
+ "%s does not understand message %s",
+ DBUS_SERVICE_DBUS, name);
+
+ return FALSE;
+}
+
+void
+bus_driver_remove_connection (DBusConnection *connection)
+{
+ /* FIXME 1.0 Does nothing for now, should unregister the connection
+ * with the bus driver.
+ */
+}
diff --git a/bus/driver.h b/bus/driver.h
new file mode 100644
index 00000000..713b2764
--- /dev/null
+++ b/bus/driver.h
@@ -0,0 +1,52 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* driver.h Bus client (driver)
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (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
+ *
+ */
+
+#ifndef BUS_DRIVER_H
+#define BUS_DRIVER_H
+
+#include <dbus/dbus.h>
+#include "connection.h"
+
+void bus_driver_remove_connection (DBusConnection *connection);
+dbus_bool_t bus_driver_handle_message (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error);
+dbus_bool_t bus_driver_send_service_lost (DBusConnection *connection,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_driver_send_service_acquired (DBusConnection *connection,
+ const char *service_name,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_driver_send_service_owner_changed (const char *service_name,
+ const char *old_owner,
+ const char *new_owner,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_driver_generate_introspect_string (DBusString *xml);
+
+
+
+#endif /* BUS_DRIVER_H */
diff --git a/bus/expirelist.c b/bus/expirelist.c
new file mode 100644
index 00000000..58e1f6d1
--- /dev/null
+++ b/bus/expirelist.c
@@ -0,0 +1,412 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* expirelist.c List of items that expire
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "expirelist.h"
+#include "test.h"
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-mainloop.h>
+#include <dbus/dbus-timeout.h>
+
+struct BusExpireList
+{
+ DBusList *items; /**< List of BusExpireItem */
+ DBusTimeout *timeout;
+ DBusLoop *loop;
+ BusExpireFunc expire_func;
+ void *data;
+ int expire_after; /**< Expire after milliseconds (thousandths) */
+};
+
+static dbus_bool_t expire_timeout_handler (void *data);
+
+static void
+call_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ /* can return FALSE on OOM but we just let it fire again later */
+ dbus_timeout_handle (timeout);
+}
+
+BusExpireList*
+bus_expire_list_new (DBusLoop *loop,
+ int expire_after,
+ BusExpireFunc expire_func,
+ void *data)
+{
+ BusExpireList *list;
+
+ list = dbus_new0 (BusExpireList, 1);
+ if (list == NULL)
+ return NULL;
+
+ list->expire_func = expire_func;
+ list->data = data;
+ list->loop = loop;
+ list->expire_after = expire_after;
+
+ list->timeout = _dbus_timeout_new (100, /* irrelevant */
+ expire_timeout_handler,
+ list, NULL);
+ if (list->timeout == NULL)
+ goto failed;
+
+ _dbus_timeout_set_enabled (list->timeout, FALSE);
+
+ if (!_dbus_loop_add_timeout (list->loop,
+ list->timeout,
+ call_timeout_callback, NULL, NULL))
+ goto failed;
+
+ return list;
+
+ failed:
+ if (list->timeout)
+ _dbus_timeout_unref (list->timeout);
+
+ dbus_free (list);
+
+ return NULL;
+}
+
+void
+bus_expire_list_free (BusExpireList *list)
+{
+ _dbus_assert (list->items == NULL);
+
+ _dbus_loop_remove_timeout (list->loop, list->timeout,
+ call_timeout_callback, NULL);
+
+ _dbus_timeout_unref (list->timeout);
+
+ dbus_free (list);
+}
+
+void
+bus_expire_timeout_set_interval (DBusTimeout *timeout,
+ int next_interval)
+{
+ if (next_interval >= 0)
+ {
+ _dbus_timeout_set_interval (timeout,
+ next_interval);
+ _dbus_timeout_set_enabled (timeout, TRUE);
+
+ _dbus_verbose ("Enabled an expire timeout with interval %d\n",
+ next_interval);
+ }
+ else if (dbus_timeout_get_enabled (timeout))
+ {
+ _dbus_timeout_set_enabled (timeout, FALSE);
+
+ _dbus_verbose ("Disabled an expire timeout\n");
+ }
+ else
+ _dbus_verbose ("No need to disable this expire timeout\n");
+}
+
+void
+bus_expire_list_recheck_immediately (BusExpireList *list)
+{
+ _dbus_verbose ("setting interval on expire list to 0 for immediate recheck\n");
+
+ bus_expire_timeout_set_interval (list->timeout, 0);
+}
+
+static int
+do_expiration_with_current_time (BusExpireList *list,
+ long tv_sec,
+ long tv_usec)
+{
+ DBusList *link;
+ int next_interval, min_wait_time, items_to_expire;
+
+ next_interval = -1;
+ min_wait_time = 3600 * 1000; /* this is reset anyway if used */
+ items_to_expire = 0;
+
+ link = _dbus_list_get_first_link (&list->items);
+ while (link != NULL)
+ {
+ DBusList *next = _dbus_list_get_next_link (&list->items, link);
+ double elapsed;
+ BusExpireItem *item;
+
+ item = link->data;
+
+ elapsed = ELAPSED_MILLISECONDS_SINCE (item->added_tv_sec,
+ item->added_tv_usec,
+ tv_sec, tv_usec);
+
+ if (((item->added_tv_sec == 0) && (item->added_tv_usec == 0)) ||
+ ((list->expire_after > 0) && (elapsed >= (double) list->expire_after)))
+ {
+ _dbus_verbose ("Expiring an item %p\n", item);
+
+ /* If the expire function fails, we just end up expiring
+ * this item next time we walk through the list. This would
+ * be an indeterminate time normally, so we set up the
+ * next_interval to be "shortly" (just enough to avoid
+ * a busy loop)
+ */
+ if (!(* list->expire_func) (list, link, list->data))
+ {
+ next_interval = _dbus_get_oom_wait ();
+ break;
+ }
+ }
+ else if (list->expire_after > 0)
+ {
+ double to_wait;
+
+ items_to_expire = 1;
+ to_wait = (double) list->expire_after - elapsed;
+ if (min_wait_time > to_wait)
+ min_wait_time = to_wait;
+ }
+
+ link = next;
+ }
+
+ if (next_interval < 0 && items_to_expire)
+ next_interval = min_wait_time;
+
+ return next_interval;
+}
+
+static void
+bus_expirelist_expire (BusExpireList *list)
+{
+ int next_interval;
+
+ next_interval = -1;
+
+ if (list->items != NULL)
+ {
+ long tv_sec, tv_usec;
+
+ _dbus_get_current_time (&tv_sec, &tv_usec);
+
+ next_interval = do_expiration_with_current_time (list, tv_sec, tv_usec);
+ }
+
+ bus_expire_timeout_set_interval (list->timeout, next_interval);
+}
+
+static dbus_bool_t
+expire_timeout_handler (void *data)
+{
+ BusExpireList *list = data;
+
+ _dbus_verbose ("Running %s\n", _DBUS_FUNCTION_NAME);
+
+ /* note that this may remove the timeout */
+ bus_expirelist_expire (list);
+
+ return TRUE;
+}
+
+void
+bus_expire_list_remove_link (BusExpireList *list,
+ DBusList *link)
+{
+ _dbus_list_remove_link (&list->items, link);
+}
+
+dbus_bool_t
+bus_expire_list_remove (BusExpireList *list,
+ BusExpireItem *item)
+{
+ return _dbus_list_remove (&list->items, item);
+}
+
+void
+bus_expire_list_unlink (BusExpireList *list,
+ DBusList *link)
+{
+ _dbus_list_unlink (&list->items, link);
+}
+
+dbus_bool_t
+bus_expire_list_add (BusExpireList *list,
+ BusExpireItem *item)
+{
+ dbus_bool_t ret;
+
+ ret = _dbus_list_prepend (&list->items, item);
+ if (ret && !dbus_timeout_get_enabled (list->timeout))
+ bus_expire_timeout_set_interval (list->timeout, 0);
+
+ return ret;
+}
+
+void
+bus_expire_list_add_link (BusExpireList *list,
+ DBusList *link)
+{
+ _dbus_assert (link->data != NULL);
+
+ _dbus_list_prepend_link (&list->items, link);
+
+ if (!dbus_timeout_get_enabled (list->timeout))
+ bus_expire_timeout_set_interval (list->timeout, 0);
+}
+
+DBusList*
+bus_expire_list_get_first_link (BusExpireList *list)
+{
+ return _dbus_list_get_first_link (&list->items);
+}
+
+DBusList*
+bus_expire_list_get_next_link (BusExpireList *list,
+ DBusList *link)
+{
+ return _dbus_list_get_next_link (&list->items, link);
+}
+
+dbus_bool_t
+bus_expire_list_contains_item (BusExpireList *list,
+ BusExpireItem *item)
+{
+ return _dbus_list_find_last (&list->items, item) != NULL;
+}
+
+#ifdef DBUS_BUILD_TESTS
+
+typedef struct
+{
+ BusExpireItem item;
+ int expire_count;
+} TestExpireItem;
+
+static dbus_bool_t
+test_expire_func (BusExpireList *list,
+ DBusList *link,
+ void *data)
+{
+ TestExpireItem *t;
+
+ t = (TestExpireItem*) link->data;
+
+ t->expire_count += 1;
+
+ return TRUE;
+}
+
+static void
+time_add_milliseconds (long *tv_sec,
+ long *tv_usec,
+ int milliseconds)
+{
+ *tv_sec = *tv_sec + milliseconds / 1000;
+ *tv_usec = *tv_usec + milliseconds * 1000;
+ if (*tv_usec >= 1000000)
+ {
+ *tv_usec -= 1000000;
+ *tv_sec += 1;
+ }
+}
+
+dbus_bool_t
+bus_expire_list_test (const DBusString *test_data_dir)
+{
+ DBusLoop *loop;
+ BusExpireList *list;
+ long tv_sec, tv_usec;
+ long tv_sec_not_expired, tv_usec_not_expired;
+ long tv_sec_expired, tv_usec_expired;
+ long tv_sec_past, tv_usec_past;
+ TestExpireItem *item;
+ int next_interval;
+ dbus_bool_t result = FALSE;
+
+
+ loop = _dbus_loop_new ();
+ _dbus_assert (loop != NULL);
+
+#define EXPIRE_AFTER 100
+
+ list = bus_expire_list_new (loop, EXPIRE_AFTER,
+ test_expire_func, NULL);
+ _dbus_assert (list != NULL);
+
+ _dbus_get_current_time (&tv_sec, &tv_usec);
+
+ tv_sec_not_expired = tv_sec;
+ tv_usec_not_expired = tv_usec;
+ time_add_milliseconds (&tv_sec_not_expired,
+ &tv_usec_not_expired, EXPIRE_AFTER - 1);
+
+ tv_sec_expired = tv_sec;
+ tv_usec_expired = tv_usec;
+ time_add_milliseconds (&tv_sec_expired,
+ &tv_usec_expired, EXPIRE_AFTER);
+
+
+ tv_sec_past = tv_sec - 1;
+ tv_usec_past = tv_usec;
+
+ item = dbus_new0 (TestExpireItem, 1);
+
+ if (item == NULL)
+ goto oom;
+
+ item->item.added_tv_sec = tv_sec;
+ item->item.added_tv_usec = tv_usec;
+ if (!bus_expire_list_add (list, &item->item))
+ _dbus_assert_not_reached ("out of memory");
+
+ next_interval =
+ do_expiration_with_current_time (list, tv_sec_not_expired,
+ tv_usec_not_expired);
+ _dbus_assert (item->expire_count == 0);
+ _dbus_verbose ("next_interval = %d\n", next_interval);
+ _dbus_assert (next_interval == 1);
+
+ next_interval =
+ do_expiration_with_current_time (list, tv_sec_expired,
+ tv_usec_expired);
+ _dbus_assert (item->expire_count == 1);
+ _dbus_verbose ("next_interval = %d\n", next_interval);
+ _dbus_assert (next_interval == -1);
+
+ next_interval =
+ do_expiration_with_current_time (list, tv_sec_past,
+ tv_usec_past);
+ _dbus_assert (item->expire_count == 1);
+ _dbus_verbose ("next_interval = %d\n", next_interval);
+ _dbus_assert (next_interval == 1000 + EXPIRE_AFTER);
+
+ bus_expire_list_remove (list, &item->item);
+ dbus_free (item);
+
+ bus_expire_list_free (list);
+ _dbus_loop_unref (loop);
+
+ result = TRUE;
+
+ oom:
+ return result;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/bus/expirelist.h b/bus/expirelist.h
new file mode 100644
index 00000000..887cb97b
--- /dev/null
+++ b/bus/expirelist.h
@@ -0,0 +1,80 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* expirelist.h List of stuff that expires
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_EXPIRE_LIST_H
+#define BUS_EXPIRE_LIST_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-mainloop.h>
+
+typedef struct BusExpireList BusExpireList;
+typedef struct BusExpireItem BusExpireItem;
+
+typedef dbus_bool_t (* BusExpireFunc) (BusExpireList *list,
+ DBusList *link,
+ void *data);
+
+
+/* embed this in a child expire item struct */
+struct BusExpireItem
+{
+ long added_tv_sec; /**< Time we were added (seconds component) */
+ long added_tv_usec; /**< Time we were added (microsec component) */
+};
+
+BusExpireList* bus_expire_list_new (DBusLoop *loop,
+ int expire_after,
+ BusExpireFunc expire_func,
+ void *data);
+void bus_expire_list_free (BusExpireList *list);
+void bus_expire_list_recheck_immediately (BusExpireList *list);
+void bus_expire_list_remove_link (BusExpireList *list,
+ DBusList *link);
+dbus_bool_t bus_expire_list_remove (BusExpireList *list,
+ BusExpireItem *item);
+DBusList* bus_expire_list_get_first_link (BusExpireList *list);
+DBusList* bus_expire_list_get_next_link (BusExpireList *list,
+ DBusList *link);
+dbus_bool_t bus_expire_list_add (BusExpireList *list,
+ BusExpireItem *item);
+void bus_expire_list_add_link (BusExpireList *list,
+ DBusList *link);
+dbus_bool_t bus_expire_list_contains_item (BusExpireList *list,
+ BusExpireItem *item);
+void bus_expire_list_unlink (BusExpireList *list,
+ DBusList *link);
+
+/* this macro and function are semi-related utility functions, not really part of the
+ * BusExpireList API
+ */
+
+#define ELAPSED_MILLISECONDS_SINCE(orig_tv_sec, orig_tv_usec, \
+ now_tv_sec, now_tv_usec) \
+ (((double) (now_tv_sec) - (double) (orig_tv_sec)) * 1000.0 + \
+ ((double) (now_tv_usec) - (double) (orig_tv_usec)) / 1000.0)
+
+void bus_expire_timeout_set_interval (DBusTimeout *timeout,
+ int next_interval);
+
+#endif /* BUS_EXPIRE_LIST_H */
diff --git a/bus/main.c b/bus/main.c
new file mode 100644
index 00000000..d17486dd
--- /dev/null
+++ b/bus/main.c
@@ -0,0 +1,479 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* main.c main() for message bus
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "bus.h"
+#include "driver.h"
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-watch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "selinux.h"
+
+static BusContext *context;
+
+static int reload_pipe[2];
+#define RELOAD_READ_END 0
+#define RELOAD_WRITE_END 1
+
+static void close_reload_pipe (void);
+
+static void
+signal_handler (int sig)
+{
+
+ switch (sig)
+ {
+#ifdef DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
+ case SIGIO:
+ /* explicit fall-through */
+#endif /* DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX */
+#ifdef SIGHUP
+ case SIGHUP:
+ {
+ DBusString str;
+ _dbus_string_init_const (&str, "foo");
+ if ((reload_pipe[RELOAD_WRITE_END] > 0) &&
+ !_dbus_write_socket (reload_pipe[RELOAD_WRITE_END], &str, 0, 1))
+ {
+ _dbus_warn ("Unable to write to reload pipe.\n");
+ close_reload_pipe ();
+ }
+ }
+ break;
+#endif
+ }
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, DAEMON_NAME " [--version] [--session] [--system] [--config-file=FILE] [--print-address[=DESCRIPTOR]] [--print-pid[=DESCRIPTOR]] [--fork] [--nofork] [--introspect]\n");
+ exit (1);
+}
+
+static void
+version (void)
+{
+ printf ("D-Bus Message Bus Daemon %s\n"
+ "Copyright (C) 2002, 2003 Red Hat, Inc., CodeFactory AB, and others\n"
+ "This is free software; see the source for copying conditions.\n"
+ "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
+ VERSION);
+ exit (0);
+}
+
+static void
+introspect (void)
+{
+ DBusString xml;
+ const char *v_STRING;
+
+ if (!_dbus_string_init (&xml))
+ goto oom;
+
+ if (!bus_driver_generate_introspect_string (&xml))
+ {
+ _dbus_string_free (&xml);
+ goto oom;
+ }
+
+ v_STRING = _dbus_string_get_const_data (&xml);
+ printf ("%s\n", v_STRING);
+
+ exit (0);
+
+ oom:
+ _dbus_warn ("Can not introspect - Out of memory\n");
+ exit (1);
+}
+static void
+check_two_config_files (const DBusString *config_file,
+ const char *extra_arg)
+{
+ if (_dbus_string_get_length (config_file) > 0)
+ {
+ fprintf (stderr, "--%s specified but configuration file %s already requested\n",
+ extra_arg, _dbus_string_get_const_data (config_file));
+ exit (1);
+ }
+}
+
+static void
+check_two_addr_descriptors (const DBusString *addr_fd,
+ const char *extra_arg)
+{
+ if (_dbus_string_get_length (addr_fd) > 0)
+ {
+ fprintf (stderr, "--%s specified but printing address to %s already requested\n",
+ extra_arg, _dbus_string_get_const_data (addr_fd));
+ exit (1);
+ }
+}
+
+static void
+check_two_pid_descriptors (const DBusString *pid_fd,
+ const char *extra_arg)
+{
+ if (_dbus_string_get_length (pid_fd) > 0)
+ {
+ fprintf (stderr, "--%s specified but printing pid to %s already requested\n",
+ extra_arg, _dbus_string_get_const_data (pid_fd));
+ exit (1);
+ }
+}
+
+static dbus_bool_t
+handle_reload_watch (DBusWatch *watch,
+ unsigned int flags,
+ void *data)
+{
+ DBusError error;
+ DBusString str;
+
+ while (!_dbus_string_init (&str))
+ _dbus_wait_for_memory ();
+
+ if ((reload_pipe[RELOAD_READ_END] > 0) &&
+ _dbus_read_socket (reload_pipe[RELOAD_READ_END], &str, 1) != 1)
+ {
+ _dbus_warn ("Couldn't read from reload pipe.\n");
+ close_reload_pipe ();
+ return TRUE;
+ }
+ _dbus_string_free (&str);
+
+ /* this can only fail if we don't understand the config file
+ * or OOM. Either way we should just stick with the currently
+ * loaded config.
+ */
+ dbus_error_init (&error);
+ if (! bus_context_reload_config (context, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ _dbus_assert (dbus_error_has_name (&error, DBUS_ERROR_FAILED) ||
+ dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY));
+ _dbus_warn ("Unable to reload configuration: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return TRUE;
+}
+
+static dbus_bool_t
+reload_watch_callback (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ return dbus_watch_handle (watch, condition);
+}
+
+static void
+setup_reload_pipe (DBusLoop *loop)
+{
+ DBusError error;
+ DBusWatch *watch;
+
+ dbus_error_init (&error);
+
+ if (!_dbus_full_duplex_pipe (&reload_pipe[0], &reload_pipe[1],
+ TRUE, &error))
+ {
+ _dbus_warn ("Unable to create reload pipe: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ exit (1);
+ }
+
+ _dbus_fd_set_close_on_exec (reload_pipe[0]);
+ _dbus_fd_set_close_on_exec (reload_pipe[1]);
+
+ watch = _dbus_watch_new (reload_pipe[RELOAD_READ_END],
+ DBUS_WATCH_READABLE, TRUE,
+ handle_reload_watch, NULL, NULL);
+
+ if (watch == NULL)
+ {
+ _dbus_warn ("Unable to create reload watch: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ exit (1);
+ }
+
+ if (!_dbus_loop_add_watch (loop, watch, reload_watch_callback,
+ NULL, NULL))
+ {
+ _dbus_warn ("Unable to add reload watch to main loop: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ exit (1);
+ }
+
+}
+
+static void
+close_reload_pipe (void)
+{
+ _dbus_close_socket (reload_pipe[RELOAD_READ_END], NULL);
+ reload_pipe[RELOAD_READ_END] = -1;
+
+ _dbus_close_socket (reload_pipe[RELOAD_WRITE_END], NULL);
+ reload_pipe[RELOAD_WRITE_END] = -1;
+}
+
+int
+main (int argc, char **argv)
+{
+ DBusError error;
+ DBusString config_file;
+ DBusString addr_fd;
+ DBusString pid_fd;
+ const char *prev_arg;
+ DBusPipe print_addr_pipe;
+ DBusPipe print_pid_pipe;
+ int i;
+ dbus_bool_t print_address;
+ dbus_bool_t print_pid;
+ int force_fork;
+
+ if (!_dbus_string_init (&config_file))
+ return 1;
+
+ if (!_dbus_string_init (&addr_fd))
+ return 1;
+
+ if (!_dbus_string_init (&pid_fd))
+ return 1;
+
+ print_address = FALSE;
+ print_pid = FALSE;
+ force_fork = FORK_FOLLOW_CONFIG_FILE;
+
+ prev_arg = NULL;
+ i = 1;
+ while (i < argc)
+ {
+ const char *arg = argv[i];
+
+ if (strcmp (arg, "--help") == 0 ||
+ strcmp (arg, "-h") == 0 ||
+ strcmp (arg, "-?") == 0)
+ usage ();
+ else if (strcmp (arg, "--version") == 0)
+ version ();
+ else if (strcmp (arg, "--introspect") == 0)
+ introspect ();
+ else if (strcmp (arg, "--nofork") == 0)
+ force_fork = FORK_NEVER;
+ else if (strcmp (arg, "--fork") == 0)
+ force_fork = FORK_ALWAYS;
+ else if (strcmp (arg, "--system") == 0)
+ {
+ check_two_config_files (&config_file, "system");
+
+ if (!_dbus_append_system_config_file (&config_file))
+ exit (1);
+ }
+ else if (strcmp (arg, "--session") == 0)
+ {
+ check_two_config_files (&config_file, "session");
+
+ if (!_dbus_append_session_config_file (&config_file))
+ exit (1);
+ }
+ else if (strstr (arg, "--config-file=") == arg)
+ {
+ const char *file;
+
+ check_two_config_files (&config_file, "config-file");
+
+ file = strchr (arg, '=');
+ ++file;
+
+ if (!_dbus_string_append (&config_file, file))
+ exit (1);
+ }
+ else if (prev_arg &&
+ strcmp (prev_arg, "--config-file") == 0)
+ {
+ check_two_config_files (&config_file, "config-file");
+
+ if (!_dbus_string_append (&config_file, arg))
+ exit (1);
+ }
+ else if (strcmp (arg, "--config-file") == 0)
+ ; /* wait for next arg */
+ else if (strstr (arg, "--print-address=") == arg)
+ {
+ const char *desc;
+
+ check_two_addr_descriptors (&addr_fd, "print-address");
+
+ desc = strchr (arg, '=');
+ ++desc;
+
+ if (!_dbus_string_append (&addr_fd, desc))
+ exit (1);
+
+ print_address = TRUE;
+ }
+ else if (prev_arg &&
+ strcmp (prev_arg, "--print-address") == 0)
+ {
+ check_two_addr_descriptors (&addr_fd, "print-address");
+
+ if (!_dbus_string_append (&addr_fd, arg))
+ exit (1);
+
+ print_address = TRUE;
+ }
+ else if (strcmp (arg, "--print-address") == 0)
+ print_address = TRUE; /* and we'll get the next arg if appropriate */
+ else if (strstr (arg, "--print-pid=") == arg)
+ {
+ const char *desc;
+
+ check_two_pid_descriptors (&pid_fd, "print-pid");
+
+ desc = strchr (arg, '=');
+ ++desc;
+
+ if (!_dbus_string_append (&pid_fd, desc))
+ exit (1);
+
+ print_pid = TRUE;
+ }
+ else if (prev_arg &&
+ strcmp (prev_arg, "--print-pid") == 0)
+ {
+ check_two_pid_descriptors (&pid_fd, "print-pid");
+
+ if (!_dbus_string_append (&pid_fd, arg))
+ exit (1);
+
+ print_pid = TRUE;
+ }
+ else if (strcmp (arg, "--print-pid") == 0)
+ print_pid = TRUE; /* and we'll get the next arg if appropriate */
+ else
+ usage ();
+
+ prev_arg = arg;
+
+ ++i;
+ }
+
+ if (_dbus_string_get_length (&config_file) == 0)
+ {
+ fprintf (stderr, "No configuration file specified.\n");
+ usage ();
+ }
+
+ _dbus_pipe_invalidate (&print_addr_pipe);
+ if (print_address)
+ {
+ _dbus_pipe_init_stdout (&print_addr_pipe);
+ if (_dbus_string_get_length (&addr_fd) > 0)
+ {
+ long val;
+ int end;
+ if (!_dbus_string_parse_int (&addr_fd, 0, &val, &end) ||
+ end != _dbus_string_get_length (&addr_fd) ||
+ val < 0 || val > _DBUS_INT_MAX)
+ {
+ fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
+ _dbus_string_get_const_data (&addr_fd));
+ exit (1);
+ }
+
+ _dbus_pipe_init (&print_addr_pipe, val);
+ }
+ }
+ _dbus_string_free (&addr_fd);
+
+ _dbus_pipe_invalidate (&print_pid_pipe);
+ if (print_pid)
+ {
+ _dbus_pipe_init_stdout (&print_pid_pipe);
+ if (_dbus_string_get_length (&pid_fd) > 0)
+ {
+ long val;
+ int end;
+ if (!_dbus_string_parse_int (&pid_fd, 0, &val, &end) ||
+ end != _dbus_string_get_length (&pid_fd) ||
+ val < 0 || val > _DBUS_INT_MAX)
+ {
+ fprintf (stderr, "Invalid file descriptor: \"%s\"\n",
+ _dbus_string_get_const_data (&pid_fd));
+ exit (1);
+ }
+
+ _dbus_pipe_init (&print_pid_pipe, val);
+ }
+ }
+ _dbus_string_free (&pid_fd);
+
+ if (!bus_selinux_pre_init ())
+ {
+ _dbus_warn ("SELinux pre-initialization failed\n");
+ exit (1);
+ }
+
+ dbus_error_init (&error);
+ context = bus_context_new (&config_file, force_fork,
+ &print_addr_pipe, &print_pid_pipe,
+ &error);
+ _dbus_string_free (&config_file);
+ if (context == NULL)
+ {
+ _dbus_warn ("Failed to start message bus: %s\n",
+ error.message);
+ dbus_error_free (&error);
+ exit (1);
+ }
+
+ /* bus_context_new() closes the print_addr_pipe and
+ * print_pid_pipe
+ */
+
+ setup_reload_pipe (bus_context_get_loop (context));
+
+#ifdef SIGHUP
+ _dbus_set_signal_handler (SIGHUP, signal_handler);
+#endif
+#ifdef DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX
+ _dbus_set_signal_handler (SIGIO, signal_handler);
+#endif /* DBUS_BUS_ENABLE_DNOTIFY_ON_LINUX */
+
+ _dbus_verbose ("We are on D-Bus...\n");
+ _dbus_loop_run (bus_context_get_loop (context));
+
+ bus_context_shutdown (context);
+ bus_context_unref (context);
+ bus_selinux_shutdown ();
+
+ return 0;
+}
diff --git a/bus/messagebus.in b/bus/messagebus.in
new file mode 100755
index 00000000..1f1004b1
--- /dev/null
+++ b/bus/messagebus.in
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# messagebus: The D-BUS systemwide message bus
+#
+# chkconfig: 345 22 85
+# description: This is a daemon which broadcasts notifications of system events \
+# and other messages. See http://www.freedesktop.org/software/dbus/
+#
+# processname: dbus-daemon
+# pidfile: @DBUS_SYSTEM_PID_FILE@
+#
+### BEGIN INIT INFO
+# Provides: messagebus
+# Required-Start: $syslog $local_fs
+# Required-Stop: $syslog $local_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: The D-Bus systemwide message bus
+# Description: This is a daemon which broadcasts notifications of system
+# events and other messages. See http://www.freedesktop.org/software/dbus
+### END INIT INFO
+
+# Sanity checks.
+[ -x @EXPANDED_BINDIR@/dbus-daemon ] || exit 0
+
+# Source function library.
+. @EXPANDED_SYSCONFDIR@/rc.d/init.d/functions
+
+# so we can rearrange this easily
+processname=dbus-daemon
+servicename=messagebus
+
+RETVAL=0
+
+start() {
+ echo -n $"Starting system message bus: "
+ if [ -x @EXPANDED_BINDIR@/dbus-uuidgen ] ; then
+ @EXPANDED_BINDIR@/dbus-uuidgen --ensure
+ fi
+
+ daemon --check $servicename $processname --system
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch @EXPANDED_LOCALSTATEDIR@/lock/subsys/$servicename
+}
+
+stop() {
+ echo -n $"Stopping system message bus: "
+
+ ## we don't want to kill all the per-user $processname, we want
+ ## to use the pid file *only*; because we use the fake nonexistent
+ ## program name "$servicename" that should be safe-ish
+ killproc $servicename -TERM
+ RETVAL=$?
+ echo
+ if [ $RETVAL -eq 0 ]; then
+ rm -f @EXPANDED_LOCALSTATEDIR@/lock/subsys/$servicename
+ rm -f @DBUS_SYSTEM_PID_FILE@
+ fi
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $processname
+ RETVAL=$?
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f @EXPANDED_LOCALSTATEDIR@/lock/subsys/$servicename ]; then
+ stop
+ start
+ fi
+ ;;
+ reload)
+ echo "Message bus can't reload its configuration, you have to restart it"
+ RETVAL=$?
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
+ ;;
+esac
+exit $RETVAL
diff --git a/bus/policy.c b/bus/policy.c
new file mode 100644
index 00000000..bc1d2d9d
--- /dev/null
+++ b/bus/policy.c
@@ -0,0 +1,1296 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* policy.c Bus security policy
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ *
+ * 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 "policy.h"
+#include "services.h"
+#include "test.h"
+#include "utils.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-internals.h>
+
+BusPolicyRule*
+bus_policy_rule_new (BusPolicyRuleType type,
+ dbus_bool_t allow)
+{
+ BusPolicyRule *rule;
+
+ rule = dbus_new0 (BusPolicyRule, 1);
+ if (rule == NULL)
+ return NULL;
+
+ rule->type = type;
+ rule->refcount = 1;
+ rule->allow = allow;
+
+ switch (rule->type)
+ {
+ case BUS_POLICY_RULE_USER:
+ rule->d.user.uid = DBUS_UID_UNSET;
+ break;
+ case BUS_POLICY_RULE_GROUP:
+ rule->d.group.gid = DBUS_GID_UNSET;
+ break;
+ case BUS_POLICY_RULE_SEND:
+ rule->d.send.message_type = DBUS_MESSAGE_TYPE_INVALID;
+
+ /* allow rules default to TRUE (only requested replies allowed)
+ * deny rules default to FALSE (only unrequested replies denied)
+ */
+ rule->d.send.requested_reply = rule->allow;
+ break;
+ case BUS_POLICY_RULE_RECEIVE:
+ rule->d.receive.message_type = DBUS_MESSAGE_TYPE_INVALID;
+ /* allow rules default to TRUE (only requested replies allowed)
+ * deny rules default to FALSE (only unrequested replies denied)
+ */
+ rule->d.receive.requested_reply = rule->allow;
+ break;
+ case BUS_POLICY_RULE_OWN:
+ break;
+ }
+
+ return rule;
+}
+
+BusPolicyRule *
+bus_policy_rule_ref (BusPolicyRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount += 1;
+
+ return rule;
+}
+
+void
+bus_policy_rule_unref (BusPolicyRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount -= 1;
+
+ if (rule->refcount == 0)
+ {
+ switch (rule->type)
+ {
+ case BUS_POLICY_RULE_SEND:
+ dbus_free (rule->d.send.path);
+ dbus_free (rule->d.send.interface);
+ dbus_free (rule->d.send.member);
+ dbus_free (rule->d.send.error);
+ dbus_free (rule->d.send.destination);
+ break;
+ case BUS_POLICY_RULE_RECEIVE:
+ dbus_free (rule->d.receive.path);
+ dbus_free (rule->d.receive.interface);
+ dbus_free (rule->d.receive.member);
+ dbus_free (rule->d.receive.error);
+ dbus_free (rule->d.receive.origin);
+ break;
+ case BUS_POLICY_RULE_OWN:
+ dbus_free (rule->d.own.service_name);
+ break;
+ case BUS_POLICY_RULE_USER:
+ break;
+ case BUS_POLICY_RULE_GROUP:
+ break;
+ }
+
+ dbus_free (rule);
+ }
+}
+
+struct BusPolicy
+{
+ int refcount;
+
+ DBusList *default_rules; /**< Default policy rules */
+ DBusList *mandatory_rules; /**< Mandatory policy rules */
+ DBusHashTable *rules_by_uid; /**< per-UID policy rules */
+ DBusHashTable *rules_by_gid; /**< per-GID policy rules */
+ DBusList *at_console_true_rules; /**< console user policy rules where at_console="true"*/
+ DBusList *at_console_false_rules; /**< console user policy rules where at_console="false"*/
+};
+
+static void
+free_rule_func (void *data,
+ void *user_data)
+{
+ BusPolicyRule *rule = data;
+
+ bus_policy_rule_unref (rule);
+}
+
+static void
+free_rule_list_func (void *data)
+{
+ DBusList **list = data;
+
+ if (list == NULL) /* DBusHashTable is on crack */
+ return;
+
+ _dbus_list_foreach (list, free_rule_func, NULL);
+
+ _dbus_list_clear (list);
+
+ dbus_free (list);
+}
+
+BusPolicy*
+bus_policy_new (void)
+{
+ BusPolicy *policy;
+
+ policy = dbus_new0 (BusPolicy, 1);
+ if (policy == NULL)
+ return NULL;
+
+ policy->refcount = 1;
+
+ policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
+ NULL,
+ free_rule_list_func);
+ if (policy->rules_by_uid == NULL)
+ goto failed;
+
+ policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
+ NULL,
+ free_rule_list_func);
+ if (policy->rules_by_gid == NULL)
+ goto failed;
+
+ return policy;
+
+ failed:
+ bus_policy_unref (policy);
+ return NULL;
+}
+
+BusPolicy *
+bus_policy_ref (BusPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount += 1;
+
+ return policy;
+}
+
+void
+bus_policy_unref (BusPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount -= 1;
+
+ if (policy->refcount == 0)
+ {
+ _dbus_list_foreach (&policy->default_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->default_rules);
+
+ _dbus_list_foreach (&policy->mandatory_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->mandatory_rules);
+
+ _dbus_list_foreach (&policy->at_console_true_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->at_console_true_rules);
+
+ _dbus_list_foreach (&policy->at_console_false_rules, free_rule_func, NULL);
+ _dbus_list_clear (&policy->at_console_false_rules);
+
+ if (policy->rules_by_uid)
+ {
+ _dbus_hash_table_unref (policy->rules_by_uid);
+ policy->rules_by_uid = NULL;
+ }
+
+ if (policy->rules_by_gid)
+ {
+ _dbus_hash_table_unref (policy->rules_by_gid);
+ policy->rules_by_gid = NULL;
+ }
+
+ dbus_free (policy);
+ }
+}
+
+static dbus_bool_t
+add_list_to_client (DBusList **list,
+ BusClientPolicy *client)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (list);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule = link->data;
+ link = _dbus_list_get_next_link (list, link);
+
+ switch (rule->type)
+ {
+ case BUS_POLICY_RULE_USER:
+ case BUS_POLICY_RULE_GROUP:
+ /* These aren't per-connection policies */
+ break;
+
+ case BUS_POLICY_RULE_OWN:
+ case BUS_POLICY_RULE_SEND:
+ case BUS_POLICY_RULE_RECEIVE:
+ /* These are per-connection */
+ if (!bus_client_policy_append_rule (client, rule))
+ return FALSE;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+BusClientPolicy*
+bus_policy_create_client_policy (BusPolicy *policy,
+ DBusConnection *connection,
+ DBusError *error)
+{
+ BusClientPolicy *client;
+ dbus_uid_t uid;
+ dbus_bool_t at_console;
+
+ _dbus_assert (dbus_connection_get_is_authenticated (connection));
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ client = bus_client_policy_new ();
+ if (client == NULL)
+ goto nomem;
+
+ if (!add_list_to_client (&policy->default_rules,
+ client))
+ goto nomem;
+
+ /* we avoid the overhead of looking up user's groups
+ * if we don't have any group rules anyway
+ */
+ if (_dbus_hash_table_get_n_entries (policy->rules_by_gid) > 0)
+ {
+ unsigned long *groups;
+ int n_groups;
+ int i;
+
+ if (!bus_connection_get_unix_groups (connection, &groups, &n_groups, error))
+ goto failed;
+
+ i = 0;
+ while (i < n_groups)
+ {
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
+ groups[i]);
+
+ if (list != NULL)
+ {
+ if (!add_list_to_client (list, client))
+ {
+ dbus_free (groups);
+ goto nomem;
+ }
+ }
+
+ ++i;
+ }
+
+ dbus_free (groups);
+ }
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
+ {
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
+ uid);
+
+ if (list != NULL)
+ {
+ if (!add_list_to_client (list, client))
+ goto nomem;
+ }
+ }
+
+ /* Add console rules */
+ at_console = _dbus_unix_user_is_at_console (uid, error);
+
+ if (at_console)
+ {
+ if (!add_list_to_client (&policy->at_console_true_rules, client))
+ goto nomem;
+ }
+ else if (dbus_error_is_set (error) == TRUE)
+ {
+ goto failed;
+ }
+ else if (!add_list_to_client (&policy->at_console_false_rules, client))
+ {
+ goto nomem;
+ }
+ }
+
+ if (!add_list_to_client (&policy->mandatory_rules,
+ client))
+ goto nomem;
+
+ bus_client_policy_optimize (client);
+
+ return client;
+
+ nomem:
+ BUS_SET_OOM (error);
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (client)
+ bus_client_policy_unref (client);
+ return NULL;
+}
+
+static dbus_bool_t
+list_allows_user (dbus_bool_t def,
+ DBusList **list,
+ unsigned long uid,
+ const unsigned long *group_ids,
+ int n_group_ids)
+{
+ DBusList *link;
+ dbus_bool_t allowed;
+
+ allowed = def;
+
+ link = _dbus_list_get_first_link (list);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule = link->data;
+ link = _dbus_list_get_next_link (list, link);
+
+ if (rule->type == BUS_POLICY_RULE_USER)
+ {
+ _dbus_verbose ("List %p user rule uid="DBUS_UID_FORMAT"\n",
+ list, rule->d.user.uid);
+
+ if (rule->d.user.uid == DBUS_UID_UNSET)
+ ; /* '*' wildcard */
+ else if (rule->d.user.uid != uid)
+ continue;
+ }
+ else if (rule->type == BUS_POLICY_RULE_GROUP)
+ {
+ _dbus_verbose ("List %p group rule uid="DBUS_UID_FORMAT"\n",
+ list, rule->d.user.uid);
+
+ if (rule->d.group.gid == DBUS_GID_UNSET)
+ ; /* '*' wildcard */
+ else
+ {
+ int i;
+
+ i = 0;
+ while (i < n_group_ids)
+ {
+ if (rule->d.group.gid == group_ids[i])
+ break;
+ ++i;
+ }
+
+ if (i == n_group_ids)
+ continue;
+ }
+ }
+ else
+ continue;
+
+ allowed = rule->allow;
+ }
+
+ return allowed;
+}
+
+dbus_bool_t
+bus_policy_allow_unix_user (BusPolicy *policy,
+ unsigned long uid)
+{
+ dbus_bool_t allowed;
+ unsigned long *group_ids;
+ int n_group_ids;
+
+ /* On OOM or error we always reject the user */
+ if (!_dbus_unix_groups_from_uid (uid, &group_ids, &n_group_ids))
+ {
+ _dbus_verbose ("Did not get any groups for UID %lu\n",
+ uid);
+ return FALSE;
+ }
+
+ /* Default to "user owning bus" can connect */
+ allowed = _dbus_unix_user_is_process_owner (uid);
+
+ allowed = list_allows_user (allowed,
+ &policy->default_rules,
+ uid,
+ group_ids, n_group_ids);
+
+ allowed = list_allows_user (allowed,
+ &policy->mandatory_rules,
+ uid,
+ group_ids, n_group_ids);
+
+ dbus_free (group_ids);
+
+ _dbus_verbose ("UID %lu allowed = %d\n", uid, allowed);
+
+ return allowed;
+}
+
+/* For now this is never actually called because the default
+ * DBusConnection behavior of 'same user that owns the bus can
+ * connect' is all it would do. Set the windows user function in
+ * connection.c if the config file ever supports doing something
+ * interesting here.
+ */
+dbus_bool_t
+bus_policy_allow_windows_user (BusPolicy *policy,
+ const char *windows_sid)
+{
+ /* Windows has no policies here since only the session bus
+ * is really used for now, so just checking that the
+ * connecting person is the same as the bus owner is fine.
+ */
+ return _dbus_windows_user_is_process_owner (windows_sid);
+}
+
+dbus_bool_t
+bus_policy_append_default_rule (BusPolicy *policy,
+ BusPolicyRule *rule)
+{
+ if (!_dbus_list_append (&policy->default_rules, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_mandatory_rule (BusPolicy *policy,
+ BusPolicyRule *rule)
+{
+ if (!_dbus_list_append (&policy->mandatory_rules, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+
+
+static DBusList**
+get_list (DBusHashTable *hash,
+ unsigned long key)
+{
+ DBusList **list;
+
+ list = _dbus_hash_table_lookup_ulong (hash, key);
+
+ if (list == NULL)
+ {
+ list = dbus_new0 (DBusList*, 1);
+ if (list == NULL)
+ return NULL;
+
+ if (!_dbus_hash_table_insert_ulong (hash, key, list))
+ {
+ dbus_free (list);
+ return NULL;
+ }
+ }
+
+ return list;
+}
+
+dbus_bool_t
+bus_policy_append_user_rule (BusPolicy *policy,
+ dbus_uid_t uid,
+ BusPolicyRule *rule)
+{
+ DBusList **list;
+
+ list = get_list (policy->rules_by_uid, uid);
+
+ if (list == NULL)
+ return FALSE;
+
+ if (!_dbus_list_append (list, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_group_rule (BusPolicy *policy,
+ dbus_gid_t gid,
+ BusPolicyRule *rule)
+{
+ DBusList **list;
+
+ list = get_list (policy->rules_by_gid, gid);
+
+ if (list == NULL)
+ return FALSE;
+
+ if (!_dbus_list_append (list, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_append_console_rule (BusPolicy *policy,
+ dbus_bool_t at_console,
+ BusPolicyRule *rule)
+{
+ if (at_console)
+ {
+ if (!_dbus_list_append (&policy->at_console_true_rules, rule))
+ return FALSE;
+ }
+ else
+ {
+ if (!_dbus_list_append (&policy->at_console_false_rules, rule))
+ return FALSE;
+ }
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+
+}
+
+static dbus_bool_t
+append_copy_of_policy_list (DBusList **list,
+ DBusList **to_append)
+{
+ DBusList *link;
+ DBusList *tmp_list;
+
+ tmp_list = NULL;
+
+ /* Preallocate all our links */
+ link = _dbus_list_get_first_link (to_append);
+ while (link != NULL)
+ {
+ if (!_dbus_list_append (&tmp_list, link->data))
+ {
+ _dbus_list_clear (&tmp_list);
+ return FALSE;
+ }
+
+ link = _dbus_list_get_next_link (to_append, link);
+ }
+
+ /* Now append them */
+ while ((link = _dbus_list_pop_first_link (&tmp_list)))
+ {
+ bus_policy_rule_ref (link->data);
+ _dbus_list_append_link (list, link);
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+merge_id_hash (DBusHashTable *dest,
+ DBusHashTable *to_absorb)
+{
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (to_absorb, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ unsigned long id = _dbus_hash_iter_get_ulong_key (&iter);
+ DBusList **list = _dbus_hash_iter_get_value (&iter);
+ DBusList **target = get_list (dest, id);
+
+ if (target == NULL)
+ return FALSE;
+
+ if (!append_copy_of_policy_list (target, list))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_policy_merge (BusPolicy *policy,
+ BusPolicy *to_absorb)
+{
+ /* FIXME Not properly atomic, but as used for configuration files we
+ * don't rely on it quite so much.
+ */
+
+ if (!append_copy_of_policy_list (&policy->default_rules,
+ &to_absorb->default_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->mandatory_rules,
+ &to_absorb->mandatory_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->at_console_true_rules,
+ &to_absorb->at_console_true_rules))
+ return FALSE;
+
+ if (!append_copy_of_policy_list (&policy->at_console_false_rules,
+ &to_absorb->at_console_false_rules))
+ return FALSE;
+
+ if (!merge_id_hash (policy->rules_by_uid,
+ to_absorb->rules_by_uid))
+ return FALSE;
+
+ if (!merge_id_hash (policy->rules_by_gid,
+ to_absorb->rules_by_gid))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct BusClientPolicy
+{
+ int refcount;
+
+ DBusList *rules;
+};
+
+BusClientPolicy*
+bus_client_policy_new (void)
+{
+ BusClientPolicy *policy;
+
+ policy = dbus_new0 (BusClientPolicy, 1);
+ if (policy == NULL)
+ return NULL;
+
+ policy->refcount = 1;
+
+ return policy;
+}
+
+BusClientPolicy *
+bus_client_policy_ref (BusClientPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount += 1;
+
+ return policy;
+}
+
+static void
+rule_unref_foreach (void *data,
+ void *user_data)
+{
+ BusPolicyRule *rule = data;
+
+ bus_policy_rule_unref (rule);
+}
+
+void
+bus_client_policy_unref (BusClientPolicy *policy)
+{
+ _dbus_assert (policy->refcount > 0);
+
+ policy->refcount -= 1;
+
+ if (policy->refcount == 0)
+ {
+ _dbus_list_foreach (&policy->rules,
+ rule_unref_foreach,
+ NULL);
+
+ _dbus_list_clear (&policy->rules);
+
+ dbus_free (policy);
+ }
+}
+
+static void
+remove_rules_by_type_up_to (BusClientPolicy *policy,
+ BusPolicyRuleType type,
+ DBusList *up_to)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&policy->rules);
+ while (link != up_to)
+ {
+ BusPolicyRule *rule = link->data;
+ DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
+
+ if (rule->type == type)
+ {
+ _dbus_list_remove_link (&policy->rules, link);
+ bus_policy_rule_unref (rule);
+ }
+
+ link = next;
+ }
+}
+
+void
+bus_client_policy_optimize (BusClientPolicy *policy)
+{
+ DBusList *link;
+
+ /* The idea here is that if we have:
+ *
+ * <allow send_interface="foo.bar"/>
+ * <deny send_interface="*"/>
+ *
+ * (for example) the deny will always override the allow. So we
+ * delete the allow. Ditto for deny followed by allow, etc. This is
+ * a dumb thing to put in a config file, but the <include> feature
+ * of files allows for an "inheritance and override" pattern where
+ * it could make sense. If an included file wants to "start over"
+ * with a blanket deny, no point keeping the rules from the parent
+ * file.
+ */
+
+ _dbus_verbose ("Optimizing policy with %d rules\n",
+ _dbus_list_get_length (&policy->rules));
+
+ link = _dbus_list_get_first_link (&policy->rules);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule;
+ DBusList *next;
+ dbus_bool_t remove_preceding;
+
+ next = _dbus_list_get_next_link (&policy->rules, link);
+ rule = link->data;
+
+ remove_preceding = FALSE;
+
+ _dbus_assert (rule != NULL);
+
+ switch (rule->type)
+ {
+ case BUS_POLICY_RULE_SEND:
+ remove_preceding =
+ rule->d.send.message_type == DBUS_MESSAGE_TYPE_INVALID &&
+ rule->d.send.path == NULL &&
+ rule->d.send.interface == NULL &&
+ rule->d.send.member == NULL &&
+ rule->d.send.error == NULL &&
+ rule->d.send.destination == NULL;
+ break;
+ case BUS_POLICY_RULE_RECEIVE:
+ remove_preceding =
+ rule->d.receive.message_type == DBUS_MESSAGE_TYPE_INVALID &&
+ rule->d.receive.path == NULL &&
+ rule->d.receive.interface == NULL &&
+ rule->d.receive.member == NULL &&
+ rule->d.receive.error == NULL &&
+ rule->d.receive.origin == NULL;
+ break;
+ case BUS_POLICY_RULE_OWN:
+ remove_preceding =
+ rule->d.own.service_name == NULL;
+ break;
+ case BUS_POLICY_RULE_USER:
+ case BUS_POLICY_RULE_GROUP:
+ _dbus_assert_not_reached ("invalid rule");
+ break;
+ }
+
+ if (remove_preceding)
+ remove_rules_by_type_up_to (policy, rule->type,
+ link);
+
+ link = next;
+ }
+
+ _dbus_verbose ("After optimization, policy has %d rules\n",
+ _dbus_list_get_length (&policy->rules));
+}
+
+dbus_bool_t
+bus_client_policy_append_rule (BusClientPolicy *policy,
+ BusPolicyRule *rule)
+{
+ _dbus_verbose ("Appending rule %p with type %d to policy %p\n",
+ rule, rule->type, policy);
+
+ if (!_dbus_list_append (&policy->rules, rule))
+ return FALSE;
+
+ bus_policy_rule_ref (rule);
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_client_policy_check_can_send (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *receiver,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ dbus_bool_t *log)
+{
+ DBusList *link;
+ dbus_bool_t allowed;
+
+ /* policy->rules is in the order the rules appeared
+ * in the config file, i.e. last rule that applies wins
+ */
+
+ _dbus_verbose (" (policy) checking send rules\n");
+ *toggles = 0;
+
+ allowed = FALSE;
+ link = _dbus_list_get_first_link (&policy->rules);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule = link->data;
+
+ link = _dbus_list_get_next_link (&policy->rules, link);
+
+ /* Rule is skipped if it specifies a different
+ * message name from the message, or a different
+ * destination from the message
+ */
+
+ if (rule->type != BUS_POLICY_RULE_SEND)
+ {
+ _dbus_verbose (" (policy) skipping non-send rule\n");
+ continue;
+ }
+
+ if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID)
+ {
+ if (dbus_message_get_type (message) != rule->d.send.message_type)
+ {
+ _dbus_verbose (" (policy) skipping rule for different message type\n");
+ continue;
+ }
+ }
+
+ /* If it's a reply, the requested_reply flag kicks in */
+ if (dbus_message_get_reply_serial (message) != 0)
+ {
+ /* for allow, requested_reply=true means the rule applies
+ * only when reply was requested. requested_reply=false means
+ * always allow.
+ */
+ if (!requested_reply && rule->allow && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
+ {
+ _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
+ continue;
+ }
+
+ /* for deny, requested_reply=false means the rule applies only
+ * when the reply was not requested. requested_reply=true means the
+ * rule always applies.
+ */
+ if (requested_reply && !rule->allow && !rule->d.send.requested_reply)
+ {
+ _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
+ continue;
+ }
+ }
+
+ if (rule->d.send.path != NULL)
+ {
+ if (dbus_message_get_path (message) != NULL &&
+ strcmp (dbus_message_get_path (message),
+ rule->d.send.path) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different path\n");
+ continue;
+ }
+ }
+
+ if (rule->d.send.interface != NULL)
+ {
+ /* The interface is optional in messages. For allow rules, if the message
+ * has no interface we want to skip the rule (and thus not allow);
+ * for deny rules, if the message has no interface we want to use the
+ * rule (and thus deny).
+ */
+ dbus_bool_t no_interface;
+
+ no_interface = dbus_message_get_interface (message) == NULL;
+
+ if ((no_interface && rule->allow) ||
+ (!no_interface &&
+ strcmp (dbus_message_get_interface (message),
+ rule->d.send.interface) != 0))
+ {
+ _dbus_verbose (" (policy) skipping rule for different interface\n");
+ continue;
+ }
+ }
+
+ if (rule->d.send.member != NULL)
+ {
+ if (dbus_message_get_member (message) != NULL &&
+ strcmp (dbus_message_get_member (message),
+ rule->d.send.member) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different member\n");
+ continue;
+ }
+ }
+
+ if (rule->d.send.error != NULL)
+ {
+ if (dbus_message_get_error_name (message) != NULL &&
+ strcmp (dbus_message_get_error_name (message),
+ rule->d.send.error) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different error name\n");
+ continue;
+ }
+ }
+
+ if (rule->d.send.destination != NULL)
+ {
+ /* receiver can be NULL for messages that are sent to the
+ * message bus itself, we check the strings in that case as
+ * built-in services don't have a DBusConnection but messages
+ * to them have a destination service name.
+ */
+ if (receiver == NULL)
+ {
+ if (!dbus_message_has_destination (message,
+ rule->d.send.destination))
+ {
+ _dbus_verbose (" (policy) skipping rule because message dest is not %s\n",
+ rule->d.send.destination);
+ continue;
+ }
+ }
+ else
+ {
+ DBusString str;
+ BusService *service;
+
+ _dbus_string_init_const (&str, rule->d.send.destination);
+
+ service = bus_registry_lookup (registry, &str);
+ if (service == NULL)
+ {
+ _dbus_verbose (" (policy) skipping rule because dest %s doesn't exist\n",
+ rule->d.send.destination);
+ continue;
+ }
+
+ if (!bus_service_has_owner (service, receiver))
+ {
+ _dbus_verbose (" (policy) skipping rule because dest %s isn't owned by receiver\n",
+ rule->d.send.destination);
+ continue;
+ }
+ }
+ }
+
+ /* Use this rule */
+ allowed = rule->allow;
+ *log = rule->d.send.log;
+ (*toggles)++;
+
+ _dbus_verbose (" (policy) used rule, allow now = %d\n",
+ allowed);
+ }
+
+ return allowed;
+}
+
+/* See docs on what the args mean on bus_context_check_security_policy()
+ * comment
+ */
+dbus_bool_t
+bus_client_policy_check_can_receive (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ dbus_int32_t *toggles)
+{
+ DBusList *link;
+ dbus_bool_t allowed;
+ dbus_bool_t eavesdropping;
+
+ eavesdropping =
+ addressed_recipient != proposed_recipient &&
+ dbus_message_get_destination (message) != NULL;
+
+ /* policy->rules is in the order the rules appeared
+ * in the config file, i.e. last rule that applies wins
+ */
+
+ _dbus_verbose (" (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
+ *toggles = 0;
+
+ allowed = FALSE;
+ link = _dbus_list_get_first_link (&policy->rules);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule = link->data;
+
+ link = _dbus_list_get_next_link (&policy->rules, link);
+
+ if (rule->type != BUS_POLICY_RULE_RECEIVE)
+ {
+ _dbus_verbose (" (policy) skipping non-receive rule\n");
+ continue;
+ }
+
+ if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID)
+ {
+ if (dbus_message_get_type (message) != rule->d.receive.message_type)
+ {
+ _dbus_verbose (" (policy) skipping rule for different message type\n");
+ continue;
+ }
+ }
+
+ /* for allow, eavesdrop=false means the rule doesn't apply when
+ * eavesdropping. eavesdrop=true means always allow.
+ */
+ if (eavesdropping && rule->allow && !rule->d.receive.eavesdrop)
+ {
+ _dbus_verbose (" (policy) skipping allow rule since it doesn't apply to eavesdropping\n");
+ continue;
+ }
+
+ /* for deny, eavesdrop=true means the rule applies only when
+ * eavesdropping; eavesdrop=false means always deny.
+ */
+ if (!eavesdropping && !rule->allow && rule->d.receive.eavesdrop)
+ {
+ _dbus_verbose (" (policy) skipping deny rule since it only applies to eavesdropping\n");
+ continue;
+ }
+
+ /* If it's a reply, the requested_reply flag kicks in */
+ if (dbus_message_get_reply_serial (message) != 0)
+ {
+ /* for allow, requested_reply=true means the rule applies
+ * only when reply was requested. requested_reply=false means
+ * always allow.
+ */
+ if (!requested_reply && rule->allow && rule->d.receive.requested_reply && !rule->d.receive.eavesdrop)
+ {
+ _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
+ continue;
+ }
+
+ /* for deny, requested_reply=false means the rule applies only
+ * when the reply was not requested. requested_reply=true means the
+ * rule always applies.
+ */
+ if (requested_reply && !rule->allow && !rule->d.receive.requested_reply)
+ {
+ _dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
+ continue;
+ }
+ }
+
+ if (rule->d.receive.path != NULL)
+ {
+ if (dbus_message_get_path (message) != NULL &&
+ strcmp (dbus_message_get_path (message),
+ rule->d.receive.path) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different path\n");
+ continue;
+ }
+ }
+
+ if (rule->d.receive.interface != NULL)
+ {
+ /* The interface is optional in messages. For allow rules, if the message
+ * has no interface we want to skip the rule (and thus not allow);
+ * for deny rules, if the message has no interface we want to use the
+ * rule (and thus deny).
+ */
+ dbus_bool_t no_interface;
+
+ no_interface = dbus_message_get_interface (message) == NULL;
+
+ if ((no_interface && rule->allow) ||
+ (!no_interface &&
+ strcmp (dbus_message_get_interface (message),
+ rule->d.receive.interface) != 0))
+ {
+ _dbus_verbose (" (policy) skipping rule for different interface\n");
+ continue;
+ }
+ }
+
+ if (rule->d.receive.member != NULL)
+ {
+ if (dbus_message_get_member (message) != NULL &&
+ strcmp (dbus_message_get_member (message),
+ rule->d.receive.member) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different member\n");
+ continue;
+ }
+ }
+
+ if (rule->d.receive.error != NULL)
+ {
+ if (dbus_message_get_error_name (message) != NULL &&
+ strcmp (dbus_message_get_error_name (message),
+ rule->d.receive.error) != 0)
+ {
+ _dbus_verbose (" (policy) skipping rule for different error name\n");
+ continue;
+ }
+ }
+
+ if (rule->d.receive.origin != NULL)
+ {
+ /* sender can be NULL for messages that originate from the
+ * message bus itself, we check the strings in that case as
+ * built-in services don't have a DBusConnection but will
+ * still set the sender on their messages.
+ */
+ if (sender == NULL)
+ {
+ if (!dbus_message_has_sender (message,
+ rule->d.receive.origin))
+ {
+ _dbus_verbose (" (policy) skipping rule because message sender is not %s\n",
+ rule->d.receive.origin);
+ continue;
+ }
+ }
+ else
+ {
+ BusService *service;
+ DBusString str;
+
+ _dbus_string_init_const (&str, rule->d.receive.origin);
+
+ service = bus_registry_lookup (registry, &str);
+
+ if (service == NULL)
+ {
+ _dbus_verbose (" (policy) skipping rule because origin %s doesn't exist\n",
+ rule->d.receive.origin);
+ continue;
+ }
+
+ if (!bus_service_has_owner (service, sender))
+ {
+ _dbus_verbose (" (policy) skipping rule because origin %s isn't owned by sender\n",
+ rule->d.receive.origin);
+ continue;
+ }
+ }
+ }
+
+ /* Use this rule */
+ allowed = rule->allow;
+ (*toggles)++;
+
+ _dbus_verbose (" (policy) used rule, allow now = %d\n",
+ allowed);
+ }
+
+ return allowed;
+}
+
+dbus_bool_t
+bus_client_policy_check_can_own (BusClientPolicy *policy,
+ DBusConnection *connection,
+ const DBusString *service_name)
+{
+ DBusList *link;
+ dbus_bool_t allowed;
+
+ /* policy->rules is in the order the rules appeared
+ * in the config file, i.e. last rule that applies wins
+ */
+
+ allowed = FALSE;
+ link = _dbus_list_get_first_link (&policy->rules);
+ while (link != NULL)
+ {
+ BusPolicyRule *rule = link->data;
+
+ link = _dbus_list_get_next_link (&policy->rules, link);
+
+ /* Rule is skipped if it specifies a different service name from
+ * the desired one.
+ */
+
+ if (rule->type != BUS_POLICY_RULE_OWN)
+ continue;
+
+ if (rule->d.own.service_name != NULL)
+ {
+ if (!_dbus_string_equal_c_str (service_name,
+ rule->d.own.service_name))
+ continue;
+ }
+
+ /* Use this rule */
+ allowed = rule->allow;
+ }
+
+ return allowed;
+}
+
+#ifdef DBUS_BUILD_TESTS
+
+dbus_bool_t
+bus_policy_test (const DBusString *test_data_dir)
+{
+ /* This doesn't do anything for now because I decided to do it in
+ * dispatch.c instead by having some of the clients in dispatch.c
+ * have particular policies applied to them.
+ */
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
diff --git a/bus/policy.h b/bus/policy.h
new file mode 100644
index 00000000..1782dbf3
--- /dev/null
+++ b/bus/policy.h
@@ -0,0 +1,164 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* policy.h Bus security policy
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_POLICY_H
+#define BUS_POLICY_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+#include "bus.h"
+
+typedef enum
+{
+ BUS_POLICY_RULE_SEND,
+ BUS_POLICY_RULE_RECEIVE,
+ BUS_POLICY_RULE_OWN,
+ BUS_POLICY_RULE_USER,
+ BUS_POLICY_RULE_GROUP
+} BusPolicyRuleType;
+
+/** determines whether the rule affects a connection, or some global item */
+#define BUS_POLICY_RULE_IS_PER_CLIENT(rule) (!((rule)->type == BUS_POLICY_RULE_USER || \
+ (rule)->type == BUS_POLICY_RULE_GROUP))
+
+struct BusPolicyRule
+{
+ int refcount;
+
+ BusPolicyRuleType type;
+
+ unsigned int allow : 1; /**< #TRUE if this allows, #FALSE if it denies */
+
+ union
+ {
+ struct
+ {
+ /* message type can be DBUS_MESSAGE_TYPE_INVALID meaning "any" */
+ int message_type;
+ /* any of these can be NULL meaning "any" */
+ char *path;
+ char *interface;
+ char *member;
+ char *error;
+ char *destination;
+ unsigned int eavesdrop : 1;
+ unsigned int requested_reply : 1;
+ unsigned int log : 1;
+ } send;
+
+ struct
+ {
+ /* message type can be DBUS_MESSAGE_TYPE_INVALID meaning "any" */
+ int message_type;
+ /* any of these can be NULL meaning "any" */
+ char *path;
+ char *interface;
+ char *member;
+ char *error;
+ char *origin;
+ unsigned int eavesdrop : 1;
+ unsigned int requested_reply : 1;
+ } receive;
+
+ struct
+ {
+ /* can be NULL meaning "any" */
+ char *service_name;
+ } own;
+
+ struct
+ {
+ /* can be DBUS_UID_UNSET meaning "any" */
+ dbus_uid_t uid;
+ } user;
+
+ struct
+ {
+ /* can be DBUS_GID_UNSET meaning "any" */
+ dbus_gid_t gid;
+ } group;
+
+ } d;
+};
+
+BusPolicyRule* bus_policy_rule_new (BusPolicyRuleType type,
+ dbus_bool_t allow);
+BusPolicyRule* bus_policy_rule_ref (BusPolicyRule *rule);
+void bus_policy_rule_unref (BusPolicyRule *rule);
+
+BusPolicy* bus_policy_new (void);
+BusPolicy* bus_policy_ref (BusPolicy *policy);
+void bus_policy_unref (BusPolicy *policy);
+BusClientPolicy* bus_policy_create_client_policy (BusPolicy *policy,
+ DBusConnection *connection,
+ DBusError *error);
+dbus_bool_t bus_policy_allow_unix_user (BusPolicy *policy,
+ unsigned long uid);
+dbus_bool_t bus_policy_allow_windows_user (BusPolicy *policy,
+ const char *windows_sid);
+dbus_bool_t bus_policy_append_default_rule (BusPolicy *policy,
+ BusPolicyRule *rule);
+dbus_bool_t bus_policy_append_mandatory_rule (BusPolicy *policy,
+ BusPolicyRule *rule);
+dbus_bool_t bus_policy_append_user_rule (BusPolicy *policy,
+ dbus_uid_t uid,
+ BusPolicyRule *rule);
+dbus_bool_t bus_policy_append_group_rule (BusPolicy *policy,
+ dbus_gid_t gid,
+ BusPolicyRule *rule);
+dbus_bool_t bus_policy_append_console_rule (BusPolicy *policy,
+ dbus_bool_t at_console,
+ BusPolicyRule *rule);
+
+dbus_bool_t bus_policy_merge (BusPolicy *policy,
+ BusPolicy *to_absorb);
+
+BusClientPolicy* bus_client_policy_new (void);
+BusClientPolicy* bus_client_policy_ref (BusClientPolicy *policy);
+void bus_client_policy_unref (BusClientPolicy *policy);
+dbus_bool_t bus_client_policy_check_can_send (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *receiver,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ dbus_bool_t *log);
+dbus_bool_t bus_client_policy_check_can_receive (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ dbus_int32_t *toggles);
+dbus_bool_t bus_client_policy_check_can_own (BusClientPolicy *policy,
+ DBusConnection *connection,
+ const DBusString *service_name);
+dbus_bool_t bus_client_policy_append_rule (BusClientPolicy *policy,
+ BusPolicyRule *rule);
+void bus_client_policy_optimize (BusClientPolicy *policy);
+
+
+#endif /* BUS_POLICY_H */
diff --git a/bus/rc.messagebus.in b/bus/rc.messagebus.in
new file mode 100644
index 00000000..b147503d
--- /dev/null
+++ b/bus/rc.messagebus.in
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# messagebus: The D-BUS systemwide message bus
+#
+# chkconfig: 345 97 03
+# description: This is a daemon which broadcasts notifications of system events \
+# and other messages. See http://www.freedesktop.org/software/dbus/
+#
+# processname: dbus-daemon
+# pidfile: @DBUS_SYSTEM_PID_FILE@
+#
+
+# Sanity checks.
+#[ -x @EXPANDED_BINDIR@/dbus-daemon ] || exit 0
+
+# Source function library.
+#. @EXPANDED_SYSCONFDIR@/rc.d/init.d/functions
+
+# so we can rearrange this easily
+#processname=dbus-daemon
+#servicename=messagebus
+
+#RETVAL=0
+
+start() {
+ echo "Starting system message bus"
+ if [ -x @EXPANDED_BINDIR@/dbus-uuidgen ] ; then
+ @EXPANDED_BINDIR@/dbus-uuidgen --ensure
+ fi
+
+ if [ -x @EXPANDED_BINDIR@/dbus-daemon ];then
+ @EXPANDED_BINDIR@/dbus-daemon --system
+ fi
+ #daemon --check $servicename $processname --system
+ #RETVAL=$?
+ #echo
+ #[ $RETVAL -eq 0 ] && touch @EXPANDED_LOCALSTATEDIR@/lock/subsys/$servicename
+}
+
+stop() {
+ echo "Stopping system message bus"
+
+ ## we don't want to kill all the per-user $processname, we want
+ ## to use the pid file *only*; because we use the fake nonexistent
+ ## program name "$servicename" that should be safe-ish
+ killall dbus-daemon
+ #RETVAL=$?
+ #echo
+ #if [ $RETVAL -eq 0 ]; then
+ # rm -f @EXPANDED_LOCALSTATEDIR@/lock/subsys/$servicename
+ # rm -f @DBUS_SYSTEM_PID_FILE@
+ #fi
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $processname
+ RETVAL=$?
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ reload)
+ echo "Message bus can't reload its configuration, you have to restart it"
+ RETVAL=$?
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|reload}"
+ ;;
+esac
+exit $RETVAL
diff --git a/bus/selinux.c b/bus/selinux.c
new file mode 100644
index 00000000..5a9af5ac
--- /dev/null
+++ b/bus/selinux.c
@@ -0,0 +1,1091 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ * selinux.c SELinux security checks for D-Bus
+ *
+ * Author: Matthew Rickard <mjricka@epoch.ncsc.mil>
+ *
+ * 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 <dbus/dbus-internals.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-userdb.h>
+#include "selinux.h"
+#include "services.h"
+#include "policy.h"
+#include "utils.h"
+#include "config-parser.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SELINUX
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <pthread.h>
+#include <syslog.h>
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include <selinux/av_permissions.h>
+#include <selinux/flask.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <grp.h>
+#endif /* HAVE_SELINUX */
+#ifdef HAVE_LIBAUDIT
+#include <cap-ng.h>
+#include <libaudit.h>
+#endif /* HAVE_LIBAUDIT */
+
+#define BUS_SID_FROM_SELINUX(sid) ((BusSELinuxID*) (sid))
+#define SELINUX_SID_FROM_BUS(sid) ((security_id_t) (sid))
+
+#ifdef HAVE_SELINUX
+/* Store the value telling us if SELinux is enabled in the kernel. */
+static dbus_bool_t selinux_enabled = FALSE;
+
+/* Store an avc_entry_ref to speed AVC decisions. */
+static struct avc_entry_ref aeref;
+
+/* Store the SID of the bus itself to use as the default. */
+static security_id_t bus_sid = SECSID_WILD;
+
+/* Thread to listen for SELinux status changes via netlink. */
+static pthread_t avc_notify_thread;
+
+/* Prototypes for AVC callback functions. */
+static void log_callback (const char *fmt, ...);
+static void log_audit_callback (void *data, security_class_t class, char *buf, size_t bufleft);
+static void *avc_create_thread (void (*run) (void));
+static void avc_stop_thread (void *thread);
+static void *avc_alloc_lock (void);
+static void avc_get_lock (void *lock);
+static void avc_release_lock (void *lock);
+static void avc_free_lock (void *lock);
+
+/* AVC callback structures for use in avc_init. */
+static const struct avc_memory_callback mem_cb =
+{
+ .func_malloc = dbus_malloc,
+ .func_free = dbus_free
+};
+static const struct avc_log_callback log_cb =
+{
+ .func_log = log_callback,
+ .func_audit = log_audit_callback
+};
+static const struct avc_thread_callback thread_cb =
+{
+ .func_create_thread = avc_create_thread,
+ .func_stop_thread = avc_stop_thread
+};
+static const struct avc_lock_callback lock_cb =
+{
+ .func_alloc_lock = avc_alloc_lock,
+ .func_get_lock = avc_get_lock,
+ .func_release_lock = avc_release_lock,
+ .func_free_lock = avc_free_lock
+};
+#endif /* HAVE_SELINUX */
+
+/**
+ * Log callback to log denial messages from the AVC.
+ * This is used in avc_init. Logs to both standard
+ * error and syslogd.
+ *
+ * @param fmt the format string
+ * @param variable argument list
+ */
+#ifdef HAVE_SELINUX
+
+#ifdef HAVE_LIBAUDIT
+static int audit_fd = -1;
+#endif
+
+void
+bus_selinux_audit_init(void)
+{
+#ifdef HAVE_LIBAUDIT
+ audit_fd = audit_open ();
+
+ if (audit_fd < 0)
+ {
+ /* If kernel doesn't support audit, bail out */
+ if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT)
+ return;
+ /* If user bus, bail out */
+ if (errno == EPERM && getuid() != 0)
+ return;
+ _dbus_warn ("Failed opening connection to the audit subsystem");
+ }
+#endif /* HAVE_LIBAUDIT */
+}
+
+static void
+log_callback (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#ifdef HAVE_LIBAUDIT
+ if (audit_fd >= 0)
+ {
+ capng_get_caps_process();
+ if (capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_WRITE))
+ {
+ char buf[PATH_MAX*2];
+
+ /* FIXME: need to change this to show real user */
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
+ NULL, getuid());
+ return;
+ }
+ }
+#endif /* HAVE_LIBAUDIT */
+
+ vsyslog (LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * On a policy reload we need to reparse the SELinux configuration file, since
+ * this could have changed. Send a SIGHUP to reload all configs.
+ */
+static int
+policy_reload_callback (u_int32_t event, security_id_t ssid,
+ security_id_t tsid, security_class_t tclass,
+ access_vector_t perms, access_vector_t *out_retained)
+{
+ if (event == AVC_CALLBACK_RESET)
+ return raise (SIGHUP);
+
+ return 0;
+}
+
+/**
+ * Log any auxiliary data
+ */
+static void
+log_audit_callback (void *data, security_class_t class, char *buf, size_t bufleft)
+{
+ DBusString *audmsg = data;
+
+ if (bufleft > (size_t) _dbus_string_get_length(audmsg))
+ {
+ _dbus_string_copy_to_buffer_with_nul (audmsg, buf, bufleft);
+ }
+ else
+ {
+ DBusString s;
+
+ _dbus_string_init_const(&s, "Buffer too small for audit message");
+
+ if (bufleft > (size_t) _dbus_string_get_length(&s))
+ _dbus_string_copy_to_buffer_with_nul (&s, buf, bufleft);
+ }
+}
+
+/**
+ * Create thread to notify the AVC of enforcing and policy reload
+ * changes via netlink.
+ *
+ * @param run the thread run function
+ * @return pointer to the thread
+ */
+static void *
+avc_create_thread (void (*run) (void))
+{
+ int rc;
+
+ rc = pthread_create (&avc_notify_thread, NULL, (void *(*) (void *)) run, NULL);
+ if (rc != 0)
+ {
+ _dbus_warn ("Failed to start AVC thread: %s\n", _dbus_strerror (rc));
+ exit (1);
+ }
+ return &avc_notify_thread;
+}
+
+/* Stop AVC netlink thread. */
+static void
+avc_stop_thread (void *thread)
+{
+ pthread_cancel (*(pthread_t *) thread);
+}
+
+/* Allocate a new AVC lock. */
+static void *
+avc_alloc_lock (void)
+{
+ pthread_mutex_t *avc_mutex;
+
+ avc_mutex = dbus_new (pthread_mutex_t, 1);
+ if (avc_mutex == NULL)
+ {
+ _dbus_warn ("Could not create mutex: %s\n", _dbus_strerror (errno));
+ exit (1);
+ }
+ pthread_mutex_init (avc_mutex, NULL);
+
+ return avc_mutex;
+}
+
+/* Acquire an AVC lock. */
+static void
+avc_get_lock (void *lock)
+{
+ pthread_mutex_lock (lock);
+}
+
+/* Release an AVC lock. */
+static void
+avc_release_lock (void *lock)
+{
+ pthread_mutex_unlock (lock);
+}
+
+/* Free an AVC lock. */
+static void
+avc_free_lock (void *lock)
+{
+ pthread_mutex_destroy (lock);
+ dbus_free (lock);
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Return whether or not SELinux is enabled; must be
+ * called after bus_selinux_init.
+ */
+dbus_bool_t
+bus_selinux_enabled (void)
+{
+#ifdef HAVE_SELINUX
+ return selinux_enabled;
+#else
+ return FALSE;
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Do early initialization; determine whether SELinux is enabled.
+ */
+dbus_bool_t
+bus_selinux_pre_init (void)
+{
+#ifdef HAVE_SELINUX
+ int r;
+ _dbus_assert (bus_sid == SECSID_WILD);
+
+ /* Determine if we are running an SELinux kernel. */
+ r = is_selinux_enabled ();
+ if (r < 0)
+ {
+ _dbus_warn ("Could not tell if SELinux is enabled: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ selinux_enabled = r != 0;
+ return TRUE;
+#else
+ return TRUE;
+#endif
+}
+
+/**
+ * Initialize the user space access vector cache (AVC) for D-Bus and set up
+ * logging callbacks.
+ */
+dbus_bool_t
+bus_selinux_full_init (void)
+{
+#ifdef HAVE_SELINUX
+ char *bus_context;
+
+ _dbus_assert (bus_sid == SECSID_WILD);
+
+ if (!selinux_enabled)
+ {
+ _dbus_verbose ("SELinux not enabled in this kernel.\n");
+ return TRUE;
+ }
+
+ _dbus_verbose ("SELinux is enabled in this kernel.\n");
+
+ avc_entry_ref_init (&aeref);
+ if (avc_init ("avc", &mem_cb, &log_cb, &thread_cb, &lock_cb) < 0)
+ {
+ _dbus_warn ("Failed to start Access Vector Cache (AVC).\n");
+ return FALSE;
+ }
+ else
+ {
+ openlog ("dbus", LOG_PERROR, LOG_USER);
+ _dbus_verbose ("Access Vector Cache (AVC) started.\n");
+ }
+
+ if (avc_add_callback (policy_reload_callback, AVC_CALLBACK_RESET,
+ NULL, NULL, 0, 0) < 0)
+ {
+ _dbus_warn ("Failed to add policy reload callback: %s\n",
+ _dbus_strerror (errno));
+ avc_destroy ();
+ return FALSE;
+ }
+
+ bus_context = NULL;
+ bus_sid = SECSID_WILD;
+
+ if (getcon (&bus_context) < 0)
+ {
+ _dbus_verbose ("Error getting context of bus: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ if (avc_context_to_sid (bus_context, &bus_sid) < 0)
+ {
+ _dbus_verbose ("Error getting SID from bus context: %s\n",
+ _dbus_strerror (errno));
+ freecon (bus_context);
+ return FALSE;
+ }
+
+ freecon (bus_context);
+
+#endif /* HAVE_SELINUX */
+ return TRUE;
+}
+
+/**
+ * Decrement SID reference count.
+ *
+ * @param sid the SID to decrement
+ */
+void
+bus_selinux_id_unref (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+ if (!selinux_enabled)
+ return;
+
+ _dbus_assert (sid != NULL);
+
+ sidput (SELINUX_SID_FROM_BUS (sid));
+#endif /* HAVE_SELINUX */
+}
+
+void
+bus_selinux_id_ref (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+ if (!selinux_enabled)
+ return;
+
+ _dbus_assert (sid != NULL);
+
+ sidget (SELINUX_SID_FROM_BUS (sid));
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Determine if the SELinux security policy allows the given sender
+ * security context to go to the given recipient security context.
+ * This function determines if the requested permissions are to be
+ * granted from the connection to the message bus or to another
+ * optionally supplied security identifier (e.g. for a service
+ * context). Currently these permissions are either send_msg or
+ * acquire_svc in the dbus class.
+ *
+ * @param sender_sid source security context
+ * @param override_sid is the target security context. If SECSID_WILD this will
+ * use the context of the bus itself (e.g. the default).
+ * @param target_class is the target security class.
+ * @param requested is the requested permissions.
+ * @returns #TRUE if security policy allows the send.
+ */
+#ifdef HAVE_SELINUX
+static dbus_bool_t
+bus_selinux_check (BusSELinuxID *sender_sid,
+ BusSELinuxID *override_sid,
+ security_class_t target_class,
+ access_vector_t requested,
+ DBusString *auxdata)
+{
+ if (!selinux_enabled)
+ return TRUE;
+
+ /* Make the security check. AVC checks enforcing mode here as well. */
+ if (avc_has_perm (SELINUX_SID_FROM_BUS (sender_sid),
+ override_sid ?
+ SELINUX_SID_FROM_BUS (override_sid) :
+ SELINUX_SID_FROM_BUS (bus_sid),
+ target_class, requested, &aeref, auxdata) < 0)
+ {
+ switch (errno)
+ {
+ case EACCES:
+ _dbus_verbose ("SELinux denying due to security policy.\n");
+ return FALSE;
+ case EINVAL:
+ _dbus_verbose ("SELinux denying due to invalid security context.\n");
+ return FALSE;
+ default:
+ _dbus_verbose ("SELinux denying due to: %s\n", _dbus_strerror (errno));
+ return FALSE;
+ }
+ }
+ else
+ return TRUE;
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Returns true if the given connection can acquire a service,
+ * assuming the given security ID is needed for that service.
+ *
+ * @param connection connection that wants to own the service
+ * @param service_sid the SID of the service from the table
+ * @returns #TRUE if acquire is permitted.
+ */
+dbus_bool_t
+bus_selinux_allows_acquire_service (DBusConnection *connection,
+ BusSELinuxID *service_sid,
+ const char *service_name,
+ DBusError *error)
+{
+#ifdef HAVE_SELINUX
+ BusSELinuxID *connection_sid;
+ unsigned long spid;
+ DBusString auxdata;
+ dbus_bool_t ret;
+
+ if (!selinux_enabled)
+ return TRUE;
+
+ connection_sid = bus_connection_get_selinux_id (connection);
+ if (!dbus_connection_get_unix_process_id (connection, &spid))
+ spid = 0;
+
+ if (!_dbus_string_init (&auxdata))
+ goto oom;
+
+ if (!_dbus_string_append (&auxdata, "service="))
+ goto oom;
+
+ if (!_dbus_string_append (&auxdata, service_name))
+ goto oom;
+
+ if (spid)
+ {
+ if (!_dbus_string_append (&auxdata, " spid="))
+ goto oom;
+
+ if (!_dbus_string_append_uint (&auxdata, spid))
+ goto oom;
+ }
+
+ ret = bus_selinux_check (connection_sid,
+ service_sid,
+ SECCLASS_DBUS,
+ DBUS__ACQUIRE_SVC,
+ &auxdata);
+
+ _dbus_string_free (&auxdata);
+ return ret;
+
+ oom:
+ _dbus_string_free (&auxdata);
+ BUS_SET_OOM (error);
+ return FALSE;
+
+#else
+ return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Check if SELinux security controls allow the message to be sent to a
+ * particular connection based on the security context of the sender and
+ * that of the receiver. The destination connection need not be the
+ * addressed recipient, it could be an "eavesdropper"
+ *
+ * @param sender the sender of the message.
+ * @param proposed_recipient the connection the message is to be sent to.
+ * @returns whether to allow the send
+ */
+dbus_bool_t
+bus_selinux_allows_send (DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ const char *msgtype,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ const char *destination,
+ DBusError *error)
+{
+#ifdef HAVE_SELINUX
+ BusSELinuxID *recipient_sid;
+ BusSELinuxID *sender_sid;
+ unsigned long spid, tpid;
+ DBusString auxdata;
+ dbus_bool_t ret;
+ dbus_bool_t string_alloced;
+
+ if (!selinux_enabled)
+ return TRUE;
+
+ if (!sender || !dbus_connection_get_unix_process_id (sender, &spid))
+ spid = 0;
+ if (!proposed_recipient || !dbus_connection_get_unix_process_id (proposed_recipient, &tpid))
+ tpid = 0;
+
+ string_alloced = FALSE;
+ if (!_dbus_string_init (&auxdata))
+ goto oom;
+ string_alloced = TRUE;
+
+ if (!_dbus_string_append (&auxdata, "msgtype="))
+ goto oom;
+
+ if (!_dbus_string_append (&auxdata, msgtype))
+ goto oom;
+
+ if (interface)
+ {
+ if (!_dbus_string_append (&auxdata, " interface="))
+ goto oom;
+ if (!_dbus_string_append (&auxdata, interface))
+ goto oom;
+ }
+
+ if (member)
+ {
+ if (!_dbus_string_append (&auxdata, " member="))
+ goto oom;
+ if (!_dbus_string_append (&auxdata, member))
+ goto oom;
+ }
+
+ if (error_name)
+ {
+ if (!_dbus_string_append (&auxdata, " error_name="))
+ goto oom;
+ if (!_dbus_string_append (&auxdata, error_name))
+ goto oom;
+ }
+
+ if (destination)
+ {
+ if (!_dbus_string_append (&auxdata, " dest="))
+ goto oom;
+ if (!_dbus_string_append (&auxdata, destination))
+ goto oom;
+ }
+
+ if (spid)
+ {
+ if (!_dbus_string_append (&auxdata, " spid="))
+ goto oom;
+
+ if (!_dbus_string_append_uint (&auxdata, spid))
+ goto oom;
+ }
+
+ if (tpid)
+ {
+ if (!_dbus_string_append (&auxdata, " tpid="))
+ goto oom;
+
+ if (!_dbus_string_append_uint (&auxdata, tpid))
+ goto oom;
+ }
+
+ sender_sid = bus_connection_get_selinux_id (sender);
+ /* A NULL proposed_recipient means the bus itself. */
+ if (proposed_recipient)
+ recipient_sid = bus_connection_get_selinux_id (proposed_recipient);
+ else
+ recipient_sid = BUS_SID_FROM_SELINUX (bus_sid);
+
+ ret = bus_selinux_check (sender_sid,
+ recipient_sid,
+ SECCLASS_DBUS,
+ DBUS__SEND_MSG,
+ &auxdata);
+
+ _dbus_string_free (&auxdata);
+
+ return ret;
+
+ oom:
+ if (string_alloced)
+ _dbus_string_free (&auxdata);
+ BUS_SET_OOM (error);
+ return FALSE;
+
+#else
+ return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+dbus_bool_t
+bus_selinux_append_context (DBusMessage *message,
+ BusSELinuxID *sid,
+ DBusError *error)
+{
+#ifdef HAVE_SELINUX
+ char *context;
+
+ if (avc_sid_to_context (SELINUX_SID_FROM_BUS (sid), &context) < 0)
+ {
+ if (errno == ENOMEM)
+ BUS_SET_OOM (error);
+ else
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Error getting context from SID: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE,
+ &context,
+ strlen (context),
+ DBUS_TYPE_INVALID))
+ {
+ _DBUS_SET_OOM (error);
+ return FALSE;
+ }
+ freecon (context);
+ return TRUE;
+#else
+ return TRUE;
+#endif
+}
+
+/**
+ * Gets the security context of a connection to the bus. It is up to
+ * the caller to freecon() when they are done.
+ *
+ * @param connection the connection to get the context of.
+ * @param con the location to store the security context.
+ * @returns #TRUE if context is successfully obtained.
+ */
+#ifdef HAVE_SELINUX
+static dbus_bool_t
+bus_connection_read_selinux_context (DBusConnection *connection,
+ char **con)
+{
+ int fd;
+
+ if (!selinux_enabled)
+ return FALSE;
+
+ _dbus_assert (connection != NULL);
+
+ if (!dbus_connection_get_unix_fd (connection, &fd))
+ {
+ _dbus_verbose ("Failed to get file descriptor of socket.\n");
+ return FALSE;
+ }
+
+ if (getpeercon (fd, con) < 0)
+ {
+ _dbus_verbose ("Error getting context of socket peer: %s\n",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ _dbus_verbose ("Successfully read connection context.\n");
+ return TRUE;
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Read the SELinux ID from the connection.
+ *
+ * @param connection the connection to read from
+ * @returns the SID if successfully determined, #NULL otherwise.
+ */
+BusSELinuxID*
+bus_selinux_init_connection_id (DBusConnection *connection,
+ DBusError *error)
+{
+#ifdef HAVE_SELINUX
+ char *con;
+ security_id_t sid;
+
+ if (!selinux_enabled)
+ return NULL;
+
+ if (!bus_connection_read_selinux_context (connection, &con))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to read an SELinux context from connection");
+ _dbus_verbose ("Error getting peer context.\n");
+ return NULL;
+ }
+
+ _dbus_verbose ("Converting context to SID to store on connection\n");
+
+ if (avc_context_to_sid (con, &sid) < 0)
+ {
+ if (errno == ENOMEM)
+ BUS_SET_OOM (error);
+ else
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Error getting SID from context \"%s\": %s\n",
+ con, _dbus_strerror (errno));
+
+ _dbus_warn ("Error getting SID from context \"%s\": %s\n",
+ con, _dbus_strerror (errno));
+
+ freecon (con);
+ return NULL;
+ }
+
+ freecon (con);
+ return BUS_SID_FROM_SELINUX (sid);
+#else
+ return NULL;
+#endif /* HAVE_SELINUX */
+}
+
+
+/**
+ * Function for freeing hash table data. These SIDs
+ * should no longer be referenced.
+ */
+static void
+bus_selinux_id_table_free_value (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+ /* NULL sometimes due to how DBusHashTable works */
+ if (sid)
+ bus_selinux_id_unref (sid);
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Creates a new table mapping service names to security ID.
+ * A security ID is a "compiled" security context, a security
+ * context is just a string.
+ *
+ * @returns the new table or #NULL if no memory
+ */
+DBusHashTable*
+bus_selinux_id_table_new (void)
+{
+ return _dbus_hash_table_new (DBUS_HASH_STRING,
+ (DBusFreeFunction) dbus_free,
+ (DBusFreeFunction) bus_selinux_id_table_free_value);
+}
+
+/**
+ * Hashes a service name and service context into the service SID
+ * table as a string and a SID.
+ *
+ * @param service_name is the name of the service.
+ * @param service_context is the context of the service.
+ * @param service_table is the table to hash them into.
+ * @return #FALSE if not enough memory
+ */
+dbus_bool_t
+bus_selinux_id_table_insert (DBusHashTable *service_table,
+ const char *service_name,
+ const char *service_context)
+{
+#ifdef HAVE_SELINUX
+ dbus_bool_t retval;
+ security_id_t sid;
+ char *key;
+
+ if (!selinux_enabled)
+ return TRUE;
+
+ sid = SECSID_WILD;
+ retval = FALSE;
+
+ key = _dbus_strdup (service_name);
+ if (key == NULL)
+ return retval;
+
+ if (avc_context_to_sid ((char *) service_context, &sid) < 0)
+ {
+ if (errno == ENOMEM)
+ {
+ dbus_free (key);
+ return FALSE;
+ }
+
+ _dbus_warn ("Error getting SID from context \"%s\": %s\n",
+ (char *) service_context,
+ _dbus_strerror (errno));
+ goto out;
+ }
+
+ if (!_dbus_hash_table_insert_string (service_table,
+ key,
+ BUS_SID_FROM_SELINUX (sid)))
+ goto out;
+
+ _dbus_verbose ("Parsed \tservice: %s \n\t\tcontext: %s\n",
+ key,
+ sid->ctx);
+
+ /* These are owned by the hash, so clear them to avoid unref */
+ key = NULL;
+ sid = SECSID_WILD;
+
+ retval = TRUE;
+
+ out:
+ if (sid != SECSID_WILD)
+ sidput (sid);
+
+ if (key)
+ dbus_free (key);
+
+ return retval;
+#else
+ return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+
+/**
+ * Find the security identifier associated with a particular service
+ * name. Return a pointer to this SID, or #NULL/SECSID_WILD if the
+ * service is not found in the hash table. This should be nearly a
+ * constant time operation. If SELinux support is not available,
+ * always return NULL.
+ *
+ * @param service_table the hash table to check for service name.
+ * @param service_name the name of the service to look for.
+ * @returns the SELinux ID associated with the service
+ */
+BusSELinuxID*
+bus_selinux_id_table_lookup (DBusHashTable *service_table,
+ const DBusString *service_name)
+{
+#ifdef HAVE_SELINUX
+ security_id_t sid;
+
+ sid = SECSID_WILD; /* default context */
+
+ if (!selinux_enabled)
+ return NULL;
+
+ _dbus_verbose ("Looking up service SID for %s\n",
+ _dbus_string_get_const_data (service_name));
+
+ sid = _dbus_hash_table_lookup_string (service_table,
+ _dbus_string_get_const_data (service_name));
+
+ if (sid == SECSID_WILD)
+ _dbus_verbose ("Service %s not found\n",
+ _dbus_string_get_const_data (service_name));
+ else
+ _dbus_verbose ("Service %s found\n",
+ _dbus_string_get_const_data (service_name));
+
+ return BUS_SID_FROM_SELINUX (sid);
+#endif /* HAVE_SELINUX */
+ return NULL;
+}
+
+/**
+ * Get the SELinux policy root. This is used to find the D-Bus
+ * specific config file within the policy.
+ */
+const char *
+bus_selinux_get_policy_root (void)
+{
+#ifdef HAVE_SELINUX
+ return selinux_policy_root ();
+#else
+ return NULL;
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * For debugging: Print out the current hash table of service SIDs.
+ */
+void
+bus_selinux_id_table_print (DBusHashTable *service_table)
+{
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+#ifdef HAVE_SELINUX
+ DBusHashIter iter;
+
+ if (!selinux_enabled)
+ return;
+
+ _dbus_verbose ("Service SID Table:\n");
+ _dbus_hash_iter_init (service_table, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ const char *key = _dbus_hash_iter_get_string_key (&iter);
+ security_id_t sid = _dbus_hash_iter_get_value (&iter);
+ _dbus_verbose ("The key is %s\n", key);
+ _dbus_verbose ("The context is %s\n", sid->ctx);
+ _dbus_verbose ("The refcount is %d\n", sid->refcnt);
+ }
+#endif /* HAVE_SELINUX */
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+}
+
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+#ifdef HAVE_SELINUX
+/**
+ * Print out some AVC statistics.
+ */
+static void
+bus_avc_print_stats (void)
+{
+ struct avc_cache_stats cstats;
+
+ if (!selinux_enabled)
+ return;
+
+ _dbus_verbose ("AVC Statistics:\n");
+ avc_cache_stats (&cstats);
+ avc_av_stats ();
+ _dbus_verbose ("AVC Cache Statistics:\n");
+ _dbus_verbose ("Entry lookups: %d\n", cstats.entry_lookups);
+ _dbus_verbose ("Entry hits: %d\n", cstats.entry_hits);
+ _dbus_verbose ("Entry misses %d\n", cstats.entry_misses);
+ _dbus_verbose ("Entry discards: %d\n", cstats.entry_discards);
+ _dbus_verbose ("CAV lookups: %d\n", cstats.cav_lookups);
+ _dbus_verbose ("CAV hits: %d\n", cstats.cav_hits);
+ _dbus_verbose ("CAV probes: %d\n", cstats.cav_probes);
+ _dbus_verbose ("CAV misses: %d\n", cstats.cav_misses);
+}
+#endif /* HAVE_SELINUX */
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+
+/**
+ * Destroy the AVC before we terminate.
+ */
+void
+bus_selinux_shutdown (void)
+{
+#ifdef HAVE_SELINUX
+ if (!selinux_enabled)
+ return;
+
+ _dbus_verbose ("AVC shutdown\n");
+
+ if (bus_sid != SECSID_WILD)
+ {
+ sidput (bus_sid);
+ bus_sid = SECSID_WILD;
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+
+ if (_dbus_is_verbose())
+ bus_avc_print_stats ();
+
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+ avc_destroy ();
+#ifdef HAVE_LIBAUDIT
+ audit_close (audit_fd);
+#endif /* HAVE_LIBAUDIT */
+ }
+#endif /* HAVE_SELINUX */
+}
+
+/* The !HAVE_LIBAUDIT case lives in dbus-sysdeps-util-unix.c */
+#ifdef HAVE_LIBAUDIT
+/**
+ * Changes the user and group the bus is running as.
+ *
+ * @param user the user to become
+ * @param error return location for errors
+ * @returns #FALSE on failure
+ */
+dbus_bool_t
+_dbus_change_to_daemon_user (const char *user,
+ DBusError *error)
+{
+ dbus_uid_t uid;
+ dbus_gid_t gid;
+ DBusString u;
+
+ _dbus_string_init_const (&u, user);
+
+ if (!_dbus_get_user_id_and_primary_group (&u, &uid, &gid))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "User '%s' does not appear to exist?",
+ user);
+ return FALSE;
+ }
+
+ /* If we were root */
+ if (_dbus_geteuid () == 0)
+ {
+ int rc;
+
+ capng_clear (CAPNG_SELECT_BOTH);
+ capng_update (CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_AUDIT_WRITE);
+ rc = capng_change_id (uid, gid, 0);
+ if (rc)
+ {
+ switch (rc) {
+ default:
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to drop capabilities: %s\n",
+ _dbus_strerror (errno));
+ break;
+ case -4:
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set GID to %lu: %s", gid,
+ _dbus_strerror (errno));
+ break;
+ case -5:
+ _dbus_warn ("Failed to drop supplementary groups: %s\n",
+ _dbus_strerror (errno));
+ break;
+ case -6:
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to set UID to %lu: %s", uid,
+ _dbus_strerror (errno));
+ break;
+ case -7:
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to unset keep-capabilities: %s\n",
+ _dbus_strerror (errno));
+ break;
+ }
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+#endif
+
diff --git a/bus/selinux.h b/bus/selinux.h
new file mode 100644
index 00000000..3bab36de
--- /dev/null
+++ b/bus/selinux.h
@@ -0,0 +1,72 @@
+/* selinux.h SELinux security check headers for D-BUS
+ *
+ * Author: Matthew Rickard <mjricka@epoch.ncsc.mil>
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_SELINUX_H
+#define BUS_SELINUX_H
+
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-connection.h>
+#include "services.h"
+
+dbus_bool_t bus_selinux_pre_init (void);
+dbus_bool_t bus_selinux_full_init(void);
+void bus_selinux_shutdown (void);
+
+dbus_bool_t bus_selinux_enabled (void);
+
+void bus_selinux_id_ref (BusSELinuxID *sid);
+void bus_selinux_id_unref (BusSELinuxID *sid);
+
+DBusHashTable* bus_selinux_id_table_new (void);
+BusSELinuxID* bus_selinux_id_table_lookup (DBusHashTable *service_table,
+ const DBusString *service_name);
+dbus_bool_t bus_selinux_id_table_insert (DBusHashTable *service_table,
+ const char *service_name,
+ const char *service_context);
+void bus_selinux_id_table_print (DBusHashTable *service_table);
+const char* bus_selinux_get_policy_root (void);
+
+dbus_bool_t bus_selinux_append_context (DBusMessage *message,
+ BusSELinuxID *context,
+ DBusError *error);
+
+dbus_bool_t bus_selinux_allows_acquire_service (DBusConnection *connection,
+ BusSELinuxID *service_sid,
+ const char *service_name,
+ DBusError *error);
+
+dbus_bool_t bus_selinux_allows_send (DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ const char *msgtype, /* Supplementary audit data */
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ const char *destination,
+ DBusError *error);
+
+BusSELinuxID* bus_selinux_init_connection_id (DBusConnection *connection,
+ DBusError *error);
+
+
+void bus_selinux_audit_init(void);
+
+#endif /* BUS_SELINUX_H */
diff --git a/bus/services.c b/bus/services.c
new file mode 100644
index 00000000..b260c633
--- /dev/null
+++ b/bus/services.c
@@ -0,0 +1,1306 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* services.c Service management
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003 CodeFactory AB
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (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 <dbus/dbus-hash.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-mempool.h>
+#include <dbus/dbus-marshal-validate.h>
+
+#include "driver.h"
+#include "services.h"
+#include "connection.h"
+#include "utils.h"
+#include "activation.h"
+#include "policy.h"
+#include "bus.h"
+#include "selinux.h"
+
+struct BusService
+{
+ int refcount;
+
+ BusRegistry *registry;
+ char *name;
+ DBusList *owners;
+};
+
+struct BusOwner
+{
+ int refcount;
+
+ BusService *service;
+ DBusConnection *conn;
+
+ unsigned int allow_replacement : 1;
+ unsigned int do_not_queue : 1;
+};
+
+struct BusRegistry
+{
+ int refcount;
+
+ BusContext *context;
+
+ DBusHashTable *service_hash;
+ DBusMemPool *service_pool;
+ DBusMemPool *owner_pool;
+
+ DBusHashTable *service_sid_table;
+};
+
+BusRegistry*
+bus_registry_new (BusContext *context)
+{
+ BusRegistry *registry;
+
+ registry = dbus_new0 (BusRegistry, 1);
+ if (registry == NULL)
+ return NULL;
+
+ registry->refcount = 1;
+ registry->context = context;
+
+ registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
+ NULL, NULL);
+ if (registry->service_hash == NULL)
+ goto failed;
+
+ registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
+ TRUE);
+
+ if (registry->service_pool == NULL)
+ goto failed;
+
+ registry->owner_pool = _dbus_mem_pool_new (sizeof (BusOwner),
+ TRUE);
+
+ if (registry->owner_pool == NULL)
+ goto failed;
+
+ registry->service_sid_table = NULL;
+
+ return registry;
+
+ failed:
+ bus_registry_unref (registry);
+ return NULL;
+}
+
+BusRegistry *
+bus_registry_ref (BusRegistry *registry)
+{
+ _dbus_assert (registry->refcount > 0);
+ registry->refcount += 1;
+
+ return registry;
+}
+
+void
+bus_registry_unref (BusRegistry *registry)
+{
+ _dbus_assert (registry->refcount > 0);
+ registry->refcount -= 1;
+
+ if (registry->refcount == 0)
+ {
+ if (registry->service_hash)
+ _dbus_hash_table_unref (registry->service_hash);
+ if (registry->service_pool)
+ _dbus_mem_pool_free (registry->service_pool);
+ if (registry->owner_pool)
+ _dbus_mem_pool_free (registry->owner_pool);
+ if (registry->service_sid_table)
+ _dbus_hash_table_unref (registry->service_sid_table);
+
+ dbus_free (registry);
+ }
+}
+
+BusService*
+bus_registry_lookup (BusRegistry *registry,
+ const DBusString *service_name)
+{
+ BusService *service;
+
+ service = _dbus_hash_table_lookup_string (registry->service_hash,
+ _dbus_string_get_const_data (service_name));
+
+ return service;
+}
+
+static DBusList *
+_bus_service_find_owner_link (BusService *service,
+ DBusConnection *connection)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&service->owners);
+
+ while (link != NULL)
+ {
+ BusOwner *bus_owner;
+
+ bus_owner = (BusOwner *) link->data;
+ if (bus_owner->conn == connection)
+ break;
+
+ link = _dbus_list_get_next_link (&service->owners, link);
+ }
+
+ return link;
+}
+
+static void
+bus_owner_set_flags (BusOwner *owner,
+ dbus_uint32_t flags)
+{
+ owner->allow_replacement =
+ (flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT) != FALSE;
+
+ owner->do_not_queue =
+ (flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) != FALSE;
+}
+
+static BusOwner *
+bus_owner_new (BusService *service,
+ DBusConnection *conn,
+ dbus_uint32_t flags)
+{
+ BusOwner *result;
+
+ result = _dbus_mem_pool_alloc (service->registry->owner_pool);
+ if (result != NULL)
+ {
+ result->refcount = 1;
+ /* don't ref the connection because we don't want
+ to block the connection from going away.
+ transactions take care of reffing the connection
+ but we need to use refcounting on the owner
+ so that the owner does not get freed before
+ we can deref the connection in the transaction
+ */
+ result->conn = conn;
+ result->service = service;
+
+ if (!bus_connection_add_owned_service (conn, service))
+ {
+ _dbus_mem_pool_dealloc (service->registry->owner_pool, result);
+ return NULL;
+ }
+
+ bus_owner_set_flags (result, flags);
+ }
+ return result;
+}
+
+static BusOwner *
+bus_owner_ref (BusOwner *owner)
+{
+ _dbus_assert (owner->refcount > 0);
+ owner->refcount += 1;
+
+ return owner;
+}
+
+static void
+bus_owner_unref (BusOwner *owner)
+{
+ _dbus_assert (owner->refcount > 0);
+ owner->refcount -= 1;
+
+ if (owner->refcount == 0)
+ {
+ bus_connection_remove_owned_service (owner->conn, owner->service);
+ _dbus_mem_pool_dealloc (owner->service->registry->owner_pool, owner);
+ }
+}
+
+BusService*
+bus_registry_ensure (BusRegistry *registry,
+ const DBusString *service_name,
+ DBusConnection *owner_connection_if_created,
+ dbus_uint32_t flags,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ BusService *service;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ _dbus_assert (owner_connection_if_created != NULL);
+ _dbus_assert (transaction != NULL);
+
+ service = _dbus_hash_table_lookup_string (registry->service_hash,
+ _dbus_string_get_const_data (service_name));
+ if (service != NULL)
+ return service;
+
+ service = _dbus_mem_pool_alloc (registry->service_pool);
+ if (service == NULL)
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ service->registry = registry;
+ service->refcount = 1;
+
+ _dbus_verbose ("copying string %p '%s' to service->name\n",
+ service_name, _dbus_string_get_const_data (service_name));
+ if (!_dbus_string_copy_data (service_name, &service->name))
+ {
+ _dbus_mem_pool_dealloc (registry->service_pool, service);
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+ _dbus_verbose ("copied string %p '%s' to '%s'\n",
+ service_name, _dbus_string_get_const_data (service_name),
+ service->name);
+
+ if (!bus_driver_send_service_owner_changed (service->name,
+ NULL,
+ bus_connection_get_name (owner_connection_if_created),
+ transaction, error))
+ {
+ bus_service_unref (service);
+ return NULL;
+ }
+
+ if (!bus_activation_service_created (bus_context_get_activation (registry->context),
+ service->name, transaction, error))
+ {
+ bus_service_unref (service);
+ return NULL;
+ }
+
+ if (!bus_service_add_owner (service, owner_connection_if_created, flags,
+ transaction, error))
+ {
+ bus_service_unref (service);
+ return NULL;
+ }
+
+ if (!_dbus_hash_table_insert_string (registry->service_hash,
+ service->name,
+ service))
+ {
+ /* The add_owner gets reverted on transaction cancel */
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ return service;
+}
+
+void
+bus_registry_foreach (BusRegistry *registry,
+ BusServiceForeachFunction function,
+ void *data)
+{
+ DBusHashIter iter;
+
+ _dbus_hash_iter_init (registry->service_hash, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusService *service = _dbus_hash_iter_get_value (&iter);
+
+ (* function) (service, data);
+ }
+}
+
+dbus_bool_t
+bus_registry_list_services (BusRegistry *registry,
+ char ***listp,
+ int *array_len)
+{
+ int i, j, len;
+ char **retval;
+ DBusHashIter iter;
+
+ len = _dbus_hash_table_get_n_entries (registry->service_hash);
+ retval = dbus_new (char *, len + 1);
+
+ if (retval == NULL)
+ return FALSE;
+
+ _dbus_hash_iter_init (registry->service_hash, &iter);
+ i = 0;
+ while (_dbus_hash_iter_next (&iter))
+ {
+ BusService *service = _dbus_hash_iter_get_value (&iter);
+
+ retval[i] = _dbus_strdup (service->name);
+ if (retval[i] == NULL)
+ goto error;
+
+ i++;
+ }
+
+ retval[i] = NULL;
+
+ if (array_len)
+ *array_len = len;
+
+ *listp = retval;
+ return TRUE;
+
+ error:
+ for (j = 0; j < i; j++)
+ dbus_free (retval[i]);
+ dbus_free (retval);
+
+ return FALSE;
+}
+
+dbus_bool_t
+bus_registry_acquire_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ dbus_bool_t retval;
+ DBusConnection *old_owner_conn;
+ DBusConnection *current_owner_conn;
+ BusClientPolicy *policy;
+ BusService *service;
+ BusActivation *activation;
+ BusSELinuxID *sid;
+ BusOwner *primary_owner;
+
+ retval = FALSE;
+
+ if (!_dbus_validate_bus_name (service_name, 0,
+ _dbus_string_get_length (service_name)))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Requested bus name \"%s\" is not valid",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to acquire invalid service name\n");
+
+ goto out;
+ }
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
+ {
+ /* Not allowed; only base services can start with ':' */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot acquire a service starting with ':' such as \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ goto out;
+ }
+
+ if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Connection \"%s\" is not allowed to own the service \"%s\"because "
+ "it is reserved for D-Bus' use only",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)",
+ DBUS_SERVICE_DBUS);
+ goto out;
+ }
+
+ policy = bus_connection_get_policy (connection);
+ _dbus_assert (policy != NULL);
+
+ /* Note that if sid is #NULL then the bus's own context gets used
+ * in bus_connection_selinux_allows_acquire_service()
+ */
+ sid = bus_selinux_id_table_lookup (registry->service_sid_table,
+ service_name);
+
+ if (!bus_selinux_allows_acquire_service (connection, sid,
+ _dbus_string_get_const_data (service_name), error))
+ {
+
+ if (dbus_error_is_set (error) &&
+ dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
+ {
+ goto out;
+ }
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service \"%s\" due "
+ "to SELinux policy",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)",
+ _dbus_string_get_const_data (service_name));
+ goto out;
+ }
+
+ if (!bus_client_policy_check_can_own (policy, connection,
+ service_name))
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service \"%s\" due "
+ "to security policies in the configuration file",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)",
+ _dbus_string_get_const_data (service_name));
+ goto out;
+ }
+
+ if (bus_connection_get_n_services_owned (connection) >=
+ bus_context_get_max_services_per_connection (registry->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Connection \"%s\" is not allowed to own more services "
+ "(increase limits in configuration file if required)",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) :
+ "(inactive)");
+ goto out;
+ }
+
+ service = bus_registry_lookup (registry, service_name);
+
+ if (service != NULL)
+ {
+ primary_owner = bus_service_get_primary_owner (service);
+ if (primary_owner != NULL)
+ old_owner_conn = primary_owner->conn;
+ else
+ old_owner_conn = NULL;
+ }
+ else
+ old_owner_conn = NULL;
+
+ if (service == NULL)
+ {
+ service = bus_registry_ensure (registry,
+ service_name, connection, flags,
+ transaction, error);
+ if (service == NULL)
+ goto out;
+ }
+
+ primary_owner = bus_service_get_primary_owner (service);
+ if (primary_owner == NULL)
+ goto out;
+
+ current_owner_conn = primary_owner->conn;
+
+ if (old_owner_conn == NULL)
+ {
+ _dbus_assert (current_owner_conn == connection);
+
+ *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+ }
+ else if (old_owner_conn == connection)
+ {
+ bus_owner_set_flags (primary_owner, flags);
+ *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
+ }
+ else if (((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+ !(bus_service_get_allow_replacement (service))) ||
+ ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+ !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING)))
+ {
+ DBusList *link;
+ BusOwner *temp_owner;
+ /* Since we can't be queued if we are already in the queue
+ remove us */
+
+ link = _bus_service_find_owner_link (service, connection);
+ if (link != NULL)
+ {
+ _dbus_list_unlink (&service->owners, link);
+ temp_owner = (BusOwner *)link->data;
+ bus_owner_unref (temp_owner);
+ _dbus_list_free_link (link);
+ }
+
+ *result = DBUS_REQUEST_NAME_REPLY_EXISTS;
+ }
+ else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
+ (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
+ !(bus_service_get_allow_replacement (service))))
+ {
+ /* Queue the connection */
+ if (!bus_service_add_owner (service, connection,
+ flags,
+ transaction, error))
+ goto out;
+
+ *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+ }
+ else
+ {
+ /* Replace the current owner */
+
+ /* We enqueue the new owner and remove the first one because
+ * that will cause NameAcquired and NameLost messages to
+ * be sent.
+ */
+
+ if (!bus_service_add_owner (service, connection,
+ flags,
+ transaction, error))
+ goto out;
+
+ if (primary_owner->do_not_queue)
+ {
+ if (!bus_service_remove_owner (service, old_owner_conn,
+ transaction, error))
+ goto out;
+ }
+ else
+ {
+ if (!bus_service_swap_owner (service, old_owner_conn,
+ transaction, error))
+ goto out;
+ }
+
+
+ _dbus_assert (connection == bus_service_get_primary_owner (service)->conn);
+ *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
+ }
+
+ activation = bus_context_get_activation (registry->context);
+ retval = bus_activation_send_pending_auto_activation_messages (activation,
+ service,
+ transaction,
+ error);
+
+ out:
+ return retval;
+}
+
+dbus_bool_t
+bus_registry_release_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ dbus_bool_t retval;
+ BusService *service;
+
+ retval = FALSE;
+
+ if (!_dbus_validate_bus_name (service_name, 0,
+ _dbus_string_get_length (service_name)))
+ {
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Given bus name \"%s\" is not valid",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to release invalid service name\n");
+
+ goto out;
+ }
+
+ if (_dbus_string_get_byte (service_name, 0) == ':')
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release a service starting with ':' such as \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ _dbus_verbose ("Attempt to release invalid base service name \"%s\"",
+ _dbus_string_get_const_data (service_name));
+
+ goto out;
+ }
+
+ if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
+ {
+ /* Not allowed; the base service name cannot be created or released */
+ dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+ "Cannot release the %s service because it is owned by the bus",
+ DBUS_SERVICE_DBUS);
+
+ _dbus_verbose ("Attempt to release service name \"%s\"",
+ DBUS_SERVICE_DBUS);
+
+ goto out;
+ }
+
+ service = bus_registry_lookup (registry, service_name);
+
+ if (service == NULL)
+ {
+ *result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
+ }
+ else if (!bus_service_has_owner (service, connection))
+ {
+ *result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
+ }
+ else
+ {
+ if (!bus_service_remove_owner (service, connection,
+ transaction, error))
+ goto out;
+
+ _dbus_assert (!bus_service_has_owner (service, connection));
+ *result = DBUS_RELEASE_NAME_REPLY_RELEASED;
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+dbus_bool_t
+bus_registry_set_service_context_table (BusRegistry *registry,
+ DBusHashTable *table)
+{
+ DBusHashTable *new_table;
+ DBusHashIter iter;
+
+ new_table = bus_selinux_id_table_new ();
+ if (!new_table)
+ return FALSE;
+
+ _dbus_hash_iter_init (table, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ const char *service = _dbus_hash_iter_get_string_key (&iter);
+ const char *context = _dbus_hash_iter_get_value (&iter);
+
+ if (!bus_selinux_id_table_insert (new_table,
+ service,
+ context))
+ return FALSE;
+ }
+
+ if (registry->service_sid_table)
+ _dbus_hash_table_unref (registry->service_sid_table);
+ registry->service_sid_table = new_table;
+ return TRUE;
+}
+
+static void
+bus_service_unlink_owner (BusService *service,
+ BusOwner *owner)
+{
+ _dbus_list_remove_last (&service->owners, owner);
+ bus_owner_unref (owner);
+}
+
+static void
+bus_service_unlink (BusService *service)
+{
+ _dbus_assert (service->owners == NULL);
+
+ /* the service may not be in the hash, if
+ * the failure causing transaction cancel
+ * was in the right place, but that's OK
+ */
+ _dbus_hash_table_remove_string (service->registry->service_hash,
+ service->name);
+
+ bus_service_unref (service);
+}
+
+static void
+bus_service_relink (BusService *service,
+ DBusPreallocatedHash *preallocated)
+{
+ _dbus_assert (service->owners == NULL);
+ _dbus_assert (preallocated != NULL);
+
+ _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
+ preallocated,
+ service->name,
+ service);
+
+ bus_service_ref (service);
+}
+
+/**
+ * Data used to represent an ownership cancellation in
+ * a bus transaction.
+ */
+typedef struct
+{
+ BusOwner *owner; /**< the owner */
+ BusService *service; /**< service to cancel ownership of */
+} OwnershipCancelData;
+
+static void
+cancel_ownership (void *data)
+{
+ OwnershipCancelData *d = data;
+
+ /* We don't need to send messages notifying of these
+ * changes, since we're reverting something that was
+ * cancelled (effectively never really happened)
+ */
+ bus_service_unlink_owner (d->service, d->owner);
+
+ if (d->service->owners == NULL)
+ bus_service_unlink (d->service);
+}
+
+static void
+free_ownership_cancel_data (void *data)
+{
+ OwnershipCancelData *d = data;
+
+ dbus_connection_unref (d->owner->conn);
+ bus_owner_unref (d->owner);
+ bus_service_unref (d->service);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_cancel_ownership_to_transaction (BusTransaction *transaction,
+ BusService *service,
+ BusOwner *owner)
+{
+ OwnershipCancelData *d;
+
+ d = dbus_new (OwnershipCancelData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->service = service;
+ d->owner = owner;
+
+ if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
+ free_ownership_cancel_data))
+ {
+ dbus_free (d);
+ return FALSE;
+ }
+
+ bus_service_ref (d->service);
+ bus_owner_ref (owner);
+ dbus_connection_ref (d->owner->conn);
+
+ return TRUE;
+}
+
+/* this function is self-cancelling if you cancel the transaction */
+dbus_bool_t
+bus_service_add_owner (BusService *service,
+ DBusConnection *connection,
+ dbus_uint32_t flags,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ BusOwner *bus_owner;
+ DBusList *bus_owner_link;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* Send service acquired message first, OOM will result
+ * in cancelling the transaction
+ */
+ if (service->owners == NULL)
+ {
+ if (!bus_driver_send_service_acquired (connection, service->name, transaction, error))
+ return FALSE;
+ }
+
+ bus_owner_link = _bus_service_find_owner_link (service, connection);
+
+ if (bus_owner_link == NULL)
+ {
+ bus_owner = bus_owner_new (service, connection, flags);
+ if (bus_owner == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ bus_owner_set_flags (bus_owner, flags);
+ if (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) || service->owners == NULL)
+ {
+ if (!_dbus_list_append (&service->owners,
+ bus_owner))
+ {
+ bus_owner_unref (bus_owner);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!_dbus_list_insert_after (&service->owners,
+ _dbus_list_get_first_link (&service->owners),
+ bus_owner))
+ {
+ bus_owner_unref (bus_owner);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ /* Update the link since we are already in the queue
+ * No need for operations that can produce OOM
+ */
+
+ bus_owner = (BusOwner *) bus_owner_link->data;
+ if (flags & DBUS_NAME_FLAG_REPLACE_EXISTING)
+ {
+ DBusList *link;
+ _dbus_list_unlink (&service->owners, bus_owner_link);
+ link = _dbus_list_get_first_link (&service->owners);
+ _dbus_assert (link != NULL);
+
+ _dbus_list_insert_after_link (&service->owners, link, bus_owner_link);
+ }
+
+ bus_owner_set_flags (bus_owner, flags);
+ return TRUE;
+ }
+
+ if (!add_cancel_ownership_to_transaction (transaction,
+ service,
+ bus_owner))
+ {
+ bus_service_unlink_owner (service, bus_owner);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct
+{
+ BusOwner *owner;
+ BusService *service;
+ BusOwner *before_owner; /* restore to position before this connection in owners list */
+ DBusList *owner_link;
+ DBusList *service_link;
+ DBusPreallocatedHash *hash_entry;
+} OwnershipRestoreData;
+
+static void
+restore_ownership (void *data)
+{
+ OwnershipRestoreData *d = data;
+ DBusList *link;
+
+ _dbus_assert (d->service_link != NULL);
+ _dbus_assert (d->owner_link != NULL);
+
+ if (d->service->owners == NULL)
+ {
+ _dbus_assert (d->hash_entry != NULL);
+ bus_service_relink (d->service, d->hash_entry);
+ }
+ else
+ {
+ _dbus_assert (d->hash_entry == NULL);
+ }
+
+ /* We don't need to send messages notifying of these
+ * changes, since we're reverting something that was
+ * cancelled (effectively never really happened)
+ */
+ link = _dbus_list_get_first_link (&d->service->owners);
+ while (link != NULL)
+ {
+ if (link->data == d->before_owner)
+ break;
+
+ link = _dbus_list_get_next_link (&d->service->owners, link);
+ }
+
+ _dbus_list_insert_before_link (&d->service->owners, link, d->owner_link);
+
+ /* Note that removing then restoring this changes the order in which
+ * ServiceDeleted messages are sent on destruction of the
+ * connection. This should be OK as the only guarantee there is
+ * that the base service is destroyed last, and we never even
+ * tentatively remove the base service.
+ */
+ bus_connection_add_owned_service_link (d->owner->conn, d->service_link);
+
+ d->hash_entry = NULL;
+ d->service_link = NULL;
+ d->owner_link = NULL;
+}
+
+static void
+free_ownership_restore_data (void *data)
+{
+ OwnershipRestoreData *d = data;
+
+ if (d->service_link)
+ _dbus_list_free_link (d->service_link);
+ if (d->owner_link)
+ _dbus_list_free_link (d->owner_link);
+ if (d->hash_entry)
+ _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
+ d->hash_entry);
+
+ dbus_connection_unref (d->owner->conn);
+ bus_owner_unref (d->owner);
+ bus_service_unref (d->service);
+
+ dbus_free (d);
+}
+
+static dbus_bool_t
+add_restore_ownership_to_transaction (BusTransaction *transaction,
+ BusService *service,
+ BusOwner *owner)
+{
+ OwnershipRestoreData *d;
+ DBusList *link;
+
+ d = dbus_new (OwnershipRestoreData, 1);
+ if (d == NULL)
+ return FALSE;
+
+ d->service = service;
+ d->owner = owner;
+ d->service_link = _dbus_list_alloc_link (service);
+ d->owner_link = _dbus_list_alloc_link (owner);
+ d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
+
+ bus_service_ref (d->service);
+ bus_owner_ref (d->owner);
+ dbus_connection_ref (d->owner->conn);
+
+ d->before_owner = NULL;
+ link = _dbus_list_get_first_link (&service->owners);
+ while (link != NULL)
+ {
+ if (link->data == owner)
+ {
+ link = _dbus_list_get_next_link (&service->owners, link);
+
+ if (link)
+ d->before_owner = link->data;
+
+ break;
+ }
+
+ link = _dbus_list_get_next_link (&service->owners, link);
+ }
+
+ if (d->service_link == NULL ||
+ d->owner_link == NULL ||
+ d->hash_entry == NULL ||
+ !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
+ free_ownership_restore_data))
+ {
+ free_ownership_restore_data (d);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_service_swap_owner (BusService *service,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ DBusList *swap_link;
+ BusOwner *primary_owner;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* We send out notifications before we do any work we
+ * might have to undo if the notification-sending failed
+ */
+
+ /* Send service lost message */
+ primary_owner = bus_service_get_primary_owner (service);
+ if (primary_owner == NULL || primary_owner->conn != connection)
+ _dbus_assert_not_reached ("Tried to swap a non primary owner");
+
+
+ if (!bus_driver_send_service_lost (connection, service->name,
+ transaction, error))
+ return FALSE;
+
+ if (service->owners == NULL)
+ {
+ _dbus_assert_not_reached ("Tried to swap owner of a service that has no owners");
+ }
+ else if (_dbus_list_length_is_one (&service->owners))
+ {
+ _dbus_assert_not_reached ("Tried to swap owner of a service that has no other owners in the queue");
+ }
+ else
+ {
+ DBusList *link;
+ BusOwner *new_owner;
+ DBusConnection *new_owner_conn;
+ link = _dbus_list_get_first_link (&service->owners);
+ _dbus_assert (link != NULL);
+ link = _dbus_list_get_next_link (&service->owners, link);
+ _dbus_assert (link != NULL);
+
+ new_owner = (BusOwner *)link->data;
+ new_owner_conn = new_owner->conn;
+
+ if (!bus_driver_send_service_owner_changed (service->name,
+ bus_connection_get_name (connection),
+ bus_connection_get_name (new_owner_conn),
+ transaction, error))
+ return FALSE;
+
+ /* This will be our new owner */
+ if (!bus_driver_send_service_acquired (new_owner_conn,
+ service->name,
+ transaction,
+ error))
+ return FALSE;
+ }
+
+ if (!add_restore_ownership_to_transaction (transaction, service, primary_owner))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* unlink the primary and make it the second link */
+ swap_link = _dbus_list_get_first_link (&service->owners);
+ _dbus_list_unlink (&service->owners, swap_link);
+
+ _dbus_list_insert_after_link (&service->owners,
+ _dbus_list_get_first_link (&service->owners),
+ swap_link);
+
+ return TRUE;
+}
+
+/* this function is self-cancelling if you cancel the transaction */
+dbus_bool_t
+bus_service_remove_owner (BusService *service,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusError *error)
+{
+ BusOwner *primary_owner;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* We send out notifications before we do any work we
+ * might have to undo if the notification-sending failed
+ */
+
+ /* Send service lost message */
+ primary_owner = bus_service_get_primary_owner (service);
+ if (primary_owner != NULL && primary_owner->conn == connection)
+ {
+ if (!bus_driver_send_service_lost (connection, service->name,
+ transaction, error))
+ return FALSE;
+ }
+ else
+ {
+ /* if we are not the primary owner then just remove us from the queue */
+ DBusList *link;
+ BusOwner *temp_owner;
+
+ link = _bus_service_find_owner_link (service, connection);
+ _dbus_list_unlink (&service->owners, link);
+ temp_owner = (BusOwner *)link->data;
+ bus_owner_unref (temp_owner);
+ _dbus_list_free_link (link);
+
+ return TRUE;
+ }
+
+ if (service->owners == NULL)
+ {
+ _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
+ }
+ else if (_dbus_list_length_is_one (&service->owners))
+ {
+ if (!bus_driver_send_service_owner_changed (service->name,
+ bus_connection_get_name (connection),
+ NULL,
+ transaction, error))
+ return FALSE;
+ }
+ else
+ {
+ DBusList *link;
+ BusOwner *new_owner;
+ DBusConnection *new_owner_conn;
+ link = _dbus_list_get_first_link (&service->owners);
+ _dbus_assert (link != NULL);
+ link = _dbus_list_get_next_link (&service->owners, link);
+ _dbus_assert (link != NULL);
+
+ new_owner = (BusOwner *)link->data;
+ new_owner_conn = new_owner->conn;
+
+ if (!bus_driver_send_service_owner_changed (service->name,
+ bus_connection_get_name (connection),
+ bus_connection_get_name (new_owner_conn),
+ transaction, error))
+ return FALSE;
+
+ /* This will be our new owner */
+ if (!bus_driver_send_service_acquired (new_owner_conn,
+ service->name,
+ transaction,
+ error))
+ return FALSE;
+ }
+
+ if (!add_restore_ownership_to_transaction (transaction, service, primary_owner))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ bus_service_unlink_owner (service, primary_owner);
+
+ if (service->owners == NULL)
+ bus_service_unlink (service);
+
+ return TRUE;
+}
+
+BusService *
+bus_service_ref (BusService *service)
+{
+ _dbus_assert (service->refcount > 0);
+
+ service->refcount += 1;
+
+ return service;
+}
+
+void
+bus_service_unref (BusService *service)
+{
+ _dbus_assert (service->refcount > 0);
+
+ service->refcount -= 1;
+
+ if (service->refcount == 0)
+ {
+ _dbus_assert (service->owners == NULL);
+
+ dbus_free (service->name);
+ _dbus_mem_pool_dealloc (service->registry->service_pool, service);
+ }
+}
+
+DBusConnection *
+bus_service_get_primary_owners_connection (BusService *service)
+{
+ BusOwner *owner;
+
+ owner = bus_service_get_primary_owner (service);
+
+ if (owner != NULL)
+ return owner->conn;
+ else
+ return NULL;
+}
+
+BusOwner*
+bus_service_get_primary_owner (BusService *service)
+{
+ return _dbus_list_get_first (&service->owners);
+}
+
+const char*
+bus_service_get_name (BusService *service)
+{
+ return service->name;
+}
+
+dbus_bool_t
+bus_service_get_allow_replacement (BusService *service)
+{
+ BusOwner *owner;
+ DBusList *link;
+
+ _dbus_assert (service->owners != NULL);
+
+ link = _dbus_list_get_first_link (&service->owners);
+ owner = (BusOwner *) link->data;
+
+ return owner->allow_replacement;
+}
+
+dbus_bool_t
+bus_service_has_owner (BusService *service,
+ DBusConnection *connection)
+{
+ DBusList *link;
+
+ link = _bus_service_find_owner_link (service, connection);
+
+ if (link == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+dbus_bool_t
+bus_service_list_queued_owners (BusService *service,
+ DBusList **return_list,
+ DBusError *error)
+{
+ DBusList *link;
+
+ _dbus_assert (*return_list == NULL);
+
+ link = _dbus_list_get_first_link (&service->owners);
+ _dbus_assert (link != NULL);
+
+ while (link != NULL)
+ {
+ BusOwner *owner;
+ const char *uname;
+
+ owner = (BusOwner *) link->data;
+ uname = bus_connection_get_name (owner->conn);
+
+ if (!_dbus_list_append (return_list, (char *)uname))
+ goto oom;
+
+ link = _dbus_list_get_next_link (&service->owners, link);
+ }
+
+ return TRUE;
+
+ oom:
+ _dbus_list_clear (return_list);
+ BUS_SET_OOM (error);
+ return FALSE;
+}
diff --git a/bus/services.h b/bus/services.h
new file mode 100644
index 00000000..056dd9fa
--- /dev/null
+++ b/bus/services.h
@@ -0,0 +1,94 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* services.h Service management
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_SERVICES_H
+#define BUS_SERVICES_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-hash.h>
+#include "connection.h"
+#include "bus.h"
+
+typedef void (* BusServiceForeachFunction) (BusService *service,
+ void *data);
+
+BusRegistry* bus_registry_new (BusContext *context);
+BusRegistry* bus_registry_ref (BusRegistry *registry);
+void bus_registry_unref (BusRegistry *registry);
+BusService* bus_registry_lookup (BusRegistry *registry,
+ const DBusString *service_name);
+BusService* bus_registry_ensure (BusRegistry *registry,
+ const DBusString *service_name,
+ DBusConnection *owner_connection_if_created,
+ dbus_uint32_t flags,
+ BusTransaction *transaction,
+ DBusError *error);
+void bus_registry_foreach (BusRegistry *registry,
+ BusServiceForeachFunction function,
+ void *data);
+dbus_bool_t bus_registry_list_services (BusRegistry *registry,
+ char ***listp,
+ int *array_len);
+dbus_bool_t bus_registry_acquire_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t flags,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_registry_release_service (BusRegistry *registry,
+ DBusConnection *connection,
+ const DBusString *service_name,
+ dbus_uint32_t *result,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_registry_set_service_context_table (BusRegistry *registry,
+ DBusHashTable *table);
+
+BusService* bus_service_ref (BusService *service);
+void bus_service_unref (BusService *service);
+dbus_bool_t bus_service_add_owner (BusService *service,
+ DBusConnection *connection,
+ dbus_uint32_t flags,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_service_swap_owner (BusService *service,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_service_remove_owner (BusService *service,
+ DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusError *error);
+dbus_bool_t bus_service_has_owner (BusService *service,
+ DBusConnection *connection);
+BusOwner* bus_service_get_primary_owner (BusService *service);
+dbus_bool_t bus_service_get_allow_replacement (BusService *service);
+const char* bus_service_get_name (BusService *service);
+dbus_bool_t bus_service_list_queued_owners (BusService *service,
+ DBusList **return_list,
+ DBusError *error);
+
+DBusConnection* bus_service_get_primary_owners_connection (BusService *service);
+#endif /* BUS_SERVICES_H */
diff --git a/bus/session.conf.in b/bus/session.conf.in
new file mode 100644
index 00000000..e7229ad5
--- /dev/null
+++ b/bus/session.conf.in
@@ -0,0 +1,60 @@
+<!-- This configuration file controls the per-user-login-session message bus.
+ Add a session-local.conf and edit that rather than changing this
+ file directly. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <!-- Our well-known bus type, don't change this -->
+ <type>session</type>
+
+ <!-- If we fork, keep the user's original umask to avoid affecting
+ the behavior of child processes. -->
+ <keep_umask/>
+
+ <listen>unix:tmpdir=@DBUS_SESSION_SOCKET_DIR@</listen>
+
+ <standard_session_servicedirs />
+
+ <policy context="default">
+ <!-- Allow everything to be sent -->
+ <allow send_destination="*" eavesdrop="true"/>
+ <!-- Allow everything to be received -->
+ <allow eavesdrop="true"/>
+ <!-- Allow anyone to own anything -->
+ <allow own="*"/>
+ </policy>
+
+ <!-- Config files are placed here that among other things,
+ further restrict the above policy for specific services. -->
+ <includedir>session.d</includedir>
+
+ <!-- This is included last so local configuration can override what's
+ in this standard file -->
+ <include ignore_missing="yes">session-local.conf</include>
+
+ <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include>
+
+ <!-- For the session bus, override the default relatively-low limits
+ with essentially infinite limits, since the bus is just running
+ as the user anyway, using up bus resources is not something we need
+ to worry about. In some cases, we do set the limits lower than
+ "all available memory" if exceeding the limit is almost certainly a bug,
+ having the bus enforce a limit is nicer than a huge memory leak. But the
+ intent is that these limits should never be hit. -->
+
+ <!-- the memory limits are 1G instead of say 4G because they can't exceed 32-bit signed int max -->
+ <limit name="max_incoming_bytes">1000000000</limit>
+ <limit name="max_outgoing_bytes">1000000000</limit>
+ <limit name="max_message_size">1000000000</limit>
+ <limit name="service_start_timeout">120000</limit>
+ <limit name="auth_timeout">240000</limit>
+ <limit name="max_completed_connections">100000</limit>
+ <limit name="max_incomplete_connections">10000</limit>
+ <limit name="max_connections_per_user">100000</limit>
+ <limit name="max_pending_service_starts">10000</limit>
+ <limit name="max_names_per_connection">50000</limit>
+ <limit name="max_match_rules_per_connection">50000</limit>
+ <limit name="max_replies_per_connection">50000</limit>
+
+</busconfig>
diff --git a/bus/signals.c b/bus/signals.c
new file mode 100644
index 00000000..b020a76c
--- /dev/null
+++ b/bus/signals.c
@@ -0,0 +1,2030 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* signals.c Bus signal connection implementation
+ *
+ * Copyright (C) 2003, 2005 Red Hat, Inc.
+ *
+ * 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 "signals.h"
+#include "services.h"
+#include "utils.h"
+#include <dbus/dbus-marshal-validate.h>
+
+struct BusMatchRule
+{
+ int refcount; /**< reference count */
+
+ DBusConnection *matches_go_to; /**< Owner of the rule */
+
+ unsigned int flags; /**< BusMatchFlags */
+
+ int message_type;
+ char *interface;
+ char *member;
+ char *sender;
+ char *destination;
+ char *path;
+
+ unsigned int *arg_lens;
+ char **args;
+ int args_len;
+};
+
+#define BUS_MATCH_ARG_IS_PATH 0x8000000u
+
+BusMatchRule*
+bus_match_rule_new (DBusConnection *matches_go_to)
+{
+ BusMatchRule *rule;
+
+ rule = dbus_new0 (BusMatchRule, 1);
+ if (rule == NULL)
+ return NULL;
+
+ rule->refcount = 1;
+ rule->matches_go_to = matches_go_to;
+
+#ifndef DBUS_BUILD_TESTS
+ _dbus_assert (rule->matches_go_to != NULL);
+#endif
+
+ return rule;
+}
+
+BusMatchRule *
+bus_match_rule_ref (BusMatchRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount += 1;
+
+ return rule;
+}
+
+void
+bus_match_rule_unref (BusMatchRule *rule)
+{
+ _dbus_assert (rule->refcount > 0);
+
+ rule->refcount -= 1;
+ if (rule->refcount == 0)
+ {
+ dbus_free (rule->interface);
+ dbus_free (rule->member);
+ dbus_free (rule->sender);
+ dbus_free (rule->destination);
+ dbus_free (rule->path);
+ dbus_free (rule->arg_lens);
+
+ /* can't use dbus_free_string_array() since there
+ * are embedded NULL
+ */
+ if (rule->args)
+ {
+ int i;
+
+ i = 0;
+ while (i < rule->args_len)
+ {
+ if (rule->args[i])
+ dbus_free (rule->args[i]);
+ ++i;
+ }
+
+ dbus_free (rule->args);
+ }
+
+ dbus_free (rule);
+ }
+}
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+/* Note this function does not do escaping, so it's only
+ * good for debug spew at the moment
+ */
+static char*
+match_rule_to_string (BusMatchRule *rule)
+{
+ DBusString str;
+ char *ret;
+
+ if (!_dbus_string_init (&str))
+ {
+ char *s;
+ while ((s = _dbus_strdup ("nomem")) == NULL)
+ ; /* only OK for debug spew... */
+ return s;
+ }
+
+ if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
+ {
+ /* FIXME make type readable */
+ if (!_dbus_string_append_printf (&str, "type='%d'", rule->message_type))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_INTERFACE)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "interface='%s'", rule->interface))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_MEMBER)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "member='%s'", rule->member))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_PATH)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "path='%s'", rule->path))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_SENDER)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "sender='%s'", rule->sender))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_DESTINATION)
+ {
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ if (!_dbus_string_append_printf (&str, "destination='%s'", rule->destination))
+ goto nomem;
+ }
+
+ if (rule->flags & BUS_MATCH_ARGS)
+ {
+ int i;
+
+ _dbus_assert (rule->args != NULL);
+
+ i = 0;
+ while (i < rule->args_len)
+ {
+ if (rule->args[i] != NULL)
+ {
+ dbus_bool_t is_path;
+
+ if (_dbus_string_get_length (&str) > 0)
+ {
+ if (!_dbus_string_append (&str, ","))
+ goto nomem;
+ }
+
+ is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+
+ if (!_dbus_string_append_printf (&str,
+ "arg%d%s='%s'",
+ i, is_path ? "path" : "",
+ rule->args[i]))
+ goto nomem;
+ }
+
+ ++i;
+ }
+ }
+
+ if (!_dbus_string_steal_data (&str, &ret))
+ goto nomem;
+
+ _dbus_string_free (&str);
+ return ret;
+
+ nomem:
+ _dbus_string_free (&str);
+ {
+ char *s;
+ while ((s = _dbus_strdup ("nomem")) == NULL)
+ ; /* only OK for debug spew... */
+ return s;
+ }
+}
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+dbus_bool_t
+bus_match_rule_set_message_type (BusMatchRule *rule,
+ int type)
+{
+ rule->flags |= BUS_MATCH_MESSAGE_TYPE;
+
+ rule->message_type = type;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_interface (BusMatchRule *rule,
+ const char *interface)
+{
+ char *new;
+
+ _dbus_assert (interface != NULL);
+
+ new = _dbus_strdup (interface);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_INTERFACE;
+ dbus_free (rule->interface);
+ rule->interface = new;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_member (BusMatchRule *rule,
+ const char *member)
+{
+ char *new;
+
+ _dbus_assert (member != NULL);
+
+ new = _dbus_strdup (member);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_MEMBER;
+ dbus_free (rule->member);
+ rule->member = new;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_sender (BusMatchRule *rule,
+ const char *sender)
+{
+ char *new;
+
+ _dbus_assert (sender != NULL);
+
+ new = _dbus_strdup (sender);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_SENDER;
+ dbus_free (rule->sender);
+ rule->sender = new;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_destination (BusMatchRule *rule,
+ const char *destination)
+{
+ char *new;
+
+ _dbus_assert (destination != NULL);
+
+ new = _dbus_strdup (destination);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_DESTINATION;
+ dbus_free (rule->destination);
+ rule->destination = new;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_path (BusMatchRule *rule,
+ const char *path)
+{
+ char *new;
+
+ _dbus_assert (path != NULL);
+
+ new = _dbus_strdup (path);
+ if (new == NULL)
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_PATH;
+ dbus_free (rule->path);
+ rule->path = new;
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_match_rule_set_arg (BusMatchRule *rule,
+ int arg,
+ const DBusString *value,
+ dbus_bool_t is_path)
+{
+ int length;
+ char *new;
+
+ _dbus_assert (value != NULL);
+
+ /* args_len is the number of args not including null termination
+ * in the char**
+ */
+ if (arg >= rule->args_len)
+ {
+ unsigned int *new_arg_lens;
+ char **new_args;
+ int new_args_len;
+ int i;
+
+ new_args_len = arg + 1;
+
+ /* add another + 1 here for null termination */
+ new_args = dbus_realloc (rule->args,
+ sizeof (char *) * (new_args_len + 1));
+ if (new_args == NULL)
+ return FALSE;
+
+ /* NULL the new slots */
+ i = rule->args_len;
+ while (i <= new_args_len) /* <= for null termination */
+ {
+ new_args[i] = NULL;
+ ++i;
+ }
+
+ rule->args = new_args;
+
+ /* and now add to the lengths */
+ new_arg_lens = dbus_realloc (rule->arg_lens,
+ sizeof (int) * (new_args_len + 1));
+
+ if (new_arg_lens == NULL)
+ return FALSE;
+
+ /* zero the new slots */
+ i = rule->args_len;
+ while (i <= new_args_len) /* <= for null termination */
+ {
+ new_arg_lens[i] = 0;
+ ++i;
+ }
+
+ rule->arg_lens = new_arg_lens;
+ rule->args_len = new_args_len;
+ }
+
+ length = _dbus_string_get_length (value);
+ if (!_dbus_string_copy_data (value, &new))
+ return FALSE;
+
+ rule->flags |= BUS_MATCH_ARGS;
+
+ dbus_free (rule->args[arg]);
+ rule->arg_lens[arg] = length;
+ rule->args[arg] = new;
+
+ if (is_path)
+ rule->arg_lens[arg] |= BUS_MATCH_ARG_IS_PATH;
+
+ /* NULL termination didn't get busted */
+ _dbus_assert (rule->args[rule->args_len] == NULL);
+ _dbus_assert (rule->arg_lens[rule->args_len] == 0);
+
+ return TRUE;
+}
+
+#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+static dbus_bool_t
+find_key (const DBusString *str,
+ int start,
+ DBusString *key,
+ int *value_pos,
+ DBusError *error)
+{
+ const char *p;
+ const char *s;
+ const char *key_start;
+ const char *key_end;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ s = _dbus_string_get_const_data (str);
+
+ p = s + start;
+
+ while (*p && ISWHITE (*p))
+ ++p;
+
+ key_start = p;
+
+ while (*p && *p != '=' && !ISWHITE (*p))
+ ++p;
+
+ key_end = p;
+
+ while (*p && ISWHITE (*p))
+ ++p;
+
+ if (key_start == key_end)
+ {
+ /* Empty match rules or trailing whitespace are OK */
+ *value_pos = p - s;
+ return TRUE;
+ }
+
+ if (*p != '=')
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Match rule has a key with no subsequent '=' character");
+ return FALSE;
+ }
+ ++p;
+
+ if (!_dbus_string_append_len (key, key_start, key_end - key_start))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ *value_pos = p - s;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+find_value (const DBusString *str,
+ int start,
+ const char *key,
+ DBusString *value,
+ int *value_end,
+ DBusError *error)
+{
+ const char *p;
+ const char *s;
+ char quote_char;
+ int orig_len;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ orig_len = _dbus_string_get_length (value);
+
+ s = _dbus_string_get_const_data (str);
+
+ p = s + start;
+
+ quote_char = '\0';
+
+ while (*p)
+ {
+ if (quote_char == '\0')
+ {
+ switch (*p)
+ {
+ case '\0':
+ goto done;
+
+ case '\'':
+ quote_char = '\'';
+ goto next;
+
+ case ',':
+ ++p;
+ goto done;
+
+ case '\\':
+ quote_char = '\\';
+ goto next;
+
+ default:
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ }
+ else if (quote_char == '\\')
+ {
+ /* \ only counts as an escape if escaping a quote mark */
+ if (*p != '\'')
+ {
+ if (!_dbus_string_append_byte (value, '\\'))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ quote_char = '\0';
+ }
+ else
+ {
+ _dbus_assert (quote_char == '\'');
+
+ if (*p == '\'')
+ {
+ quote_char = '\0';
+ }
+ else
+ {
+ if (!_dbus_string_append_byte (value, *p))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ }
+
+ next:
+ ++p;
+ }
+
+ done:
+
+ if (quote_char == '\\')
+ {
+ if (!_dbus_string_append_byte (value, '\\'))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (quote_char == '\'')
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Unbalanced quotation marks in match rule");
+ goto failed;
+ }
+ else
+ _dbus_assert (quote_char == '\0');
+
+ /* Zero-length values are allowed */
+
+ *value_end = p - s;
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ _dbus_string_set_length (value, orig_len);
+ return FALSE;
+}
+
+/* duplicates aren't allowed so the real legitimate max is only 6 or
+ * so. Leaving extra so we don't have to bother to update it.
+ * FIXME this is sort of busted now with arg matching, but we let
+ * you match on up to 10 args for now
+ */
+#define MAX_RULE_TOKENS 16
+
+/* this is slightly too high level to be termed a "token"
+ * but let's not be pedantic.
+ */
+typedef struct
+{
+ char *key;
+ char *value;
+} RuleToken;
+
+static dbus_bool_t
+tokenize_rule (const DBusString *rule_text,
+ RuleToken tokens[MAX_RULE_TOKENS],
+ DBusError *error)
+{
+ int i;
+ int pos;
+ DBusString key;
+ DBusString value;
+ dbus_bool_t retval;
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&key))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!_dbus_string_init (&value))
+ {
+ _dbus_string_free (&key);
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ i = 0;
+ pos = 0;
+ while (i < MAX_RULE_TOKENS &&
+ pos < _dbus_string_get_length (rule_text))
+ {
+ _dbus_assert (tokens[i].key == NULL);
+ _dbus_assert (tokens[i].value == NULL);
+
+ if (!find_key (rule_text, pos, &key, &pos, error))
+ goto out;
+
+ if (_dbus_string_get_length (&key) == 0)
+ goto next;
+
+ if (!_dbus_string_steal_data (&key, &tokens[i].key))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ if (!find_value (rule_text, pos, tokens[i].key, &value, &pos, error))
+ goto out;
+
+ if (!_dbus_string_steal_data (&value, &tokens[i].value))
+ {
+ BUS_SET_OOM (error);
+ goto out;
+ }
+
+ next:
+ ++i;
+ }
+
+ retval = TRUE;
+
+ out:
+ if (!retval)
+ {
+ i = 0;
+ while (tokens[i].key || tokens[i].value)
+ {
+ dbus_free (tokens[i].key);
+ dbus_free (tokens[i].value);
+ tokens[i].key = NULL;
+ tokens[i].value = NULL;
+ ++i;
+ }
+ }
+
+ _dbus_string_free (&key);
+ _dbus_string_free (&value);
+
+ return retval;
+}
+
+static dbus_bool_t
+bus_match_rule_parse_arg_match (BusMatchRule *rule,
+ const char *key,
+ const DBusString *value,
+ DBusError *error)
+{
+ dbus_bool_t is_path;
+ DBusString key_str;
+ unsigned long arg;
+ int length;
+ int end;
+
+ /* For now, arg0='foo' always implies that 'foo' is a
+ * DBUS_TYPE_STRING. Someday we could add an arg0type='int32' thing
+ * if we wanted, which would specify another type, in which case
+ * arg0='5' would have the 5 parsed as an int rather than string.
+ */
+
+ /* First we need to parse arg0 = 0, arg27 = 27 */
+
+ _dbus_string_init_const (&key_str, key);
+ length = _dbus_string_get_length (&key_str);
+
+ if (_dbus_string_get_length (&key_str) < 4)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule starts with 'arg' but lacks an arg number. Should be 'arg0' or 'arg7' for example.\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_string_parse_uint (&key_str, 3, &arg, &end))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule starts with 'arg' but could not parse arg number. Should be 'arg0' or 'arg7' for example.\n", key);
+ goto failed;
+ }
+
+ if (end != length &&
+ ((end + 4) != length ||
+ !_dbus_string_ends_with_c_str (&key_str, "path")))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule contains junk after argument number. Only 'path' is optionally valid ('arg0path' for example).\n", key);
+ goto failed;
+ }
+
+ is_path = end != length;
+
+ /* If we didn't check this we could allocate a huge amount of RAM */
+ if (arg > DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key '%s' in match rule has arg number %lu but the maximum is %d.\n", key, (unsigned long) arg, DBUS_MAXIMUM_MATCH_RULE_ARG_NUMBER);
+ goto failed;
+ }
+
+ if ((rule->flags & BUS_MATCH_ARGS) &&
+ rule->args_len > (int) arg &&
+ rule->args[arg] != NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Argument %d matched more than once in match rule\n", key);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_arg (rule, arg, value, is_path))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ return TRUE;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+}
+
+/*
+ * The format is comma-separated with strings quoted with single quotes
+ * as for the shell (to escape a literal single quote, use '\'').
+ *
+ * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',
+ * path='/bar/foo',destination=':452345.34'
+ *
+ */
+BusMatchRule*
+bus_match_rule_parse (DBusConnection *matches_go_to,
+ const DBusString *rule_text,
+ DBusError *error)
+{
+ BusMatchRule *rule;
+ RuleToken tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
+ int i;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (_dbus_string_get_length (rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH)
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Match rule text is %d bytes, maximum is %d",
+ _dbus_string_get_length (rule_text),
+ DBUS_MAXIMUM_MATCH_RULE_LENGTH);
+ return NULL;
+ }
+
+ memset (tokens, '\0', sizeof (tokens));
+
+ rule = bus_match_rule_new (matches_go_to);
+ if (rule == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!tokenize_rule (rule_text, tokens, error))
+ goto failed;
+
+ i = 0;
+ while (tokens[i].key != NULL)
+ {
+ DBusString tmp_str;
+ int len;
+ const char *key = tokens[i].key;
+ const char *value = tokens[i].value;
+
+ _dbus_string_init_const (&tmp_str, value);
+ len = _dbus_string_get_length (&tmp_str);
+
+ if (strcmp (key, "type") == 0)
+ {
+ int t;
+
+ if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ t = dbus_message_type_from_string (value);
+
+ if (t == DBUS_MESSAGE_TYPE_INVALID)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Invalid message type (%s) in match rule\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_message_type (rule, t))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "sender") == 0)
+ {
+ if (rule->flags & BUS_MATCH_SENDER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_bus_name (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Sender name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_sender (rule, value))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "interface") == 0)
+ {
+ if (rule->flags & BUS_MATCH_INTERFACE)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_interface (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Interface name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_interface (rule, value))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "member") == 0)
+ {
+ if (rule->flags & BUS_MATCH_MEMBER)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_member (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Member name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_member (rule, value))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "path") == 0)
+ {
+ if (rule->flags & BUS_MATCH_PATH)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_path (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Path '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_path (rule, value))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strcmp (key, "destination") == 0)
+ {
+ if (rule->flags & BUS_MATCH_DESTINATION)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Key %s specified twice in match rule\n", key);
+ goto failed;
+ }
+
+ if (!_dbus_validate_bus_name (&tmp_str, 0, len))
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Destination name '%s' is invalid\n", value);
+ goto failed;
+ }
+
+ if (!bus_match_rule_set_destination (rule, value))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
+ else if (strncmp (key, "arg", 3) == 0)
+ {
+ if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
+ goto failed;
+ }
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+ "Unknown key \"%s\" in match rule",
+ key);
+ goto failed;
+ }
+
+ ++i;
+ }
+
+
+ goto out;
+
+ failed:
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ if (rule)
+ {
+ bus_match_rule_unref (rule);
+ rule = NULL;
+ }
+
+ out:
+
+ i = 0;
+ while (tokens[i].key || tokens[i].value)
+ {
+ _dbus_assert (i < MAX_RULE_TOKENS);
+ dbus_free (tokens[i].key);
+ dbus_free (tokens[i].value);
+ ++i;
+ }
+
+ return rule;
+}
+
+struct BusMatchmaker
+{
+ int refcount;
+
+ DBusList *all_rules;
+};
+
+BusMatchmaker*
+bus_matchmaker_new (void)
+{
+ BusMatchmaker *matchmaker;
+
+ matchmaker = dbus_new0 (BusMatchmaker, 1);
+ if (matchmaker == NULL)
+ return NULL;
+
+ matchmaker->refcount = 1;
+
+ return matchmaker;
+}
+
+BusMatchmaker *
+bus_matchmaker_ref (BusMatchmaker *matchmaker)
+{
+ _dbus_assert (matchmaker->refcount > 0);
+
+ matchmaker->refcount += 1;
+
+ return matchmaker;
+}
+
+void
+bus_matchmaker_unref (BusMatchmaker *matchmaker)
+{
+ _dbus_assert (matchmaker->refcount > 0);
+
+ matchmaker->refcount -= 1;
+ if (matchmaker->refcount == 0)
+ {
+ while (matchmaker->all_rules != NULL)
+ {
+ BusMatchRule *rule;
+
+ rule = matchmaker->all_rules->data;
+ bus_match_rule_unref (rule);
+ _dbus_list_remove_link (&matchmaker->all_rules,
+ matchmaker->all_rules);
+ }
+
+ dbus_free (matchmaker);
+ }
+}
+
+/* The rule can't be modified after it's added. */
+dbus_bool_t
+bus_matchmaker_add_rule (BusMatchmaker *matchmaker,
+ BusMatchRule *rule)
+{
+ _dbus_assert (bus_connection_is_active (rule->matches_go_to));
+
+ if (!_dbus_list_append (&matchmaker->all_rules, rule))
+ return FALSE;
+
+ if (!bus_connection_add_match_rule (rule->matches_go_to, rule))
+ {
+ _dbus_list_remove_last (&matchmaker->all_rules, rule);
+ return FALSE;
+ }
+
+ bus_match_rule_ref (rule);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Added match rule %s to connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ return TRUE;
+}
+
+static dbus_bool_t
+match_rule_equal (BusMatchRule *a,
+ BusMatchRule *b)
+{
+ if (a->flags != b->flags)
+ return FALSE;
+
+ if (a->matches_go_to != b->matches_go_to)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
+ a->message_type != b->message_type)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_MEMBER) &&
+ strcmp (a->member, b->member) != 0)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_PATH) &&
+ strcmp (a->path, b->path) != 0)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_INTERFACE) &&
+ strcmp (a->interface, b->interface) != 0)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_SENDER) &&
+ strcmp (a->sender, b->sender) != 0)
+ return FALSE;
+
+ if ((a->flags & BUS_MATCH_DESTINATION) &&
+ strcmp (a->destination, b->destination) != 0)
+ return FALSE;
+
+ if (a->flags & BUS_MATCH_ARGS)
+ {
+ int i;
+
+ if (a->args_len != b->args_len)
+ return FALSE;
+
+ i = 0;
+ while (i < a->args_len)
+ {
+ int length;
+
+ if ((a->args[i] != NULL) != (b->args[i] != NULL))
+ return FALSE;
+
+ if (a->arg_lens[i] != b->arg_lens[i])
+ return FALSE;
+
+ length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+
+ if (a->args[i] != NULL)
+ {
+ _dbus_assert (b->args[i] != NULL);
+ if (memcmp (a->args[i], b->args[i], length) != 0)
+ return FALSE;
+ }
+
+ ++i;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+bus_matchmaker_remove_rule_link (BusMatchmaker *matchmaker,
+ DBusList *link)
+{
+ BusMatchRule *rule = link->data;
+
+ bus_connection_remove_match_rule (rule->matches_go_to, rule);
+ _dbus_list_remove_link (&matchmaker->all_rules, link);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Removed match rule %s for connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ bus_match_rule_unref (rule);
+}
+
+void
+bus_matchmaker_remove_rule (BusMatchmaker *matchmaker,
+ BusMatchRule *rule)
+{
+ bus_connection_remove_match_rule (rule->matches_go_to, rule);
+ _dbus_list_remove (&matchmaker->all_rules, rule);
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Removed match rule %s for connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ bus_match_rule_unref (rule);
+}
+
+/* Remove a single rule which is equal to the given rule by value */
+dbus_bool_t
+bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker,
+ BusMatchRule *value,
+ DBusError *error)
+{
+ /* FIXME this is an unoptimized linear scan */
+
+ DBusList *link;
+
+ /* we traverse backward because bus_connection_remove_match_rule()
+ * removes the most-recently-added rule
+ */
+ link = _dbus_list_get_last_link (&matchmaker->all_rules);
+ while (link != NULL)
+ {
+ BusMatchRule *rule;
+ DBusList *prev;
+
+ rule = link->data;
+ prev = _dbus_list_get_prev_link (&matchmaker->all_rules, link);
+
+ if (match_rule_equal (rule, value))
+ {
+ bus_matchmaker_remove_rule_link (matchmaker, link);
+ break;
+ }
+
+ link = prev;
+ }
+
+ if (link == NULL)
+ {
+ dbus_set_error (error, DBUS_ERROR_MATCH_RULE_NOT_FOUND,
+ "The given match rule wasn't found and can't be removed");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+bus_matchmaker_disconnected (BusMatchmaker *matchmaker,
+ DBusConnection *disconnected)
+{
+ DBusList *link;
+
+ /* FIXME
+ *
+ * This scans all match rules on the bus. We could avoid that
+ * for the rules belonging to the connection, since we keep
+ * a list of those; but for the rules that just refer to
+ * the connection we'd need to do something more elaborate.
+ *
+ */
+
+ _dbus_assert (bus_connection_is_active (disconnected));
+
+ link = _dbus_list_get_first_link (&matchmaker->all_rules);
+ while (link != NULL)
+ {
+ BusMatchRule *rule;
+ DBusList *next;
+
+ rule = link->data;
+ next = _dbus_list_get_next_link (&matchmaker->all_rules, link);
+
+ if (rule->matches_go_to == disconnected)
+ {
+ bus_matchmaker_remove_rule_link (matchmaker, link);
+ }
+ else if (((rule->flags & BUS_MATCH_SENDER) && *rule->sender == ':') ||
+ ((rule->flags & BUS_MATCH_DESTINATION) && *rule->destination == ':'))
+ {
+ /* The rule matches to/from a base service, see if it's the
+ * one being disconnected, since we know this service name
+ * will never be recycled.
+ */
+ const char *name;
+
+ name = bus_connection_get_name (disconnected);
+ _dbus_assert (name != NULL); /* because we're an active connection */
+
+ if (((rule->flags & BUS_MATCH_SENDER) &&
+ strcmp (rule->sender, name) == 0) ||
+ ((rule->flags & BUS_MATCH_DESTINATION) &&
+ strcmp (rule->destination, name) == 0))
+ {
+ bus_matchmaker_remove_rule_link (matchmaker, link);
+ }
+ }
+
+ link = next;
+ }
+}
+
+static dbus_bool_t
+connection_is_primary_owner (DBusConnection *connection,
+ const char *service_name)
+{
+ BusService *service;
+ DBusString str;
+ BusRegistry *registry;
+
+ _dbus_assert (connection != NULL);
+
+ registry = bus_connection_get_registry (connection);
+
+ _dbus_string_init_const (&str, service_name);
+ service = bus_registry_lookup (registry, &str);
+
+ if (service == NULL)
+ return FALSE; /* Service doesn't exist so connection can't own it. */
+
+ return bus_service_get_primary_owners_connection (service) == connection;
+}
+
+static dbus_bool_t
+match_rule_matches (BusMatchRule *rule,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message)
+{
+ /* All features of the match rule are AND'd together,
+ * so FALSE if any of them don't match.
+ */
+
+ /* sender/addressed_recipient of #NULL may mean bus driver,
+ * or for addressed_recipient may mean a message with no
+ * specific recipient (i.e. a signal)
+ */
+
+ if (rule->flags & BUS_MATCH_MESSAGE_TYPE)
+ {
+ _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
+
+ if (rule->message_type != dbus_message_get_type (message))
+ return FALSE;
+ }
+
+ if (rule->flags & BUS_MATCH_INTERFACE)
+ {
+ const char *iface;
+
+ _dbus_assert (rule->interface != NULL);
+
+ iface = dbus_message_get_interface (message);
+ if (iface == NULL)
+ return FALSE;
+
+ if (strcmp (iface, rule->interface) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & BUS_MATCH_MEMBER)
+ {
+ const char *member;
+
+ _dbus_assert (rule->member != NULL);
+
+ member = dbus_message_get_member (message);
+ if (member == NULL)
+ return FALSE;
+
+ if (strcmp (member, rule->member) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & BUS_MATCH_SENDER)
+ {
+ _dbus_assert (rule->sender != NULL);
+
+ if (sender == NULL)
+ {
+ if (strcmp (rule->sender,
+ DBUS_SERVICE_DBUS) != 0)
+ return FALSE;
+ }
+ else
+ {
+ if (!connection_is_primary_owner (sender, rule->sender))
+ return FALSE;
+ }
+ }
+
+ if (rule->flags & BUS_MATCH_DESTINATION)
+ {
+ const char *destination;
+
+ _dbus_assert (rule->destination != NULL);
+
+ destination = dbus_message_get_destination (message);
+ if (destination == NULL)
+ return FALSE;
+
+ if (addressed_recipient == NULL)
+ {
+ if (strcmp (rule->destination,
+ DBUS_SERVICE_DBUS) != 0)
+ return FALSE;
+ }
+ else
+ {
+ if (!connection_is_primary_owner (addressed_recipient, rule->destination))
+ return FALSE;
+ }
+ }
+
+ if (rule->flags & BUS_MATCH_PATH)
+ {
+ const char *path;
+
+ _dbus_assert (rule->path != NULL);
+
+ path = dbus_message_get_path (message);
+ if (path == NULL)
+ return FALSE;
+
+ if (strcmp (path, rule->path) != 0)
+ return FALSE;
+ }
+
+ if (rule->flags & BUS_MATCH_ARGS)
+ {
+ int i;
+ DBusMessageIter iter;
+
+ _dbus_assert (rule->args != NULL);
+
+ dbus_message_iter_init (message, &iter);
+
+ i = 0;
+ while (i < rule->args_len)
+ {
+ int current_type;
+ const char *expected_arg;
+ int expected_length;
+ dbus_bool_t is_path;
+
+ expected_arg = rule->args[i];
+ expected_length = rule->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+ is_path = (rule->arg_lens[i] & BUS_MATCH_ARG_IS_PATH) != 0;
+
+ current_type = dbus_message_iter_get_arg_type (&iter);
+
+ if (expected_arg != NULL)
+ {
+ const char *actual_arg;
+ int actual_length;
+
+ if (current_type != DBUS_TYPE_STRING)
+ return FALSE;
+
+ actual_arg = NULL;
+ dbus_message_iter_get_basic (&iter, &actual_arg);
+ _dbus_assert (actual_arg != NULL);
+
+ actual_length = strlen (actual_arg);
+
+ if (is_path)
+ {
+ if (actual_length < expected_length &&
+ actual_arg[actual_length - 1] != '/')
+ return FALSE;
+
+ if (expected_length < actual_length &&
+ expected_arg[expected_length - 1] != '/')
+ return FALSE;
+
+ if (memcmp (actual_arg, expected_arg,
+ MIN (actual_length, expected_length)) != 0)
+ return FALSE;
+ }
+ else
+ {
+ if (expected_length != actual_length ||
+ memcmp (expected_arg, actual_arg, expected_length) != 0)
+ return FALSE;
+ }
+
+ }
+
+ if (current_type != DBUS_TYPE_INVALID)
+ dbus_message_iter_next (&iter);
+
+ ++i;
+ }
+ }
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_matchmaker_get_recipients (BusMatchmaker *matchmaker,
+ BusConnections *connections,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message,
+ DBusList **recipients_p)
+{
+ /* FIXME for now this is a wholly unoptimized linear search */
+ /* Guessing the important optimization is to skip the signal-related
+ * match lists when processing method call and exception messages.
+ * So separate match rule lists for signals?
+ */
+
+ DBusList *link;
+
+ _dbus_assert (*recipients_p == NULL);
+
+ /* This avoids sending same message to the same connection twice.
+ * Purpose of the stamp instead of a bool is to avoid iterating over
+ * all connections resetting the bool each time.
+ */
+ bus_connections_increment_stamp (connections);
+
+ /* addressed_recipient is already receiving the message, don't add to list.
+ * NULL addressed_recipient means either bus driver, or this is a signal
+ * and thus lacks a specific addressed_recipient.
+ */
+ if (addressed_recipient != NULL)
+ bus_connection_mark_stamp (addressed_recipient);
+
+ link = _dbus_list_get_first_link (&matchmaker->all_rules);
+ while (link != NULL)
+ {
+ BusMatchRule *rule;
+
+ rule = link->data;
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ {
+ char *s = match_rule_to_string (rule);
+
+ _dbus_verbose ("Checking whether message matches rule %s for connection %p\n",
+ s, rule->matches_go_to);
+ dbus_free (s);
+ }
+#endif
+
+ if (match_rule_matches (rule,
+ sender, addressed_recipient, message))
+ {
+ _dbus_verbose ("Rule matched\n");
+
+ /* Append to the list if we haven't already */
+ if (bus_connection_mark_stamp (rule->matches_go_to))
+ {
+ if (!_dbus_list_append (recipients_p, rule->matches_go_to))
+ goto nomem;
+ }
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ else
+ {
+ _dbus_verbose ("Connection already receiving this message, so not adding again\n");
+ }
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+ }
+
+ link = _dbus_list_get_next_link (&matchmaker->all_rules, link);
+ }
+
+ return TRUE;
+
+ nomem:
+ _dbus_list_clear (recipients_p);
+ return FALSE;
+}
+
+#ifdef DBUS_BUILD_TESTS
+#include "test.h"
+#include <stdlib.h>
+
+static BusMatchRule*
+check_parse (dbus_bool_t should_succeed,
+ const char *text)
+{
+ BusMatchRule *rule;
+ DBusString str;
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ _dbus_string_init_const (&str, text);
+
+ rule = bus_match_rule_parse (NULL, &str, &error);
+ if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ dbus_error_free (&error);
+ return NULL;
+ }
+
+ if (should_succeed && rule == NULL)
+ {
+ _dbus_warn ("Failed to parse: %s: %s: \"%s\"\n",
+ error.name, error.message,
+ _dbus_string_get_const_data (&str));
+ exit (1);
+ }
+
+ if (!should_succeed && rule != NULL)
+ {
+ _dbus_warn ("Failed to fail to parse: \"%s\"\n",
+ _dbus_string_get_const_data (&str));
+ exit (1);
+ }
+
+ dbus_error_free (&error);
+
+ return rule;
+}
+
+static void
+assert_large_rule (BusMatchRule *rule)
+{
+ _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
+ _dbus_assert (rule->flags & BUS_MATCH_SENDER);
+ _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
+ _dbus_assert (rule->flags & BUS_MATCH_MEMBER);
+ _dbus_assert (rule->flags & BUS_MATCH_DESTINATION);
+ _dbus_assert (rule->flags & BUS_MATCH_PATH);
+
+ _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (rule->interface != NULL);
+ _dbus_assert (rule->member != NULL);
+ _dbus_assert (rule->sender != NULL);
+ _dbus_assert (rule->destination != NULL);
+ _dbus_assert (rule->path != NULL);
+
+ _dbus_assert (strcmp (rule->interface, "org.freedesktop.DBusInterface") == 0);
+ _dbus_assert (strcmp (rule->sender, "org.freedesktop.DBusSender") == 0);
+ _dbus_assert (strcmp (rule->member, "Foo") == 0);
+ _dbus_assert (strcmp (rule->path, "/bar/foo") == 0);
+ _dbus_assert (strcmp (rule->destination, ":452345.34") == 0);
+}
+
+static dbus_bool_t
+test_parsing (void *data)
+{
+ BusMatchRule *rule;
+
+ rule = check_parse (TRUE, "type='signal',sender='org.freedesktop.DBusSender',interface='org.freedesktop.DBusInterface',member='Foo',path='/bar/foo',destination=':452345.34'");
+ if (rule != NULL)
+ {
+ assert_large_rule (rule);
+ bus_match_rule_unref (rule);
+ }
+
+ /* With extra whitespace and useless quotes */
+ rule = check_parse (TRUE, " type='signal', \tsender='org.freedes''ktop.DBusSender', interface='org.freedesktop.DBusInterface''''', \tmember='Foo',path='/bar/foo',destination=':452345.34'''''");
+ if (rule != NULL)
+ {
+ assert_large_rule (rule);
+ bus_match_rule_unref (rule);
+ }
+
+
+ /* A simple signal connection */
+ rule = check_parse (TRUE, "type='signal',path='/foo',interface='org.Bar'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags & BUS_MATCH_MESSAGE_TYPE);
+ _dbus_assert (rule->flags & BUS_MATCH_INTERFACE);
+ _dbus_assert (rule->flags & BUS_MATCH_PATH);
+
+ _dbus_assert (rule->message_type == DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (rule->interface != NULL);
+ _dbus_assert (rule->path != NULL);
+
+ _dbus_assert (strcmp (rule->interface, "org.Bar") == 0);
+ _dbus_assert (strcmp (rule->path, "/foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* argN */
+ rule = check_parse (TRUE, "arg0='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 1);
+ _dbus_assert (rule->args[0] != NULL);
+ _dbus_assert (rule->args[1] == NULL);
+ _dbus_assert (strcmp (rule->args[0], "foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ rule = check_parse (TRUE, "arg1='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 2);
+ _dbus_assert (rule->args[0] == NULL);
+ _dbus_assert (rule->args[1] != NULL);
+ _dbus_assert (rule->args[2] == NULL);
+ _dbus_assert (strcmp (rule->args[1], "foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ rule = check_parse (TRUE, "arg2='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 3);
+ _dbus_assert (rule->args[0] == NULL);
+ _dbus_assert (rule->args[1] == NULL);
+ _dbus_assert (rule->args[2] != NULL);
+ _dbus_assert (rule->args[3] == NULL);
+ _dbus_assert (strcmp (rule->args[2], "foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ rule = check_parse (TRUE, "arg40='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 41);
+ _dbus_assert (rule->args[0] == NULL);
+ _dbus_assert (rule->args[1] == NULL);
+ _dbus_assert (rule->args[40] != NULL);
+ _dbus_assert (rule->args[41] == NULL);
+ _dbus_assert (strcmp (rule->args[40], "foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ rule = check_parse (TRUE, "arg63='foo'");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == BUS_MATCH_ARGS);
+ _dbus_assert (rule->args != NULL);
+ _dbus_assert (rule->args_len == 64);
+ _dbus_assert (rule->args[0] == NULL);
+ _dbus_assert (rule->args[1] == NULL);
+ _dbus_assert (rule->args[63] != NULL);
+ _dbus_assert (rule->args[64] == NULL);
+ _dbus_assert (strcmp (rule->args[63], "foo") == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* Too-large argN */
+ rule = check_parse (FALSE, "arg300='foo'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "arg64='foo'");
+ _dbus_assert (rule == NULL);
+
+ /* No N in argN */
+ rule = check_parse (FALSE, "arg='foo'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "argv='foo'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "arg3junk='foo'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "argument='foo'");
+ _dbus_assert (rule == NULL);
+
+ /* Reject duplicates */
+ rule = check_parse (FALSE, "type='signal',type='method_call'");
+ _dbus_assert (rule == NULL);
+
+ /* Duplicates with the argN code */
+ rule = check_parse (FALSE, "arg0='foo',arg0='bar'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "arg3='foo',arg3='bar'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "arg30='foo',arg30='bar'");
+ _dbus_assert (rule == NULL);
+
+ /* Reject broken keys */
+ rule = check_parse (FALSE, "blah='signal'");
+ _dbus_assert (rule == NULL);
+
+ /* Reject broken values */
+ rule = check_parse (FALSE, "type='chouin'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "interface='abc@def++'");
+ _dbus_assert (rule == NULL);
+ rule = check_parse (FALSE, "service='youpi'");
+ _dbus_assert (rule == NULL);
+
+ /* Allow empty rule */
+ rule = check_parse (TRUE, "");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* All-whitespace rule is the same as empty */
+ rule = check_parse (TRUE, " \t");
+ if (rule != NULL)
+ {
+ _dbus_assert (rule->flags == 0);
+
+ bus_match_rule_unref (rule);
+ }
+
+ /* But with non-whitespace chars and no =value, it's not OK */
+ rule = check_parse (FALSE, "type");
+ _dbus_assert (rule == NULL);
+
+ return TRUE;
+}
+
+static struct {
+ const char *first;
+ const char *second;
+} equality_tests[] = {
+ { "type='signal'", "type='signal'" },
+ { "type='signal',interface='foo.bar'", "interface='foo.bar',type='signal'" },
+ { "type='signal',member='bar'", "member='bar',type='signal'" },
+ { "type='method_call',sender=':1.0'", "sender=':1.0',type='method_call'" },
+ { "type='method_call',destination=':1.0'", "destination=':1.0',type='method_call'" },
+ { "type='method_call',path='/foo/bar'", "path='/foo/bar',type='method_call'" },
+ { "type='method_call',arg0='blah'", "arg0='blah',type='method_call'" },
+ { "type='method_call',arg0='boo'", "arg0='boo',type='method_call'" },
+ { "type='method_call',arg0='blah',arg1='baz'", "arg0='blah',arg1='baz',type='method_call'" },
+ { "type='method_call',arg3='foosh'", "arg3='foosh',type='method_call'" },
+ { "arg3='fool'", "arg3='fool'" },
+ { "member='food'", "member='food'" }
+};
+
+static void
+test_equality (void)
+{
+ int i;
+
+ i = 0;
+ while (i < _DBUS_N_ELEMENTS (equality_tests))
+ {
+ BusMatchRule *first;
+ BusMatchRule *second;
+ int j;
+
+ first = check_parse (TRUE, equality_tests[i].first);
+ _dbus_assert (first != NULL);
+ second = check_parse (TRUE, equality_tests[i].second);
+ _dbus_assert (second != NULL);
+
+ if (!match_rule_equal (first, second))
+ {
+ _dbus_warn ("rule %s and %s should have been equal\n",
+ equality_tests[i].first,
+ equality_tests[i].second);
+ exit (1);
+ }
+
+ bus_match_rule_unref (second);
+
+ /* Check that the rule is not equal to any of the
+ * others besides its pair match
+ */
+ j = 0;
+ while (j < _DBUS_N_ELEMENTS (equality_tests))
+ {
+ if (i != j)
+ {
+ second = check_parse (TRUE, equality_tests[j].second);
+
+ if (match_rule_equal (first, second))
+ {
+ _dbus_warn ("rule %s and %s should not have been equal\n",
+ equality_tests[i].first,
+ equality_tests[j].second);
+ exit (1);
+ }
+
+ bus_match_rule_unref (second);
+ }
+
+ ++j;
+ }
+
+ bus_match_rule_unref (first);
+
+ ++i;
+ }
+}
+
+static const char*
+should_match_message_1[] = {
+ "type='signal'",
+ "member='Frobated'",
+ "arg0='foobar'",
+ "type='signal',member='Frobated'",
+ "type='signal',member='Frobated',arg0='foobar'",
+ "member='Frobated',arg0='foobar'",
+ "type='signal',arg0='foobar'",
+ NULL
+};
+
+static const char*
+should_not_match_message_1[] = {
+ "type='method_call'",
+ "type='error'",
+ "type='method_return'",
+ "type='signal',member='Oopsed'",
+ "arg0='blah'",
+ "arg1='foobar'",
+ "arg2='foobar'",
+ "arg3='foobar'",
+ "arg0='3'",
+ "arg1='3'",
+ "arg0='foobar',arg1='abcdef'",
+ "arg0='foobar',arg1='abcdef',arg2='abcdefghi',arg3='abcdefghi',arg4='abcdefghi'",
+ "arg0='foobar',arg1='abcdef',arg4='abcdefghi',arg3='abcdefghi',arg2='abcdefghi'",
+ NULL
+};
+
+static void
+check_matches (dbus_bool_t expected_to_match,
+ int number,
+ DBusMessage *message,
+ const char *rule_text)
+{
+ BusMatchRule *rule;
+ dbus_bool_t matched;
+
+ rule = check_parse (TRUE, rule_text);
+ _dbus_assert (rule != NULL);
+
+ /* We can't test sender/destination rules since we pass NULL here */
+ matched = match_rule_matches (rule, NULL, NULL, message);
+
+ if (matched != expected_to_match)
+ {
+ _dbus_warn ("Expected rule %s to %s message %d, failed\n",
+ rule_text, expected_to_match ?
+ "match" : "not match", number);
+ exit (1);
+ }
+
+ bus_match_rule_unref (rule);
+}
+
+static void
+check_matching (DBusMessage *message,
+ int number,
+ const char **should_match,
+ const char **should_not_match)
+{
+ int i;
+
+ i = 0;
+ while (should_match[i] != NULL)
+ {
+ check_matches (TRUE, number, message, should_match[i]);
+ ++i;
+ }
+
+ i = 0;
+ while (should_not_match[i] != NULL)
+ {
+ check_matches (FALSE, number, message, should_not_match[i]);
+ ++i;
+ }
+}
+
+static void
+test_matching (void)
+{
+ DBusMessage *message1;
+ const char *v_STRING;
+ dbus_int32_t v_INT32;
+
+ message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
+ _dbus_assert (message1 != NULL);
+ if (!dbus_message_set_member (message1, "Frobated"))
+ _dbus_assert_not_reached ("oom");
+
+ v_STRING = "foobar";
+ v_INT32 = 3;
+ if (!dbus_message_append_args (message1,
+ DBUS_TYPE_STRING, &v_STRING,
+ DBUS_TYPE_INT32, &v_INT32,
+ NULL))
+ _dbus_assert_not_reached ("oom");
+
+ check_matching (message1, 1,
+ should_match_message_1,
+ should_not_match_message_1);
+
+ dbus_message_unref (message1);
+}
+
+dbus_bool_t
+bus_signals_test (const DBusString *test_data_dir)
+{
+ BusMatchmaker *matchmaker;
+
+ matchmaker = bus_matchmaker_new ();
+ bus_matchmaker_ref (matchmaker);
+ bus_matchmaker_unref (matchmaker);
+ bus_matchmaker_unref (matchmaker);
+
+ if (!_dbus_test_oom_handling ("parsing match rules", test_parsing, NULL))
+ _dbus_assert_not_reached ("Parsing match rules test failed");
+
+ test_equality ();
+
+ test_matching ();
+
+ return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
+
diff --git a/bus/signals.h b/bus/signals.h
new file mode 100644
index 00000000..4ea10755
--- /dev/null
+++ b/bus/signals.h
@@ -0,0 +1,88 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* signals.h Bus signal connection implementation
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_SIGNALS_H
+#define BUS_SIGNALS_H
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+#include "connection.h"
+
+typedef enum
+{
+ BUS_MATCH_MESSAGE_TYPE = 1 << 0,
+ BUS_MATCH_INTERFACE = 1 << 1,
+ BUS_MATCH_MEMBER = 1 << 2,
+ BUS_MATCH_SENDER = 1 << 3,
+ BUS_MATCH_DESTINATION = 1 << 4,
+ BUS_MATCH_PATH = 1 << 5,
+ BUS_MATCH_ARGS = 1 << 6
+} BusMatchFlags;
+
+BusMatchRule* bus_match_rule_new (DBusConnection *matches_go_to);
+BusMatchRule* bus_match_rule_ref (BusMatchRule *rule);
+void bus_match_rule_unref (BusMatchRule *rule);
+
+dbus_bool_t bus_match_rule_set_message_type (BusMatchRule *rule,
+ int type);
+dbus_bool_t bus_match_rule_set_interface (BusMatchRule *rule,
+ const char *interface);
+dbus_bool_t bus_match_rule_set_member (BusMatchRule *rule,
+ const char *member);
+dbus_bool_t bus_match_rule_set_sender (BusMatchRule *rule,
+ const char *sender);
+dbus_bool_t bus_match_rule_set_destination (BusMatchRule *rule,
+ const char *destination);
+dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule,
+ const char *path);
+dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule,
+ int arg,
+ const DBusString *value,
+ dbus_bool_t is_path);
+
+BusMatchRule* bus_match_rule_parse (DBusConnection *matches_go_to,
+ const DBusString *rule_text,
+ DBusError *error);
+
+BusMatchmaker* bus_matchmaker_new (void);
+BusMatchmaker* bus_matchmaker_ref (BusMatchmaker *matchmaker);
+void bus_matchmaker_unref (BusMatchmaker *matchmaker);
+
+dbus_bool_t bus_matchmaker_add_rule (BusMatchmaker *matchmaker,
+ BusMatchRule *rule);
+dbus_bool_t bus_matchmaker_remove_rule_by_value (BusMatchmaker *matchmaker,
+ BusMatchRule *value,
+ DBusError *error);
+void bus_matchmaker_remove_rule (BusMatchmaker *matchmaker,
+ BusMatchRule *rule);
+void bus_matchmaker_disconnected (BusMatchmaker *matchmaker,
+ DBusConnection *disconnected);
+dbus_bool_t bus_matchmaker_get_recipients (BusMatchmaker *matchmaker,
+ BusConnections *connections,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusMessage *message,
+ DBusList **recipients_p);
+
+#endif /* BUS_SIGNALS_H */
diff --git a/bus/system.conf.in b/bus/system.conf.in
new file mode 100644
index 00000000..92f4cc42
--- /dev/null
+++ b/bus/system.conf.in
@@ -0,0 +1,83 @@
+<!-- This configuration file controls the systemwide message bus.
+ Add a system-local.conf and edit that rather than changing this
+ file directly. -->
+
+<!-- Note that there are any number of ways you can hose yourself
+ security-wise by screwing up this file; in particular, you
+ probably don't want to listen on any more addresses, add any more
+ auth mechanisms, run as a different user, etc. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <!-- Our well-known bus type, do not change this -->
+ <type>system</type>
+
+ <!-- Run as special user -->
+ <user>@DBUS_USER@</user>
+
+ <!-- Fork into daemon mode -->
+ <fork/>
+
+ <!-- We use system service launching using a helper -->
+ <standard_system_servicedirs/>
+
+ <!-- This is a setuid helper that is used to launch system services -->
+ <servicehelper>@DBUS_LIBEXECDIR@/dbus-daemon-launch-helper</servicehelper>
+
+ <!-- Write a pid file -->
+ <pidfile>@DBUS_SYSTEM_PID_FILE@</pidfile>
+
+ <!-- Enable logging to syslog -->
+ <syslog/>
+
+ <!-- Only allow socket-credentials-based authentication -->
+ <auth>EXTERNAL</auth>
+
+ <!-- Only listen on a local socket. (abstract=/path/to/socket
+ means use abstract namespace, don't really create filesystem
+ file; only Linux supports this. Use path=/whatever on other
+ systems.) -->
+ <listen>@DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@</listen>
+
+ <policy context="default">
+ <!-- All users can connect to system bus -->
+ <allow user="*"/>
+
+ <!-- Holes must be punched in service configuration files for
+ name ownership and sending method calls -->
+ <deny own="*"/>
+ <deny send_type="method_call"/>
+
+ <!-- Signals and reply messages (method returns, errors) are allowed
+ by default -->
+ <allow send_type="signal"/>
+ <allow send_requested_reply="true" send_type="method_return"/>
+ <allow send_requested_reply="true" send_type="error"/>
+
+ <!-- All messages may be received by default -->
+ <allow receive_type="method_call"/>
+ <allow receive_type="method_return"/>
+ <allow receive_type="error"/>
+ <allow receive_type="signal"/>
+
+ <!-- Allow anyone to talk to the message bus -->
+ <allow send_destination="org.freedesktop.DBus"/>
+ <!-- But disallow some specific bus services -->
+ <deny send_destination="org.freedesktop.DBus"
+ send_interface="org.freedesktop.DBus"
+ send_member="UpdateActivationEnvironment"/>
+ </policy>
+
+ <!-- Config files are placed here that among other things, punch
+ holes in the above policy for specific services. -->
+ <includedir>system.d</includedir>
+
+ <!-- This is included last so local configuration can override what's
+ in this standard file -->
+ <include ignore_missing="yes">system-local.conf</include>
+
+ <include if_selinux_enabled="yes" selinux_root_relative="yes">contexts/dbus_contexts</include>
+
+</busconfig>
diff --git a/bus/test-launch-helper.c b/bus/test-launch-helper.c
new file mode 100644
index 00000000..d78ca519
--- /dev/null
+++ b/bus/test-launch-helper.c
@@ -0,0 +1,146 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* test-main.c main() for the OOM check of the launch helper
+ *
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * 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 "test.h"
+#include "activation-helper.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dbus/dbus-internals.h>
+
+#ifdef DBUS_BUILD_TESTS
+static void
+die (const char *failure)
+{
+ fprintf (stderr, "Unit test failed: %s\n", failure);
+ exit (1);
+}
+
+static void
+check_memleaks (const char *name)
+{
+ dbus_shutdown ();
+
+ printf ("%s: checking for memleaks\n", name);
+ if (_dbus_get_malloc_blocks_outstanding () != 0)
+ {
+ _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+ _dbus_get_malloc_blocks_outstanding ());
+ die ("memleaks");
+ }
+}
+
+static void
+test_post_hook (const char *name)
+{
+ check_memleaks (name);
+}
+#endif /* DBUS_BUILD_TESTS */
+
+
+#ifdef ACTIVATION_LAUNCHER_DO_OOM
+
+/* returns true if good things happen, or if we get OOM */
+static dbus_bool_t
+bus_activation_helper_oom_test (void *data)
+{
+ const char *service;
+ DBusError error;
+ dbus_bool_t retval;
+
+ service = (const char *) data;
+ retval = TRUE;
+
+ dbus_error_init (&error);
+ if (!run_launch_helper (service, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+ /* we failed, but a OOM is good */
+ if (!dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+ {
+ _dbus_warn ("FAILED SELF TEST: Error: %s\n", error.message);
+ retval = FALSE;
+ }
+ dbus_error_free (&error);
+ }
+ else
+ {
+ /* we succeeded, yay! */
+ _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
+ }
+ return retval;
+}
+
+#endif
+
+int
+main (int argc, char **argv)
+{
+#ifdef DBUS_BUILD_TESTS
+ const char *dir;
+ DBusString config_file;
+
+ if (argc > 1)
+ dir = argv[1];
+ else
+ dir = _dbus_getenv ("DBUS_TEST_DATA");
+
+ if (dir == NULL)
+ {
+ fprintf (stderr, "Must specify test data directory as argv[1] or in DBUS_TEST_DATA env variable\n");
+ return 1;
+ }
+
+ printf ("%s: Running launch helper OOM checks\n", argv[0]);
+
+ if (!_dbus_string_init (&config_file))
+ return 1;
+ if (!_dbus_string_append (&config_file, dir))
+ return 1;
+ if (!_dbus_string_append (&config_file, "/valid-config-files-system/debug-allow-all-pass.conf"))
+ return 1;
+
+ /* use a config file that will actually work... */
+ _dbus_setenv ("TEST_LAUNCH_HELPER_CONFIG",
+ _dbus_string_get_const_data (&config_file));
+
+ _dbus_string_free (&config_file);
+
+ if (!_dbus_test_oom_handling ("dbus-daemon-launch-helper",
+ bus_activation_helper_oom_test,
+ "org.freedesktop.DBus.TestSuiteEchoService"))
+ die ("OOM failed");
+
+ test_post_hook (argv[0]);
+
+ printf ("%s: Success\n", argv[0]);
+
+ return 0;
+#else /* DBUS_BUILD_TESTS */
+
+ printf ("Not compiled with test support\n");
+
+ return 0;
+#endif
+}
+
diff --git a/bus/test-main.c b/bus/test-main.c
new file mode 100644
index 00000000..f19d0d55
--- /dev/null
+++ b/bus/test-main.c
@@ -0,0 +1,151 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* test-main.c main() for make check
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "test.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-internals.h>
+#include "selinux.h"
+
+#ifdef DBUS_BUILD_TESTS
+static void
+die (const char *failure)
+{
+ fprintf (stderr, "Unit test failed: %s\n", failure);
+ exit (1);
+}
+
+static void
+check_memleaks (const char *name)
+{
+ dbus_shutdown ();
+
+ printf ("%s: checking for memleaks\n", name);
+ if (_dbus_get_malloc_blocks_outstanding () != 0)
+ {
+ _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+ _dbus_get_malloc_blocks_outstanding ());
+ die ("memleaks");
+ }
+}
+#endif /* DBUS_BUILD_TESTS */
+
+static void
+test_pre_hook (void)
+{
+
+ if (_dbus_getenv ("DBUS_TEST_SELINUX")
+ && (!bus_selinux_pre_init ()
+ || !bus_selinux_full_init ()))
+ die ("could not init selinux support");
+}
+
+static char *progname = "";
+static void
+test_post_hook (void)
+{
+ if (_dbus_getenv ("DBUS_TEST_SELINUX"))
+ bus_selinux_shutdown ();
+ check_memleaks (progname);
+}
+
+int
+main (int argc, char **argv)
+{
+#ifdef DBUS_BUILD_TESTS
+ const char *dir;
+ DBusString test_data_dir;
+
+ progname = argv[0];
+
+ if (argc > 1)
+ dir = argv[1];
+ else
+ dir = _dbus_getenv ("DBUS_TEST_DATA");
+
+ if (dir == NULL)
+ {
+ fprintf (stderr, "Must specify test data directory as argv[1] or in DBUS_TEST_DATA env variable\n");
+ return 1;
+ }
+
+ _dbus_string_init_const (&test_data_dir, dir);
+
+ if (!_dbus_threads_init_debug ())
+ die ("initializing debug threads");
+
+ test_pre_hook ();
+ printf ("%s: Running expire list test\n", argv[0]);
+ if (!bus_expire_list_test (&test_data_dir))
+ die ("expire list");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running config file parser test\n", argv[0]);
+ if (!bus_config_parser_test (&test_data_dir))
+ die ("parser");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running policy test\n", argv[0]);
+ if (!bus_policy_test (&test_data_dir))
+ die ("policy");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running signals test\n", argv[0]);
+ if (!bus_signals_test (&test_data_dir))
+ die ("signals");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running SHA1 connection test\n", argv[0]);
+ if (!bus_dispatch_sha1_test (&test_data_dir))
+ die ("sha1");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running message dispatch test\n", argv[0]);
+ if (!bus_dispatch_test (&test_data_dir))
+ die ("dispatch");
+ test_post_hook ();
+
+ test_pre_hook ();
+ printf ("%s: Running service files reloading test\n", argv[0]);
+ if (!bus_activation_service_reload_test (&test_data_dir))
+ die ("service reload");
+ test_post_hook ();
+
+ printf ("%s: Success\n", argv[0]);
+
+
+ return 0;
+#else /* DBUS_BUILD_TESTS */
+
+ printf ("Not compiled with test support\n");
+
+ return 0;
+#endif
+}
diff --git a/bus/test-system.c b/bus/test-system.c
new file mode 100644
index 00000000..6224ab5e
--- /dev/null
+++ b/bus/test-system.c
@@ -0,0 +1,106 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* test-main.c main() for make check
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "test.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-internals.h>
+
+#ifdef DBUS_BUILD_TESTS
+static void
+die (const char *failure)
+{
+ fprintf (stderr, "Unit test failed: %s\n", failure);
+ exit (1);
+}
+
+static void
+check_memleaks (const char *name)
+{
+ dbus_shutdown ();
+
+ printf ("%s: checking for memleaks\n", name);
+ if (_dbus_get_malloc_blocks_outstanding () != 0)
+ {
+ _dbus_warn ("%d dbus_malloc blocks were not freed\n",
+ _dbus_get_malloc_blocks_outstanding ());
+ die ("memleaks");
+ }
+}
+#endif /* DBUS_BUILD_TESTS */
+
+static void
+test_pre_hook (void)
+{
+}
+
+static char *progname = "";
+static void
+test_post_hook (void)
+{
+ check_memleaks (progname);
+}
+
+int
+main (int argc, char **argv)
+{
+#ifdef DBUS_BUILD_TESTS
+ const char *dir;
+ DBusString test_data_dir;
+
+ progname = argv[0];
+
+ if (argc > 1)
+ dir = argv[1];
+ else
+ dir = _dbus_getenv ("DBUS_TEST_DATA");
+
+ if (dir == NULL)
+ {
+ fprintf (stderr, "Must specify test data directory as argv[1] or in DBUS_TEST_DATA env variable\n");
+ return 1;
+ }
+
+ _dbus_string_init_const (&test_data_dir, dir);
+
+ if (!_dbus_threads_init_debug ())
+ die ("initializing debug threads");
+
+ test_pre_hook ();
+ printf ("%s: Running config file parser (trivial) test\n", argv[0]);
+ if (!bus_config_parser_trivial_test (&test_data_dir))
+ die ("parser");
+ test_post_hook ();
+
+ printf ("%s: Success\n", argv[0]);
+
+ return 0;
+#else /* DBUS_BUILD_TESTS */
+
+ printf ("Not compiled with test support\n");
+
+ return 0;
+#endif
+}
diff --git a/bus/test.c b/bus/test.c
new file mode 100644
index 00000000..8dfe098c
--- /dev/null
+++ b/bus/test.c
@@ -0,0 +1,346 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* test.c unit test routines
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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>
+
+#ifdef DBUS_BUILD_TESTS
+#include "test.h"
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-sysdeps.h>
+
+/* The "debug client" watch/timeout handlers don't dispatch messages,
+ * as we manually pull them in order to verify them. This is why they
+ * are different from the real handlers in connection.c
+ */
+static DBusList *clients = NULL;
+static DBusLoop *client_loop = NULL;
+
+static dbus_bool_t
+client_watch_callback (DBusWatch *watch,
+ unsigned int condition,
+ void *data)
+{
+ /* FIXME this can be done in dbus-mainloop.c
+ * if the code in activation.c for the babysitter
+ * watch handler is fixed.
+ */
+
+ return dbus_watch_handle (watch, condition);
+}
+
+static dbus_bool_t
+add_client_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ return _dbus_loop_add_watch (client_loop,
+ watch, client_watch_callback, connection,
+ NULL);
+}
+
+static void
+remove_client_watch (DBusWatch *watch,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ _dbus_loop_remove_watch (client_loop,
+ watch, client_watch_callback, connection);
+}
+
+static void
+client_timeout_callback (DBusTimeout *timeout,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ dbus_connection_ref (connection);
+
+ /* can return FALSE on OOM but we just let it fire again later */
+ dbus_timeout_handle (timeout);
+
+ dbus_connection_unref (connection);
+}
+
+static dbus_bool_t
+add_client_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ return _dbus_loop_add_timeout (client_loop, timeout, client_timeout_callback, connection, NULL);
+}
+
+static void
+remove_client_timeout (DBusTimeout *timeout,
+ void *data)
+{
+ DBusConnection *connection = data;
+
+ _dbus_loop_remove_timeout (client_loop, timeout, client_timeout_callback, connection);
+}
+
+static DBusHandlerResult
+client_disconnect_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ if (!dbus_message_is_signal (message,
+ DBUS_INTERFACE_LOCAL,
+ "Disconnected"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ _dbus_verbose ("Removing client %p in disconnect handler\n",
+ connection);
+
+ _dbus_list_remove (&clients, connection);
+
+ dbus_connection_unref (connection);
+
+ if (clients == NULL)
+ {
+ _dbus_loop_unref (client_loop);
+ client_loop = NULL;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+dbus_bool_t
+bus_setup_debug_client (DBusConnection *connection)
+{
+ dbus_bool_t retval;
+
+ if (!dbus_connection_add_filter (connection,
+ client_disconnect_filter,
+ NULL, NULL))
+ return FALSE;
+
+ retval = FALSE;
+
+ if (client_loop == NULL)
+ {
+ client_loop = _dbus_loop_new ();
+ if (client_loop == NULL)
+ goto out;
+ }
+
+ if (!dbus_connection_set_watch_functions (connection,
+ add_client_watch,
+ remove_client_watch,
+ NULL,
+ connection,
+ NULL))
+ goto out;
+
+ if (!dbus_connection_set_timeout_functions (connection,
+ add_client_timeout,
+ remove_client_timeout,
+ NULL,
+ connection, NULL))
+ goto out;
+
+ if (!_dbus_list_append (&clients, connection))
+ goto out;
+
+ retval = TRUE;
+
+ out:
+ if (!retval)
+ {
+ dbus_connection_remove_filter (connection,
+ client_disconnect_filter,
+ NULL);
+
+ dbus_connection_set_watch_functions (connection,
+ NULL, NULL, NULL, NULL, NULL);
+ dbus_connection_set_timeout_functions (connection,
+ NULL, NULL, NULL, NULL, NULL);
+
+ _dbus_list_remove_last (&clients, connection);
+
+ if (clients == NULL)
+ {
+ _dbus_loop_unref (client_loop);
+ client_loop = NULL;
+ }
+ }
+
+ return retval;
+}
+
+void
+bus_test_clients_foreach (BusConnectionForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&clients);
+ while (link != NULL)
+ {
+ DBusConnection *connection = link->data;
+ DBusList *next = _dbus_list_get_next_link (&clients, link);
+
+ if (!(* function) (connection, data))
+ break;
+
+ link = next;
+ }
+}
+
+dbus_bool_t
+bus_test_client_listed (DBusConnection *connection)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&clients);
+ while (link != NULL)
+ {
+ DBusConnection *c = link->data;
+ DBusList *next = _dbus_list_get_next_link (&clients, link);
+
+ if (c == connection)
+ return TRUE;
+
+ link = next;
+ }
+
+ return FALSE;
+}
+
+void
+bus_test_run_clients_loop (dbus_bool_t block_once)
+{
+ if (client_loop == NULL)
+ return;
+
+ _dbus_verbose ("---> Dispatching on \"client side\"\n");
+
+ /* dispatch before we block so pending dispatches
+ * won't make our block return early
+ */
+ _dbus_loop_dispatch (client_loop);
+
+ /* Do one blocking wait, since we're expecting data */
+ if (block_once)
+ {
+ _dbus_verbose ("---> blocking on \"client side\"\n");
+ _dbus_loop_iterate (client_loop, TRUE);
+ }
+
+ /* Then mop everything up */
+ while (_dbus_loop_iterate (client_loop, FALSE))
+ ;
+
+ _dbus_verbose ("---> Done dispatching on \"client side\"\n");
+}
+
+void
+bus_test_run_bus_loop (BusContext *context,
+ dbus_bool_t block_once)
+{
+ _dbus_verbose ("---> Dispatching on \"server side\"\n");
+
+ /* dispatch before we block so pending dispatches
+ * won't make our block return early
+ */
+ _dbus_loop_dispatch (bus_context_get_loop (context));
+
+ /* Do one blocking wait, since we're expecting data */
+ if (block_once)
+ {
+ _dbus_verbose ("---> blocking on \"server side\"\n");
+ _dbus_loop_iterate (bus_context_get_loop (context), TRUE);
+ }
+
+ /* Then mop everything up */
+ while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE))
+ ;
+
+ _dbus_verbose ("---> Done dispatching on \"server side\"\n");
+}
+
+void
+bus_test_run_everything (BusContext *context)
+{
+ while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE) ||
+ (client_loop == NULL || _dbus_loop_iterate (client_loop, FALSE)))
+ ;
+}
+
+BusContext*
+bus_context_new_test (const DBusString *test_data_dir,
+ const char *filename)
+{
+ DBusError error;
+ DBusString config_file;
+ DBusString relative;
+ BusContext *context;
+
+ if (!_dbus_string_init (&config_file))
+ {
+ _dbus_warn ("No memory\n");
+ return NULL;
+ }
+
+ if (!_dbus_string_copy (test_data_dir, 0,
+ &config_file, 0))
+ {
+ _dbus_warn ("No memory\n");
+ _dbus_string_free (&config_file);
+ return NULL;
+ }
+
+ _dbus_string_init_const (&relative, filename);
+
+ if (!_dbus_concat_dir_and_file (&config_file, &relative))
+ {
+ _dbus_warn ("No memory\n");
+ _dbus_string_free (&config_file);
+ return NULL;
+ }
+
+ dbus_error_init (&error);
+ context = bus_context_new (&config_file, FALSE, NULL, NULL, &error);
+ if (context == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
+ filename, error.message);
+
+ dbus_error_free (&error);
+
+ _dbus_string_free (&config_file);
+
+ return NULL;
+ }
+
+ _dbus_string_free (&config_file);
+
+ return context;
+}
+
+#endif
diff --git a/bus/test.h b/bus/test.h
new file mode 100644
index 00000000..72d68b09
--- /dev/null
+++ b/bus/test.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* test.h unit test routines
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_TEST_H
+#define BUS_TEST_H
+
+#include <config.h>
+
+#ifdef DBUS_BUILD_TESTS
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
+#include "connection.h"
+
+dbus_bool_t bus_dispatch_test (const DBusString *test_data_dir);
+dbus_bool_t bus_dispatch_sha1_test (const DBusString *test_data_dir);
+dbus_bool_t bus_policy_test (const DBusString *test_data_dir);
+dbus_bool_t bus_config_parser_test (const DBusString *test_data_dir);
+dbus_bool_t bus_config_parser_trivial_test (const DBusString *test_data_dir);
+dbus_bool_t bus_signals_test (const DBusString *test_data_dir);
+dbus_bool_t bus_expire_list_test (const DBusString *test_data_dir);
+dbus_bool_t bus_activation_service_reload_test (const DBusString *test_data_dir);
+dbus_bool_t bus_setup_debug_client (DBusConnection *connection);
+void bus_test_clients_foreach (BusConnectionForeachFunction function,
+ void *data);
+dbus_bool_t bus_test_client_listed (DBusConnection *connection);
+void bus_test_run_bus_loop (BusContext *context,
+ dbus_bool_t block);
+void bus_test_run_clients_loop (dbus_bool_t block);
+void bus_test_run_everything (BusContext *context);
+BusContext* bus_context_new_test (const DBusString *test_data_dir,
+ const char *filename);
+
+
+
+#endif
+
+#endif /* BUS_TEST_H */
diff --git a/bus/utils.c b/bus/utils.c
new file mode 100644
index 00000000..7d248727
--- /dev/null
+++ b/bus/utils.c
@@ -0,0 +1,48 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* utils.c General utility functions
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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 "utils.h"
+#include <dbus/dbus-sysdeps.h>
+#include <dbus/dbus-mainloop.h>
+
+const char bus_no_memory_message[] = "Memory allocation failure in message bus";
+
+void
+bus_connection_dispatch_all_messages (DBusConnection *connection)
+{
+ while (bus_connection_dispatch_one_message (connection))
+ ;
+}
+
+dbus_bool_t
+bus_connection_dispatch_one_message (DBusConnection *connection)
+{
+ DBusDispatchStatus status;
+
+ while ((status = dbus_connection_dispatch (connection)) == DBUS_DISPATCH_NEED_MEMORY)
+ _dbus_wait_for_memory ();
+
+ return status == DBUS_DISPATCH_DATA_REMAINS;
+}
diff --git a/bus/utils.h b/bus/utils.h
new file mode 100644
index 00000000..f533895f
--- /dev/null
+++ b/bus/utils.h
@@ -0,0 +1,36 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* utils.h General utility functions
+ *
+ * Copyright (C) 2003 CodeFactory AB
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * 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
+ *
+ */
+
+#ifndef BUS_UTILS_H
+#define BUS_UTILS_H
+
+#include <dbus/dbus.h>
+
+extern const char bus_no_memory_message[];
+#define BUS_SET_OOM(error) dbus_set_error_const ((error), DBUS_ERROR_NO_MEMORY, bus_no_memory_message)
+
+void bus_connection_dispatch_all_messages (DBusConnection *connection);
+dbus_bool_t bus_connection_dispatch_one_message (DBusConnection *connection);
+
+#endif /* BUS_UTILS_H */